mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
PCI changes for the v4.9 merge window:
Enumeration microblaze: Add multidomain support for procfs (Bharat Kumar Gogada) Resource management Ignore requested alignment for PROBE_ONLY and fixed resources (Yongji Xie) Ignore requested alignment for VF BARs (Yongji Xie) PCI device hotplug Make core explicitly non-modular (Paul Gortmaker) PCIe native device hotplug Rename pcie_isr() locals for clarity (Bjorn Helgaas) Return IRQ_NONE when we can't read interrupt status (Bjorn Helgaas) Remove unnecessary guard (Bjorn Helgaas) Clean up dmesg "Slot(%s)" messages (Bjorn Helgaas) Remove useless pciehp_get_latch_status() calls (Bjorn Helgaas) Clear attention LED on device add (Keith Busch) Allow exclusive userspace control of indicators (Keith Busch) Process all hotplug events before looking for new ones (Mayurkumar Patel) Don't re-read Slot Status when queuing hotplug event (Mayurkumar Patel) Don't re-read Slot Status when handling surprise event (Mayurkumar Patel) Make explicitly non-modular (Paul Gortmaker) Power management Afford direct-complete to devices with non-standard PM (Lukas Wunner) Query platform firmware for device power state (Lukas Wunner) Recognize D3cold in pci_update_current_state() (Lukas Wunner) Avoid unnecessary resume after direct-complete (Lukas Wunner) Make explicitly non-modular (Paul Gortmaker) Virtualization Mark Atheros AR9580 to avoid bus reset (Maik Broemme) Check for pci_setup_device() failure in pci_iov_add_virtfn() (Po Liu) MSI Enable PCI_MSI_IRQ_DOMAIN support for ARC (Joao Pinto) AER Remove aerdriver.nosourceid kernel parameter (Bjorn Helgaas) Remove aerdriver.forceload kernel parameter (Bjorn Helgaas) Fix aer_probe() kernel-doc comment (Cao jin) Add bus flag to skip source ID matching (Jon Derrick) Avoid memory allocation in interrupt handling path (Jon Derrick) Cache capability position (Keith Busch) Make explicitly non-modular (Paul Gortmaker) Remove duplicate AER severity translation (Tyler Baicar) Send correct severity to calculate AER severity (Tyler Baicar) Precision Time Measurement Add Precision Time Measurement (PTM) support (Jonathan Yong) Add PTM clock granularity information (Bjorn Helgaas) Add pci_enable_ptm() for drivers to enable PTM on endpoints (Bjorn Helgaas) Generic host bridge driver Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) Make explicitly non-modular (Paul Gortmaker) Altera host bridge driver Remove redundant platform_get_resource() return value check (Bjorn Helgaas) Poll for link training status after retraining the link (Ley Foon Tan) Rework config accessors for use without a struct pci_bus (Ley Foon Tan) Move retrain from fixup to altera_pcie_host_init() (Ley Foon Tan) Make MSI explicitly non-modular (Paul Gortmaker) Make explicitly non-modular (Paul Gortmaker) Relax device number checking to allow SR-IOV (Po Liu) ARM Versatile host bridge driver Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) Axis ARTPEC-6 host bridge driver Drop __init from artpec6_add_pcie_port() (Niklas Cassel) Freescale i.MX6 host bridge driver Make explicitly non-modular (Paul Gortmaker) Intel VMD host bridge driver Add quirk for AER to ignore source ID (Jon Derrick) Allocate IRQ lists with correct MSI-X count (Jon Derrick) Convert to use pci_alloc_irq_vectors() API (Jon Derrick) Eliminate vmd_vector member from list type (Jon Derrick) Eliminate index member from IRQ list (Jon Derrick) Synchronize with RCU freeing MSI IRQ descs (Keith Busch) Request userspace control of PCIe hotplug indicators (Keith Busch) Move VMD driver to drivers/pci/host (Keith Busch) Marvell Aardvark host bridge driver Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) Remove redundant dev_err call in advk_pcie_probe() (Wei Yongjun) Microsoft Hyper-V host bridge driver Use zero-length array in struct pci_packet (Dexuan Cui) Use pci_function_description[0] in struct definitions (Dexuan Cui) Remove the unused 'wrk' in struct hv_pcibus_device (Dexuan Cui) Handle vmbus_sendpacket() failure in hv_compose_msi_msg() (Dexuan Cui) Handle hv_pci_generic_compl() error case (Dexuan Cui) Use list_move_tail() instead of list_del() + list_add_tail() (Wei Yongjun) NVIDIA Tegra host bridge driver Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) Remove redundant _data suffix (Thierry Reding) Use of_device_get_match_data() (Thierry Reding) Qualcomm host bridge driver Make explicitly non-modular (Paul Gortmaker) Renesas R-Car host bridge driver Consolidate register space lookup and ioremap (Bjorn Helgaas) Don't disable/unprepare clocks on prepare/enable failure (Geert Uytterhoeven) Add multi-MSI support (Grigory Kletsko) Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) Fix some checkpatch warnings (Sergei Shtylyov) Try increasing PCIe link speed to 5 GT/s at boot (Sergei Shtylyov) Rockchip host bridge driver Add DT bindings for Rockchip PCIe controller (Shawn Lin) Add Rockchip PCIe controller support (Shawn Lin) Improve the deassert sequence of four reset pins (Shawn Lin) Fix wrong transmitted FTS count (Shawn Lin) Increase the Max Credit update interval (Rajat Jain) Samsung Exynos host bridge driver Make explicitly non-modular (Paul Gortmaker) ST Microelectronics SPEAr13xx host bridge driver Make explicitly non-modular (Paul Gortmaker) Synopsys DesignWare host bridge driver Return data directly from dw_pcie_readl_rc() (Bjorn Helgaas) Exchange viewport of `MEMORYs' and `CFGs/IOs' (Dong Bo) Check LTSSM training bit before deciding link is up (Jisheng Zhang) Move link wait definitions to .c file (Joao Pinto) Wait for iATU enable (Joao Pinto) Add iATU Unroll feature (Joao Pinto) Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) Make explicitly non-modular (Paul Gortmaker) Relax device number checking to allow SR-IOV (Po Liu) Keep viewport fixed for IO transaction if num_viewport > 2 (Pratyush Anand) Remove redundant platform_get_resource() return value check (Wei Yongjun) TI DRA7xx host bridge driver Make explicitly non-modular (Paul Gortmaker) TI Keystone host bridge driver Propagate request_irq() failure (Wei Yongjun) Xilinx AXI host bridge driver Keep both legacy and MSI interrupt domain references (Bharat Kumar Gogada) Clear interrupt register for invalid interrupt (Bharat Kumar Gogada) Clear correct MSI set bit (Bharat Kumar Gogada) Dispose of MSI virtual IRQ (Bharat Kumar Gogada) Make explicitly non-modular (Paul Gortmaker) Relax device number checking to allow SR-IOV (Po Liu) Xilinx NWL host bridge driver Expand error logging (Bharat Kumar Gogada) Enable all MSI interrupts using MSI mask (Bharat Kumar Gogada) Make explicitly non-modular (Paul Gortmaker) Miscellaneous Drop CONFIG_KEXEC_CORE ifdeffery (Lukas Wunner) portdrv: Make explicitly non-modular (Paul Gortmaker) Make DPC explicitly non-modular (Paul Gortmaker) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJX9pOwAAoJEFmIoMA60/r8NfMQAMEMYK3yv/RQtcQvtB26L2eJ Zn6eJXZPFDsaP+uRYrZEEphHccGFJgmtnOEYjo5XH+wUovmj8BxIy58APEGij/69 iJyaXkI2jRVCOFna6WLJKS0jM6/Lw40RdwrANqHfvzR9N8pEjDD36BglshFNK/pK 07dYNtI+/sq1j5K1UE6bK4uiAURiPc2SfK4/+M4Mg4SfZMQgLIvpT8VHdercYSXN eGA0SHGNm8qIK2JArwa293z6zB0Kw05juLayZHai6PwDqqNMvzgzS4pChGJl4klH fFe3bhlBVR2SGh9nuHMgIMWKGKGkjWBjwS1p0ENDgdpBVPXmDIc7Ka8Q0BqAFz+F YOmj4BtR8U3mOj942bxYZYqqqt02Brlu+/YkY6+OtNjKWi7w0s24dd5vEfBl2nVV T2klW4P9MuCnsutfrxkFL8voJRCIdV3fohwLHfHVfNTZN+bSZnlo9MiywbgQlexV 5BKIA5z9E87bhvo7Mr8VCZjjEV0E3RrT4sgaJyQhGUGIpuZDiZFHSujBo53jRSdy jvIb22cQtmQJAkafID6djcMbXh+LIhXqUfH1ZVqpJFFMJN43zB7dwaMwrLsZgqTi ZV7VxZJVN7U6JCQvtNvwLBeKT82T7jxVZoSIogc/UR0gelb6jPN/yiTX5TUzMgPs HW7F3hv0AdmYKPcGZJAP =0Aq4 -----END PGP SIGNATURE----- Merge tag 'pci-v4.9-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci Pull PCI updates from Bjorn Helgaas: "Summary of PCI changes for the v4.9 merge window: Enumeration: - microblaze: Add multidomain support for procfs (Bharat Kumar Gogada) Resource management: - Ignore requested alignment for PROBE_ONLY and fixed resources (Yongji Xie) - Ignore requested alignment for VF BARs (Yongji Xie) PCI device hotplug: - Make core explicitly non-modular (Paul Gortmaker) PCIe native device hotplug: - Rename pcie_isr() locals for clarity (Bjorn Helgaas) - Return IRQ_NONE when we can't read interrupt status (Bjorn Helgaas) - Remove unnecessary guard (Bjorn Helgaas) - Clean up dmesg "Slot(%s)" messages (Bjorn Helgaas) - Remove useless pciehp_get_latch_status() calls (Bjorn Helgaas) - Clear attention LED on device add (Keith Busch) - Allow exclusive userspace control of indicators (Keith Busch) - Process all hotplug events before looking for new ones (Mayurkumar Patel) - Don't re-read Slot Status when queuing hotplug event (Mayurkumar Patel) - Don't re-read Slot Status when handling surprise event (Mayurkumar Patel) - Make explicitly non-modular (Paul Gortmaker) Power management: - Afford direct-complete to devices with non-standard PM (Lukas Wunner) - Query platform firmware for device power state (Lukas Wunner) - Recognize D3cold in pci_update_current_state() (Lukas Wunner) - Avoid unnecessary resume after direct-complete (Lukas Wunner) - Make explicitly non-modular (Paul Gortmaker) Virtualization: - Mark Atheros AR9580 to avoid bus reset (Maik Broemme) - Check for pci_setup_device() failure in pci_iov_add_virtfn() (Po Liu) MSI: - Enable PCI_MSI_IRQ_DOMAIN support for ARC (Joao Pinto) AER: - Remove aerdriver.nosourceid kernel parameter (Bjorn Helgaas) - Remove aerdriver.forceload kernel parameter (Bjorn Helgaas) - Fix aer_probe() kernel-doc comment (Cao jin) - Add bus flag to skip source ID matching (Jon Derrick) - Avoid memory allocation in interrupt handling path (Jon Derrick) - Cache capability position (Keith Busch) - Make explicitly non-modular (Paul Gortmaker) - Remove duplicate AER severity translation (Tyler Baicar) - Send correct severity to calculate AER severity (Tyler Baicar) Precision Time Measurement: - Add Precision Time Measurement (PTM) support (Jonathan Yong) - Add PTM clock granularity information (Bjorn Helgaas) - Add pci_enable_ptm() for drivers to enable PTM on endpoints (Bjorn Helgaas) Generic host bridge driver: - Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) - Make explicitly non-modular (Paul Gortmaker) Altera host bridge driver: - Remove redundant platform_get_resource() return value check (Bjorn Helgaas) - Poll for link training status after retraining the link (Ley Foon Tan) - Rework config accessors for use without a struct pci_bus (Ley Foon Tan) - Move retrain from fixup to altera_pcie_host_init() (Ley Foon Tan) - Make MSI explicitly non-modular (Paul Gortmaker) - Make explicitly non-modular (Paul Gortmaker) - Relax device number checking to allow SR-IOV (Po Liu) ARM Versatile host bridge driver: - Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) Axis ARTPEC-6 host bridge driver: - Drop __init from artpec6_add_pcie_port() (Niklas Cassel) Freescale i.MX6 host bridge driver: - Make explicitly non-modular (Paul Gortmaker) Intel VMD host bridge driver: - Add quirk for AER to ignore source ID (Jon Derrick) - Allocate IRQ lists with correct MSI-X count (Jon Derrick) - Convert to use pci_alloc_irq_vectors() API (Jon Derrick) - Eliminate vmd_vector member from list type (Jon Derrick) - Eliminate index member from IRQ list (Jon Derrick) - Synchronize with RCU freeing MSI IRQ descs (Keith Busch) - Request userspace control of PCIe hotplug indicators (Keith Busch) - Move VMD driver to drivers/pci/host (Keith Busch) Marvell Aardvark host bridge driver: - Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) - Remove redundant dev_err call in advk_pcie_probe() (Wei Yongjun) Microsoft Hyper-V host bridge driver: - Use zero-length array in struct pci_packet (Dexuan Cui) - Use pci_function_description[0] in struct definitions (Dexuan Cui) - Remove the unused 'wrk' in struct hv_pcibus_device (Dexuan Cui) - Handle vmbus_sendpacket() failure in hv_compose_msi_msg() (Dexuan Cui) - Handle hv_pci_generic_compl() error case (Dexuan Cui) - Use list_move_tail() instead of list_del() + list_add_tail() (Wei Yongjun) NVIDIA Tegra host bridge driver: - Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) - Remove redundant _data suffix (Thierry Reding) - Use of_device_get_match_data() (Thierry Reding) Qualcomm host bridge driver: - Make explicitly non-modular (Paul Gortmaker) Renesas R-Car host bridge driver: - Consolidate register space lookup and ioremap (Bjorn Helgaas) - Don't disable/unprepare clocks on prepare/enable failure (Geert Uytterhoeven) - Add multi-MSI support (Grigory Kletsko) - Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) - Fix some checkpatch warnings (Sergei Shtylyov) - Try increasing PCIe link speed to 5 GT/s at boot (Sergei Shtylyov) Rockchip host bridge driver: - Add DT bindings for Rockchip PCIe controller (Shawn Lin) - Add Rockchip PCIe controller support (Shawn Lin) - Improve the deassert sequence of four reset pins (Shawn Lin) - Fix wrong transmitted FTS count (Shawn Lin) - Increase the Max Credit update interval (Rajat Jain) Samsung Exynos host bridge driver: - Make explicitly non-modular (Paul Gortmaker) ST Microelectronics SPEAr13xx host bridge driver: - Make explicitly non-modular (Paul Gortmaker) Synopsys DesignWare host bridge driver: - Return data directly from dw_pcie_readl_rc() (Bjorn Helgaas) - Exchange viewport of `MEMORYs' and `CFGs/IOs' (Dong Bo) - Check LTSSM training bit before deciding link is up (Jisheng Zhang) - Move link wait definitions to .c file (Joao Pinto) - Wait for iATU enable (Joao Pinto) - Add iATU Unroll feature (Joao Pinto) - Fix pci_remap_iospace() failure path (Lorenzo Pieralisi) - Make explicitly non-modular (Paul Gortmaker) - Relax device number checking to allow SR-IOV (Po Liu) - Keep viewport fixed for IO transaction if num_viewport > 2 (Pratyush Anand) - Remove redundant platform_get_resource() return value check (Wei Yongjun) TI DRA7xx host bridge driver: - Make explicitly non-modular (Paul Gortmaker) TI Keystone host bridge driver: - Propagate request_irq() failure (Wei Yongjun) Xilinx AXI host bridge driver: - Keep both legacy and MSI interrupt domain references (Bharat Kumar Gogada) - Clear interrupt register for invalid interrupt (Bharat Kumar Gogada) - Clear correct MSI set bit (Bharat Kumar Gogada) - Dispose of MSI virtual IRQ (Bharat Kumar Gogada) - Make explicitly non-modular (Paul Gortmaker) - Relax device number checking to allow SR-IOV (Po Liu) Xilinx NWL host bridge driver: - Expand error logging (Bharat Kumar Gogada) - Enable all MSI interrupts using MSI mask (Bharat Kumar Gogada) - Make explicitly non-modular (Paul Gortmaker) Miscellaneous: - Drop CONFIG_KEXEC_CORE ifdeffery (Lukas Wunner) - portdrv: Make explicitly non-modular (Paul Gortmaker) - Make DPC explicitly non-modular (Paul Gortmaker)" * tag 'pci-v4.9-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (105 commits) x86/PCI: VMD: Move VMD driver to drivers/pci/host PCI: rockchip: Fix wrong transmitted FTS count PCI: rockchip: Improve the deassert sequence of four reset pins PCI: rockchip: Increase the Max Credit update interval PCI: rcar: Try increasing PCIe link speed to 5 GT/s at boot PCI/AER: Fix aer_probe() kernel-doc comment PCI: Ignore requested alignment for VF BARs PCI: Ignore requested alignment for PROBE_ONLY and fixed resources PCI: Avoid unnecessary resume after direct-complete PCI: Recognize D3cold in pci_update_current_state() PCI: Query platform firmware for device power state PCI: Afford direct-complete to devices with non-standard PM PCI/AER: Cache capability position PCI/AER: Avoid memory allocation in interrupt handling path x86/PCI: VMD: Request userspace control of PCIe hotplug indicators PCI: pciehp: Allow exclusive userspace control of indicators ACPI / APEI: Send correct severity to calculate AER severity PCI/AER: Remove duplicate AER severity translation x86/PCI: VMD: Synchronize with RCU freeing MSI IRQ descs x86/PCI: VMD: Eliminate index member from IRQ list ...
This commit is contained in:
commit
e6e3d8f8f4
@ -49,25 +49,17 @@ depends on CONFIG_PCIEPORTBUS, so pls. set CONFIG_PCIEPORTBUS=y and
|
||||
CONFIG_PCIEAER = y.
|
||||
|
||||
2.2 Load PCI Express AER Root Driver
|
||||
There is a case where a system has AER support in BIOS. Enabling the AER
|
||||
Root driver and having AER support in BIOS may result unpredictable
|
||||
behavior. To avoid this conflict, a successful load of the AER Root driver
|
||||
requires ACPI _OSC support in the BIOS to allow the AER Root driver to
|
||||
request for native control of AER. See the PCI FW 3.0 Specification for
|
||||
details regarding OSC usage. Currently, lots of firmwares don't provide
|
||||
_OSC support while they use PCI Express. To support such firmwares,
|
||||
forceload, a parameter of type bool, could enable AER to continue to
|
||||
be initiated although firmwares have no _OSC support. To enable the
|
||||
walkaround, pls. add aerdriver.forceload=y to kernel boot parameter line
|
||||
when booting kernel. Note that forceload=n by default.
|
||||
|
||||
nosourceid, another parameter of type bool, can be used when broken
|
||||
hardware (mostly chipsets) has root ports that cannot obtain the reporting
|
||||
source ID. nosourceid=n by default.
|
||||
Some systems have AER support in firmware. Enabling Linux AER support at
|
||||
the same time the firmware handles AER may result in unpredictable
|
||||
behavior. Therefore, Linux does not handle AER events unless the firmware
|
||||
grants AER control to the OS via the ACPI _OSC method. See the PCI FW 3.0
|
||||
Specification for details regarding _OSC usage.
|
||||
|
||||
2.3 AER error output
|
||||
When a PCI-E AER error is captured, an error message will be outputted to
|
||||
console. If it's a correctable error, it is outputted as a warning.
|
||||
|
||||
When a PCIe AER error is captured, an error message will be output to
|
||||
console. If it's a correctable error, it is output as a warning.
|
||||
Otherwise, it is printed as an error. So users could choose different
|
||||
log level to filter out correctable error messages.
|
||||
|
||||
|
@ -17,6 +17,8 @@ Required properties:
|
||||
- num-lanes: number of lanes to use
|
||||
|
||||
Optional properties:
|
||||
- num-viewport: number of view ports configured in hardware. If a platform
|
||||
does not specify it, the driver assumes 2.
|
||||
- num-lanes: number of lanes to use (this property should be specified unless
|
||||
the link is brought already up in BIOS)
|
||||
- reset-gpio: gpio pin number of power good signal
|
||||
@ -44,4 +46,5 @@ Example configuration:
|
||||
interrupts = <25>, <24>;
|
||||
#interrupt-cells = <1>;
|
||||
num-lanes = <1>;
|
||||
num-viewport = <3>;
|
||||
};
|
||||
|
106
Documentation/devicetree/bindings/pci/rockchip-pcie.txt
Normal file
106
Documentation/devicetree/bindings/pci/rockchip-pcie.txt
Normal file
@ -0,0 +1,106 @@
|
||||
* Rockchip AXI PCIe Root Port Bridge DT description
|
||||
|
||||
Required properties:
|
||||
- #address-cells: Address representation for root ports, set to <3>
|
||||
- #size-cells: Size representation for root ports, set to <2>
|
||||
- #interrupt-cells: specifies the number of cells needed to encode an
|
||||
interrupt source. The value must be 1.
|
||||
- compatible: Should contain "rockchip,rk3399-pcie"
|
||||
- reg: Two register ranges as listed in the reg-names property
|
||||
- reg-names: Must include the following names
|
||||
- "axi-base"
|
||||
- "apb-base"
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: Must include the following entries:
|
||||
- "aclk"
|
||||
- "aclk-perf"
|
||||
- "hclk"
|
||||
- "pm"
|
||||
- msi-map: Maps a Requester ID to an MSI controller and associated
|
||||
msi-specifier data. See ./pci-msi.txt
|
||||
- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
|
||||
- phy-names: MUST be "pcie-phy".
|
||||
- interrupts: Three interrupt entries must be specified.
|
||||
- interrupt-names: Must include the following names
|
||||
- "sys"
|
||||
- "legacy"
|
||||
- "client"
|
||||
- resets: Must contain five entries for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names: Must include the following names
|
||||
- "core"
|
||||
- "mgmt"
|
||||
- "mgmt-sticky"
|
||||
- "pipe"
|
||||
- pinctrl-names : The pin control state names
|
||||
- pinctrl-0: The "default" pinctrl state
|
||||
- #interrupt-cells: specifies the number of cells needed to encode an
|
||||
interrupt source. The value must be 1.
|
||||
- interrupt-map-mask and interrupt-map: standard PCI properties
|
||||
|
||||
Optional Property:
|
||||
- ep-gpios: contain the entry for pre-reset gpio
|
||||
- num-lanes: number of lanes to use
|
||||
- vpcie3v3-supply: The phandle to the 3.3v regulator to use for PCIe.
|
||||
- vpcie1v8-supply: The phandle to the 1.8v regulator to use for PCIe.
|
||||
- vpcie0v9-supply: The phandle to the 0.9v regulator to use for PCIe.
|
||||
|
||||
*Interrupt controller child node*
|
||||
The core controller provides a single interrupt for legacy INTx. The PCIe node
|
||||
should contain an interrupt controller node as a target for the PCI
|
||||
'interrupt-map' property. This node represents the domain at which the four
|
||||
INTx interrupts are decoded and routed.
|
||||
|
||||
|
||||
Required properties for Interrupt controller child node:
|
||||
- interrupt-controller: identifies the node as an interrupt controller
|
||||
- #address-cells: specifies the number of cells needed to encode an
|
||||
address. The value must be 0.
|
||||
- #interrupt-cells: specifies the number of cells needed to encode an
|
||||
interrupt source. The value must be 1.
|
||||
|
||||
Example:
|
||||
|
||||
pcie0: pcie@f8000000 {
|
||||
compatible = "rockchip,rk3399-pcie";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
|
||||
<&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
|
||||
clock-names = "aclk", "aclk-perf",
|
||||
"hclk", "pm";
|
||||
bus-range = <0x0 0x1>;
|
||||
interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH 0>,
|
||||
<GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH 0>,
|
||||
<GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
interrupt-names = "sys", "legacy", "client";
|
||||
assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
|
||||
assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
|
||||
assigned-clock-rates = <100000000>;
|
||||
ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
|
||||
ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
|
||||
0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
|
||||
num-lanes = <4>;
|
||||
msi-map = <0x0 &its 0x0 0x1000>;
|
||||
reg = <0x0 0xf8000000 0x0 0x2000000>, <0x0 0xfd000000 0x0 0x1000000>;
|
||||
reg-names = "axi-base", "apb-base";
|
||||
resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
|
||||
<&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
|
||||
reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
|
||||
phys = <&pcie_phy>;
|
||||
phy-names = "pcie-phy";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie_clkreq>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0 0 0 1 &pcie0_intc 0>,
|
||||
<0 0 0 2 &pcie0_intc 1>,
|
||||
<0 0 0 3 &pcie0_intc 2>,
|
||||
<0 0 0 4 &pcie0_intc 3>;
|
||||
pcie0_intc: interrupt-controller {
|
||||
interrupt-controller;
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
};
|
@ -9256,6 +9256,15 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt
|
||||
F: drivers/pci/host/pcie-hisi.c
|
||||
|
||||
PCIE DRIVER FOR ROCKCHIP
|
||||
M: Shawn Lin <shawn.lin@rock-chips.com>
|
||||
M: Wenrui Li <wenrui.li@rock-chips.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
L: linux-rockchip@lists.infradead.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/pci/rockchip-pcie.txt
|
||||
F: drivers/pci/host/pcie-rockchip.c
|
||||
|
||||
PCIE DRIVER FOR QUALCOMM MSM
|
||||
M: Stanimir Varbanov <svarbanov@mm-sol.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
|
@ -25,6 +25,7 @@ generic-y += mcs_spinlock.h
|
||||
generic-y += mm-arch-hooks.h
|
||||
generic-y += mman.h
|
||||
generic-y += msgbuf.h
|
||||
generic-y += msi.h
|
||||
generic-y += param.h
|
||||
generic-y += parport.h
|
||||
generic-y += pci.h
|
||||
|
@ -632,10 +632,10 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose,
|
||||
}
|
||||
}
|
||||
|
||||
/* Decide whether to display the domain number in /proc */
|
||||
/* Display the domain number in /proc */
|
||||
int pci_proc_domain(struct pci_bus *bus)
|
||||
{
|
||||
return 0;
|
||||
return pci_domain_nr(bus);
|
||||
}
|
||||
|
||||
/* This header fixup will do the resource fixup for all devices as they are
|
||||
|
@ -2757,19 +2757,6 @@ config PMC_ATOM
|
||||
def_bool y
|
||||
depends on PCI
|
||||
|
||||
config VMD
|
||||
depends on PCI_MSI
|
||||
tristate "Volume Management Device Driver"
|
||||
default N
|
||||
---help---
|
||||
Adds support for the Intel Volume Management Device (VMD). VMD is a
|
||||
secondary PCI host bridge that allows PCI Express root ports,
|
||||
and devices attached to them, to be removed from the default
|
||||
PCI domain and placed within the VMD domain. This provides
|
||||
more bus resources than are otherwise possible with a
|
||||
single domain. If you know your system provides one of these and
|
||||
has devices attached to it, say Y; if you are not sure, say N.
|
||||
|
||||
source "net/Kconfig"
|
||||
|
||||
source "drivers/Kconfig"
|
||||
|
@ -23,6 +23,9 @@ struct pci_sysdata {
|
||||
#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
|
||||
void *fwnode; /* IRQ domain for MSI assignment */
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_VMD)
|
||||
bool vmd_domain; /* True if in Intel VMD domain */
|
||||
#endif
|
||||
};
|
||||
|
||||
extern int pci_routeirq;
|
||||
@ -56,6 +59,17 @@ static inline void *_pci_root_bus_fwnode(struct pci_bus *bus)
|
||||
#define pci_root_bus_fwnode _pci_root_bus_fwnode
|
||||
#endif
|
||||
|
||||
static inline bool is_vmd(struct pci_bus *bus)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_VMD)
|
||||
struct pci_sysdata *sd = bus->sysdata;
|
||||
|
||||
return sd->vmd_domain;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Can be used to override the logic in pci_scan_bus for skipping
|
||||
already-configured bus numbers - to be used for buggy BIOSes
|
||||
or architectures with incomplete PCI setup by the loader */
|
||||
|
@ -23,8 +23,6 @@ obj-y += bus_numa.o
|
||||
obj-$(CONFIG_AMD_NB) += amd_bus.o
|
||||
obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o
|
||||
|
||||
obj-$(CONFIG_VMD) += vmd.o
|
||||
|
||||
ifeq ($(CONFIG_PCI_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
@ -677,6 +677,12 @@ static void set_dma_domain_ops(struct pci_dev *pdev)
|
||||
static void set_dma_domain_ops(struct pci_dev *pdev) {}
|
||||
#endif
|
||||
|
||||
static void set_dev_domain_options(struct pci_dev *pdev)
|
||||
{
|
||||
if (is_vmd(pdev->bus))
|
||||
pdev->hotplug_user_indicators = 1;
|
||||
}
|
||||
|
||||
int pcibios_add_device(struct pci_dev *dev)
|
||||
{
|
||||
struct setup_data *data;
|
||||
@ -707,6 +713,7 @@ int pcibios_add_device(struct pci_dev *dev)
|
||||
iounmap(data);
|
||||
}
|
||||
set_dma_domain_ops(dev);
|
||||
set_dev_domain_options(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -457,7 +457,7 @@ static void ghes_do_proc(struct ghes *ghes,
|
||||
|
||||
devfn = PCI_DEVFN(pcie_err->device_id.device,
|
||||
pcie_err->device_id.function);
|
||||
aer_severity = cper_severity_to_aer(sev);
|
||||
aer_severity = cper_severity_to_aer(gdata->error_severity);
|
||||
|
||||
/*
|
||||
* If firmware reset the component to contain
|
||||
|
@ -25,7 +25,7 @@ config PCI_MSI
|
||||
If you don't know what to do here, say Y.
|
||||
|
||||
config PCI_MSI_IRQ_DOMAIN
|
||||
def_bool ARM || ARM64 || X86
|
||||
def_bool ARC || ARM || ARM64 || X86
|
||||
depends on PCI_MSI
|
||||
select GENERIC_MSI_IRQ_DOMAIN
|
||||
|
||||
|
@ -274,4 +274,31 @@ config PCIE_ARTPEC6
|
||||
Say Y here to enable PCIe controller support on Axis ARTPEC-6
|
||||
SoCs. This PCIe controller uses the DesignWare core.
|
||||
|
||||
config PCIE_ROCKCHIP
|
||||
bool "Rockchip PCIe controller"
|
||||
depends on ARCH_ROCKCHIP
|
||||
depends on OF
|
||||
depends on PCI_MSI_IRQ_DOMAIN
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Say Y here if you want internal PCI support on Rockchip SoC.
|
||||
There is 1 internal PCIe port available to support GEN2 with
|
||||
4 slots.
|
||||
|
||||
config VMD
|
||||
depends on PCI_MSI && X86_64
|
||||
tristate "Intel Volume Management Device Driver"
|
||||
default N
|
||||
---help---
|
||||
Adds support for the Intel Volume Management Device (VMD). VMD is a
|
||||
secondary PCI host bridge that allows PCI Express root ports,
|
||||
and devices attached to them, to be removed from the default
|
||||
PCI domain and placed within the VMD domain. This provides
|
||||
more bus resources than are otherwise possible with a
|
||||
single domain. If you know your system provides one of these and
|
||||
has devices attached to it, say Y; if you are not sure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vmd.
|
||||
|
||||
endmenu
|
||||
|
@ -31,3 +31,5 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
|
||||
obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
|
||||
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
|
||||
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
|
||||
obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
|
||||
obj-$(CONFIG_VMD) += vmd.o
|
||||
|
@ -848,7 +848,7 @@ static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
|
||||
int err, res_valid = 0;
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct resource_entry *win;
|
||||
struct resource_entry *win, *tmp;
|
||||
resource_size_t iobase;
|
||||
|
||||
INIT_LIST_HEAD(&pcie->resources);
|
||||
@ -862,7 +862,7 @@ static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
|
||||
if (err)
|
||||
goto out_release_res;
|
||||
|
||||
resource_list_for_each_entry(win, &pcie->resources) {
|
||||
resource_list_for_each_entry_safe(win, tmp, &pcie->resources) {
|
||||
struct resource *res = win->res;
|
||||
|
||||
switch (resource_type(res)) {
|
||||
@ -874,9 +874,11 @@ static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
|
||||
lower_32_bits(res->start),
|
||||
OB_PCIE_IO);
|
||||
err = pci_remap_iospace(res, iobase);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_warn(dev, "error %d: failed to map resource %pR\n",
|
||||
err, res);
|
||||
resource_list_destroy_entry(win);
|
||||
}
|
||||
break;
|
||||
case IORESOURCE_MEM:
|
||||
advk_pcie_set_ob_win(pcie, 0,
|
||||
@ -925,10 +927,8 @@ static int advk_pcie_probe(struct platform_device *pdev)
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pcie->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pcie->base)) {
|
||||
dev_err(&pdev->dev, "Failed to map registers\n");
|
||||
if (IS_ERR(pcie->base))
|
||||
return PTR_ERR(pcie->base);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler,
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/phy/phy.h>
|
||||
@ -443,25 +443,6 @@ err_phy:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit dra7xx_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dra7xx_pcie *dra7xx = platform_get_drvdata(pdev);
|
||||
struct pcie_port *pp = &dra7xx->pp;
|
||||
struct device *dev = &pdev->dev;
|
||||
int count = dra7xx->phy_count;
|
||||
|
||||
if (pp->irq_domain)
|
||||
irq_domain_remove(pp->irq_domain);
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_disable(dev);
|
||||
while (count--) {
|
||||
phy_power_off(dra7xx->phy[count]);
|
||||
phy_exit(dra7xx->phy[count]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dra7xx_pcie_suspend(struct device *dev)
|
||||
{
|
||||
@ -545,19 +526,13 @@ static const struct of_device_id of_dra7xx_pcie_match[] = {
|
||||
{ .compatible = "ti,dra7-pcie", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dra7xx_pcie_match);
|
||||
|
||||
static struct platform_driver dra7xx_pcie_driver = {
|
||||
.remove = __exit_p(dra7xx_pcie_remove),
|
||||
.driver = {
|
||||
.name = "dra7-pcie",
|
||||
.of_match_table = of_dra7xx_pcie_match,
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &dra7xx_pcie_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe);
|
||||
|
||||
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
|
||||
MODULE_DESCRIPTION("TI PCIe controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
builtin_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe);
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -425,12 +425,15 @@ static void exynos_pcie_enable_interrupts(struct pcie_port *pp)
|
||||
exynos_pcie_msi_init(pp);
|
||||
}
|
||||
|
||||
static inline void exynos_pcie_readl_rc(struct pcie_port *pp,
|
||||
void __iomem *dbi_base, u32 *val)
|
||||
static inline u32 exynos_pcie_readl_rc(struct pcie_port *pp,
|
||||
void __iomem *dbi_base)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
exynos_pcie_sideband_dbi_r_mode(pp, true);
|
||||
*val = readl(dbi_base);
|
||||
val = readl(dbi_base);
|
||||
exynos_pcie_sideband_dbi_r_mode(pp, false);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void exynos_pcie_writel_rc(struct pcie_port *pp,
|
||||
@ -624,7 +627,6 @@ static const struct of_device_id exynos_pcie_of_match[] = {
|
||||
{ .compatible = "samsung,exynos5440-pcie", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_pcie_of_match);
|
||||
|
||||
static struct platform_driver exynos_pcie_driver = {
|
||||
.remove = __exit_p(exynos_pcie_remove),
|
||||
@ -641,7 +643,3 @@ static int __init exynos_pcie_init(void)
|
||||
return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe);
|
||||
}
|
||||
subsys_initcall(exynos_pcie_init);
|
||||
|
||||
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung PCIe host controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -1,4 +1,6 @@
|
||||
/*
|
||||
* Generic PCI host driver common code
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
@ -17,7 +19,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
@ -29,7 +30,7 @@ static int gen_pci_parse_request_of_pci_ranges(struct device *dev,
|
||||
int err, res_valid = 0;
|
||||
struct device_node *np = dev->of_node;
|
||||
resource_size_t iobase;
|
||||
struct resource_entry *win;
|
||||
struct resource_entry *win, *tmp;
|
||||
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase);
|
||||
if (err)
|
||||
@ -39,15 +40,17 @@ static int gen_pci_parse_request_of_pci_ranges(struct device *dev,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
resource_list_for_each_entry(win, resources) {
|
||||
resource_list_for_each_entry_safe(win, tmp, resources) {
|
||||
struct resource *res = win->res;
|
||||
|
||||
switch (resource_type(res)) {
|
||||
case IORESOURCE_IO:
|
||||
err = pci_remap_iospace(res, iobase);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_warn(dev, "error %d: failed to map resource %pR\n",
|
||||
err, res);
|
||||
resource_list_destroy_entry(win);
|
||||
}
|
||||
break;
|
||||
case IORESOURCE_MEM:
|
||||
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
||||
@ -162,7 +165,3 @@ int pci_host_common_probe(struct platform_device *pdev,
|
||||
pci_bus_add_devices(bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("Generic PCI host driver common code");
|
||||
MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -200,11 +200,11 @@ struct tran_int_desc {
|
||||
*/
|
||||
|
||||
struct pci_message {
|
||||
u32 message_type;
|
||||
u32 type;
|
||||
} __packed;
|
||||
|
||||
struct pci_child_message {
|
||||
u32 message_type;
|
||||
struct pci_message message_type;
|
||||
union win_slot_encoding wslot;
|
||||
} __packed;
|
||||
|
||||
@ -222,7 +222,8 @@ struct pci_packet {
|
||||
void (*completion_func)(void *context, struct pci_response *resp,
|
||||
int resp_packet_size);
|
||||
void *compl_ctxt;
|
||||
struct pci_message message;
|
||||
|
||||
struct pci_message message[0];
|
||||
};
|
||||
|
||||
/*
|
||||
@ -258,7 +259,7 @@ struct pci_bus_d0_entry {
|
||||
struct pci_bus_relations {
|
||||
struct pci_incoming_message incoming;
|
||||
u32 device_count;
|
||||
struct pci_function_description func[1];
|
||||
struct pci_function_description func[0];
|
||||
} __packed;
|
||||
|
||||
struct pci_q_res_req_response {
|
||||
@ -314,7 +315,7 @@ struct pci_dev_incoming {
|
||||
} __packed;
|
||||
|
||||
struct pci_eject_response {
|
||||
u32 message_type;
|
||||
struct pci_message message_type;
|
||||
union win_slot_encoding wslot;
|
||||
u32 status;
|
||||
} __packed;
|
||||
@ -373,7 +374,6 @@ struct hv_pcibus_device {
|
||||
|
||||
struct list_head children;
|
||||
struct list_head dr_list;
|
||||
struct work_struct wrk;
|
||||
|
||||
struct msi_domain_info msi_info;
|
||||
struct msi_controller msi_chip;
|
||||
@ -393,7 +393,7 @@ struct hv_dr_work {
|
||||
struct hv_dr_state {
|
||||
struct list_head list_entry;
|
||||
u32 device_count;
|
||||
struct pci_function_description func[1];
|
||||
struct pci_function_description func[0];
|
||||
};
|
||||
|
||||
enum hv_pcichild_state {
|
||||
@ -447,15 +447,16 @@ struct hv_pci_compl {
|
||||
* for any message for which the completion packet contains a
|
||||
* status and nothing else.
|
||||
*/
|
||||
static
|
||||
void
|
||||
hv_pci_generic_compl(void *context, struct pci_response *resp,
|
||||
int resp_packet_size)
|
||||
static void hv_pci_generic_compl(void *context, struct pci_response *resp,
|
||||
int resp_packet_size)
|
||||
{
|
||||
struct hv_pci_compl *comp_pkt = context;
|
||||
|
||||
if (resp_packet_size >= offsetofend(struct pci_response, status))
|
||||
comp_pkt->completion_status = resp->status;
|
||||
else
|
||||
comp_pkt->completion_status = -1;
|
||||
|
||||
complete(&comp_pkt->host_event);
|
||||
}
|
||||
|
||||
@ -694,13 +695,12 @@ static void hv_int_desc_free(struct hv_pci_dev *hpdev,
|
||||
struct pci_delete_interrupt *int_pkt;
|
||||
struct {
|
||||
struct pci_packet pkt;
|
||||
u8 buffer[sizeof(struct pci_delete_interrupt) -
|
||||
sizeof(struct pci_message)];
|
||||
u8 buffer[sizeof(struct pci_delete_interrupt)];
|
||||
} ctxt;
|
||||
|
||||
memset(&ctxt, 0, sizeof(ctxt));
|
||||
int_pkt = (struct pci_delete_interrupt *)&ctxt.pkt.message;
|
||||
int_pkt->message_type.message_type =
|
||||
int_pkt->message_type.type =
|
||||
PCI_DELETE_INTERRUPT_MESSAGE;
|
||||
int_pkt->wslot.slot = hpdev->desc.win_slot.slot;
|
||||
int_pkt->int_desc = *int_desc;
|
||||
@ -847,8 +847,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
struct cpumask *affinity;
|
||||
struct {
|
||||
struct pci_packet pkt;
|
||||
u8 buffer[sizeof(struct pci_create_interrupt) -
|
||||
sizeof(struct pci_message)];
|
||||
u8 buffer[sizeof(struct pci_create_interrupt)];
|
||||
} ctxt;
|
||||
int cpu;
|
||||
int ret;
|
||||
@ -876,7 +875,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
ctxt.pkt.completion_func = hv_pci_compose_compl;
|
||||
ctxt.pkt.compl_ctxt = ∁
|
||||
int_pkt = (struct pci_create_interrupt *)&ctxt.pkt.message;
|
||||
int_pkt->message_type.message_type = PCI_CREATE_INTERRUPT_MESSAGE;
|
||||
int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE;
|
||||
int_pkt->wslot.slot = hpdev->desc.win_slot.slot;
|
||||
int_pkt->int_desc.vector = cfg->vector;
|
||||
int_pkt->int_desc.vector_count = 1;
|
||||
@ -897,8 +896,10 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
sizeof(*int_pkt), (unsigned long)&ctxt.pkt,
|
||||
VM_PKT_DATA_INBAND,
|
||||
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
||||
if (!ret)
|
||||
wait_for_completion(&comp.comp_pkt.host_event);
|
||||
if (ret)
|
||||
goto free_int_desc;
|
||||
|
||||
wait_for_completion(&comp.comp_pkt.host_event);
|
||||
|
||||
if (comp.comp_pkt.completion_status < 0) {
|
||||
dev_err(&hbus->hdev->device,
|
||||
@ -1289,7 +1290,7 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
|
||||
pkt.init_packet.compl_ctxt = &comp_pkt;
|
||||
pkt.init_packet.completion_func = q_resource_requirements;
|
||||
res_req = (struct pci_child_message *)&pkt.init_packet.message;
|
||||
res_req->message_type = PCI_QUERY_RESOURCE_REQUIREMENTS;
|
||||
res_req->message_type.type = PCI_QUERY_RESOURCE_REQUIREMENTS;
|
||||
res_req->wslot.slot = desc->win_slot.slot;
|
||||
|
||||
ret = vmbus_sendpacket(hbus->hdev->channel, res_req,
|
||||
@ -1466,8 +1467,7 @@ static void pci_devices_present_work(struct work_struct *work)
|
||||
if (hpdev->reported_missing) {
|
||||
found = true;
|
||||
put_pcichild(hpdev, hv_pcidev_ref_childlist);
|
||||
list_del(&hpdev->list_entry);
|
||||
list_add_tail(&hpdev->list_entry, &removed);
|
||||
list_move_tail(&hpdev->list_entry, &removed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1558,8 +1558,7 @@ static void hv_eject_device_work(struct work_struct *work)
|
||||
int wslot;
|
||||
struct {
|
||||
struct pci_packet pkt;
|
||||
u8 buffer[sizeof(struct pci_eject_response) -
|
||||
sizeof(struct pci_message)];
|
||||
u8 buffer[sizeof(struct pci_eject_response)];
|
||||
} ctxt;
|
||||
|
||||
hpdev = container_of(work, struct hv_pci_dev, wrk);
|
||||
@ -1585,7 +1584,7 @@ static void hv_eject_device_work(struct work_struct *work)
|
||||
|
||||
memset(&ctxt, 0, sizeof(ctxt));
|
||||
ejct_pkt = (struct pci_eject_response *)&ctxt.pkt.message;
|
||||
ejct_pkt->message_type = PCI_EJECTION_COMPLETE;
|
||||
ejct_pkt->message_type.type = PCI_EJECTION_COMPLETE;
|
||||
ejct_pkt->wslot.slot = hpdev->desc.win_slot.slot;
|
||||
vmbus_sendpacket(hpdev->hbus->hdev->channel, ejct_pkt,
|
||||
sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt,
|
||||
@ -1688,7 +1687,7 @@ static void hv_pci_onchannelcallback(void *context)
|
||||
case VM_PKT_DATA_INBAND:
|
||||
|
||||
new_message = (struct pci_incoming_message *)buffer;
|
||||
switch (new_message->message_type.message_type) {
|
||||
switch (new_message->message_type.type) {
|
||||
case PCI_BUS_RELATIONS:
|
||||
|
||||
bus_rel = (struct pci_bus_relations *)buffer;
|
||||
@ -1719,7 +1718,7 @@ static void hv_pci_onchannelcallback(void *context)
|
||||
default:
|
||||
dev_warn(&hbus->hdev->device,
|
||||
"Unimplemented protocol message %x\n",
|
||||
new_message->message_type.message_type);
|
||||
new_message->message_type.type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -1772,7 +1771,7 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev)
|
||||
pkt->completion_func = hv_pci_generic_compl;
|
||||
pkt->compl_ctxt = &comp_pkt;
|
||||
version_req = (struct pci_version_request *)&pkt->message;
|
||||
version_req->message_type.message_type = PCI_QUERY_PROTOCOL_VERSION;
|
||||
version_req->message_type.type = PCI_QUERY_PROTOCOL_VERSION;
|
||||
version_req->protocol_version = PCI_PROTOCOL_VERSION_CURRENT;
|
||||
|
||||
ret = vmbus_sendpacket(hdev->channel, version_req,
|
||||
@ -1973,7 +1972,7 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
|
||||
pkt->completion_func = hv_pci_generic_compl;
|
||||
pkt->compl_ctxt = &comp_pkt;
|
||||
d0_entry = (struct pci_bus_d0_entry *)&pkt->message;
|
||||
d0_entry->message_type.message_type = PCI_BUS_D0ENTRY;
|
||||
d0_entry->message_type.type = PCI_BUS_D0ENTRY;
|
||||
d0_entry->mmio_base = hbus->mem_config->start;
|
||||
|
||||
ret = vmbus_sendpacket(hdev->channel, d0_entry, sizeof(*d0_entry),
|
||||
@ -2019,7 +2018,7 @@ static int hv_pci_query_relations(struct hv_device *hdev)
|
||||
return -ENOTEMPTY;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.message_type = PCI_QUERY_BUS_RELATIONS;
|
||||
message.type = PCI_QUERY_BUS_RELATIONS;
|
||||
|
||||
ret = vmbus_sendpacket(hdev->channel, &message, sizeof(message),
|
||||
0, VM_PKT_DATA_INBAND, 0);
|
||||
@ -2072,8 +2071,8 @@ static int hv_send_resources_allocated(struct hv_device *hdev)
|
||||
init_completion(&comp_pkt.host_event);
|
||||
pkt->completion_func = hv_pci_generic_compl;
|
||||
pkt->compl_ctxt = &comp_pkt;
|
||||
pkt->message.message_type = PCI_RESOURCES_ASSIGNED;
|
||||
res_assigned = (struct pci_resources_assigned *)&pkt->message;
|
||||
res_assigned->message_type.type = PCI_RESOURCES_ASSIGNED;
|
||||
res_assigned->wslot.slot = hpdev->desc.win_slot.slot;
|
||||
|
||||
put_pcichild(hpdev, hv_pcidev_ref_by_slot);
|
||||
@ -2123,7 +2122,7 @@ static int hv_send_resources_released(struct hv_device *hdev)
|
||||
continue;
|
||||
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
pkt.message_type = PCI_RESOURCES_RELEASED;
|
||||
pkt.message_type.type = PCI_RESOURCES_RELEASED;
|
||||
pkt.wslot.slot = hpdev->desc.win_slot.slot;
|
||||
|
||||
put_pcichild(hpdev, hv_pcidev_ref_by_slot);
|
||||
@ -2290,7 +2289,7 @@ static int hv_pci_remove(struct hv_device *hdev)
|
||||
init_completion(&comp_pkt.host_event);
|
||||
pkt.teardown_packet.completion_func = hv_pci_generic_compl;
|
||||
pkt.teardown_packet.compl_ctxt = &comp_pkt;
|
||||
pkt.teardown_packet.message.message_type = PCI_BUS_D0EXIT;
|
||||
pkt.teardown_packet.message[0].type = PCI_BUS_D0EXIT;
|
||||
|
||||
ret = vmbus_sendpacket(hdev->channel, &pkt.teardown_packet.message,
|
||||
sizeof(struct pci_message),
|
||||
|
@ -739,7 +739,6 @@ static const struct of_device_id imx6_pcie_of_match[] = {
|
||||
{ .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
|
||||
|
||||
static struct platform_driver imx6_pcie_driver = {
|
||||
.driver = {
|
||||
@ -749,14 +748,8 @@ static struct platform_driver imx6_pcie_driver = {
|
||||
.shutdown = imx6_pcie_shutdown,
|
||||
};
|
||||
|
||||
/* Freescale PCIe driver does not allow module unload */
|
||||
|
||||
static int __init imx6_pcie_init(void)
|
||||
{
|
||||
return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
|
||||
}
|
||||
module_init(imx6_pcie_init);
|
||||
|
||||
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
|
||||
MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
device_initcall(imx6_pcie_init);
|
||||
|
@ -334,8 +334,9 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
|
||||
if (ks_pcie->error_irq <= 0)
|
||||
dev_info(&pdev->dev, "no error IRQ defined\n");
|
||||
else {
|
||||
if (request_irq(ks_pcie->error_irq, pcie_err_irq_handler,
|
||||
IRQF_SHARED, "pcie-error-irq", ks_pcie) < 0) {
|
||||
ret = request_irq(ks_pcie->error_irq, pcie_err_irq_handler,
|
||||
IRQF_SHARED, "pcie-error-irq", ks_pcie);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request error IRQ %d\n",
|
||||
ks_pcie->error_irq);
|
||||
return ret;
|
||||
|
@ -240,7 +240,7 @@ struct tegra_msi {
|
||||
};
|
||||
|
||||
/* used to differentiate between Tegra SoC generations */
|
||||
struct tegra_pcie_soc_data {
|
||||
struct tegra_pcie_soc {
|
||||
unsigned int num_ports;
|
||||
unsigned int msi_base_shift;
|
||||
u32 pads_pll_ctl;
|
||||
@ -300,7 +300,7 @@ struct tegra_pcie {
|
||||
struct regulator_bulk_data *supplies;
|
||||
unsigned int num_supplies;
|
||||
|
||||
const struct tegra_pcie_soc_data *soc_data;
|
||||
const struct tegra_pcie_soc *soc;
|
||||
struct dentry *debugfs;
|
||||
};
|
||||
|
||||
@ -542,8 +542,8 @@ static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
|
||||
|
||||
static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
|
||||
unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
|
||||
const struct tegra_pcie_soc *soc = port->pcie->soc;
|
||||
unsigned long value;
|
||||
|
||||
/* enable reference clock */
|
||||
@ -562,8 +562,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
|
||||
|
||||
static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
|
||||
unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
|
||||
const struct tegra_pcie_soc *soc = port->pcie->soc;
|
||||
unsigned long value;
|
||||
|
||||
/* assert port reset */
|
||||
@ -621,7 +621,11 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pci_add_resource_offset(&sys->resources, &pcie->pio, sys->io_offset);
|
||||
err = pci_remap_iospace(&pcie->pio, pcie->io.start);
|
||||
if (!err)
|
||||
pci_add_resource_offset(&sys->resources, &pcie->pio,
|
||||
sys->io_offset);
|
||||
|
||||
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
|
||||
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
|
||||
sys->mem_offset);
|
||||
@ -631,7 +635,6 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pci_remap_iospace(&pcie->pio, pcie->io.start);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -774,7 +777,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
|
||||
|
||||
static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
u32 value;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(timeout);
|
||||
@ -790,7 +793,7 @@ static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
|
||||
|
||||
static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
u32 value;
|
||||
int err;
|
||||
|
||||
@ -845,7 +848,7 @@ static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
|
||||
|
||||
static int tegra_pcie_phy_disable(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
u32 value;
|
||||
|
||||
/* disable TX/RX data */
|
||||
@ -906,7 +909,7 @@ static int tegra_pcie_port_phy_power_off(struct tegra_pcie_port *port)
|
||||
|
||||
static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
struct tegra_pcie_port *port;
|
||||
int err;
|
||||
|
||||
@ -974,7 +977,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
|
||||
|
||||
static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
struct tegra_pcie_port *port;
|
||||
unsigned long value;
|
||||
int err;
|
||||
@ -1067,7 +1070,7 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
|
||||
|
||||
static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
int err;
|
||||
|
||||
reset_control_assert(pcie->pcie_xrst);
|
||||
@ -1117,7 +1120,7 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||
|
||||
static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
|
||||
pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
|
||||
if (IS_ERR(pcie->pex_clk))
|
||||
@ -1234,7 +1237,7 @@ static int tegra_pcie_port_get_phys(struct tegra_pcie_port *port)
|
||||
|
||||
static int tegra_pcie_phys_get(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
struct device_node *np = pcie->dev->of_node;
|
||||
struct tegra_pcie_port *port;
|
||||
int err;
|
||||
@ -1486,7 +1489,7 @@ static const struct irq_domain_ops msi_domain_ops = {
|
||||
static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(pcie->dev);
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
struct tegra_msi *msi = &pcie->msi;
|
||||
unsigned long base;
|
||||
int err;
|
||||
@ -1799,8 +1802,8 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
|
||||
|
||||
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
struct device_node *np = pcie->dev->of_node, *port;
|
||||
const struct tegra_pcie_soc *soc = pcie->soc;
|
||||
struct of_pci_range_parser parser;
|
||||
struct of_pci_range range;
|
||||
u32 lanes = 0, mask = 0;
|
||||
@ -2043,7 +2046,7 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tegra_pcie_soc_data tegra20_pcie_data = {
|
||||
static const struct tegra_pcie_soc tegra20_pcie = {
|
||||
.num_ports = 2,
|
||||
.msi_base_shift = 0,
|
||||
.pads_pll_ctl = PADS_PLL_CTL_TEGRA20,
|
||||
@ -2056,7 +2059,7 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = {
|
||||
.has_gen2 = false,
|
||||
};
|
||||
|
||||
static const struct tegra_pcie_soc_data tegra30_pcie_data = {
|
||||
static const struct tegra_pcie_soc tegra30_pcie = {
|
||||
.num_ports = 3,
|
||||
.msi_base_shift = 8,
|
||||
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
|
||||
@ -2070,7 +2073,7 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = {
|
||||
.has_gen2 = false,
|
||||
};
|
||||
|
||||
static const struct tegra_pcie_soc_data tegra124_pcie_data = {
|
||||
static const struct tegra_pcie_soc tegra124_pcie = {
|
||||
.num_ports = 2,
|
||||
.msi_base_shift = 8,
|
||||
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
|
||||
@ -2084,9 +2087,9 @@ static const struct tegra_pcie_soc_data tegra124_pcie_data = {
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_pcie_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie_data },
|
||||
{ .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data },
|
||||
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data },
|
||||
{ .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie },
|
||||
{ .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie },
|
||||
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie },
|
||||
{ },
|
||||
};
|
||||
|
||||
@ -2201,21 +2204,16 @@ remove:
|
||||
|
||||
static int tegra_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct tegra_pcie *pcie;
|
||||
int err;
|
||||
|
||||
match = of_match_device(tegra_pcie_of_match, &pdev->dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->soc = of_device_get_match_data(&pdev->dev);
|
||||
INIT_LIST_HEAD(&pcie->buses);
|
||||
INIT_LIST_HEAD(&pcie->ports);
|
||||
pcie->soc_data = match->data;
|
||||
pcie->dev = &pdev->dev;
|
||||
|
||||
err = tegra_pcie_parse_dt(pcie);
|
||||
|
@ -74,7 +74,7 @@ static int versatile_pci_parse_request_of_pci_ranges(struct device *dev,
|
||||
int err, mem = 1, res_valid = 0;
|
||||
struct device_node *np = dev->of_node;
|
||||
resource_size_t iobase;
|
||||
struct resource_entry *win;
|
||||
struct resource_entry *win, *tmp;
|
||||
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, res, &iobase);
|
||||
if (err)
|
||||
@ -84,15 +84,17 @@ static int versatile_pci_parse_request_of_pci_ranges(struct device *dev,
|
||||
if (err)
|
||||
goto out_release_res;
|
||||
|
||||
resource_list_for_each_entry(win, res) {
|
||||
resource_list_for_each_entry_safe(win, tmp, res) {
|
||||
struct resource *res = win->res;
|
||||
|
||||
switch (resource_type(res)) {
|
||||
case IORESOURCE_IO:
|
||||
err = pci_remap_iospace(res, iobase);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_warn(dev, "error %d: failed to map resource %pR\n",
|
||||
err, res);
|
||||
resource_list_destroy_entry(win);
|
||||
}
|
||||
break;
|
||||
case IORESOURCE_MEM:
|
||||
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
||||
|
@ -1,4 +1,8 @@
|
||||
/*
|
||||
* Altera PCIe MSI support
|
||||
*
|
||||
* Author: Ley Foon Tan <lftan@altera.com>
|
||||
*
|
||||
* Copyright Altera Corporation (C) 2013-2015. All rights reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@ -16,7 +20,7 @@
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
@ -237,11 +241,6 @@ static int altera_msi_probe(struct platform_device *pdev)
|
||||
msi->pdev = pdev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no csr memory resource defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
msi->csr_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(msi->csr_base)) {
|
||||
dev_err(&pdev->dev, "failed to map csr memory\n");
|
||||
@ -250,11 +249,6 @@ static int altera_msi_probe(struct platform_device *pdev)
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"vector_slave");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no vector_slave memory resource defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(msi->vector_base)) {
|
||||
dev_err(&pdev->dev, "failed to map vector_slave memory\n");
|
||||
@ -308,7 +302,3 @@ static int __init altera_msi_init(void)
|
||||
return platform_driver_register(&altera_msi_driver);
|
||||
}
|
||||
subsys_initcall(altera_msi_init);
|
||||
|
||||
MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
|
||||
MODULE_DESCRIPTION("Altera PCIe MSI support");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -1,6 +1,9 @@
|
||||
/*
|
||||
* Copyright Altera Corporation (C) 2013-2015. All rights reserved
|
||||
*
|
||||
* Author: Ley Foon Tan <lftan@altera.com>
|
||||
* Description: Altera PCIe host controller driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
@ -17,7 +20,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
@ -43,6 +46,7 @@
|
||||
#define RP_LTSSM_MASK 0x1f
|
||||
#define LTSSM_L0 0xf
|
||||
|
||||
#define PCIE_CAP_OFFSET 0x80
|
||||
/* TLP configuration type 0 and 1 */
|
||||
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
|
||||
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
|
||||
@ -61,7 +65,8 @@
|
||||
#define TLP_LOOP 500
|
||||
#define RP_DEVFN 0
|
||||
|
||||
#define LINK_UP_TIMEOUT 5000
|
||||
#define LINK_UP_TIMEOUT HZ
|
||||
#define LINK_RETRAIN_TIMEOUT HZ
|
||||
|
||||
#define INTX_NUM 4
|
||||
|
||||
@ -99,38 +104,6 @@ static bool altera_pcie_link_is_up(struct altera_pcie *pcie)
|
||||
return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0);
|
||||
}
|
||||
|
||||
static void altera_pcie_retrain(struct pci_dev *dev)
|
||||
{
|
||||
u16 linkcap, linkstat;
|
||||
struct altera_pcie *pcie = dev->bus->sysdata;
|
||||
int timeout = 0;
|
||||
|
||||
if (!altera_pcie_link_is_up(pcie))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
|
||||
* current speed is 2.5 GB/s.
|
||||
*/
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKCAP, &linkcap);
|
||||
|
||||
if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
|
||||
return;
|
||||
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat);
|
||||
if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
|
||||
pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
|
||||
PCI_EXP_LNKCTL_RL);
|
||||
while (!altera_pcie_link_is_up(pcie)) {
|
||||
timeout++;
|
||||
if (timeout > LINK_UP_TIMEOUT)
|
||||
break;
|
||||
udelay(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain);
|
||||
|
||||
/*
|
||||
* Altera PCIe port uses BAR0 of RC's configuration space as the translation
|
||||
* from PCI bus to native BUS. Entire DDR region is mapped into PCIe space
|
||||
@ -171,13 +144,6 @@ static bool altera_pcie_valid_config(struct altera_pcie *pcie,
|
||||
if (bus->number == pcie->root_bus_nr && dev > 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Do not read more than one device on the bus directly attached
|
||||
* to root port, root port can only attach to one downstream port.
|
||||
*/
|
||||
if (bus->primary == pcie->root_bus_nr && dev > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -301,22 +267,14 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *value)
|
||||
static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno,
|
||||
unsigned int devfn, int where, int size,
|
||||
u32 *value)
|
||||
{
|
||||
struct altera_pcie *pcie = bus->sysdata;
|
||||
int ret;
|
||||
u32 data;
|
||||
u8 byte_en;
|
||||
|
||||
if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
|
||||
*value = 0xffffffff;
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
byte_en = 1 << (where & 3);
|
||||
@ -329,7 +287,7 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
||||
break;
|
||||
}
|
||||
|
||||
ret = tlp_cfg_dword_read(pcie, bus->number, devfn,
|
||||
ret = tlp_cfg_dword_read(pcie, busno, devfn,
|
||||
(where & ~DWORD_MASK), byte_en, &data);
|
||||
if (ret != PCIBIOS_SUCCESSFUL)
|
||||
return ret;
|
||||
@ -349,20 +307,14 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 value)
|
||||
static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno,
|
||||
unsigned int devfn, int where, int size,
|
||||
u32 value)
|
||||
{
|
||||
struct altera_pcie *pcie = bus->sysdata;
|
||||
u32 data32;
|
||||
u32 shift = 8 * (where & 3);
|
||||
u8 byte_en;
|
||||
|
||||
if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
data32 = (value & 0xff) << shift;
|
||||
@ -378,8 +330,40 @@ static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
|
||||
break;
|
||||
}
|
||||
|
||||
return tlp_cfg_dword_write(pcie, bus->number, devfn,
|
||||
(where & ~DWORD_MASK), byte_en, data32);
|
||||
return tlp_cfg_dword_write(pcie, busno, devfn, (where & ~DWORD_MASK),
|
||||
byte_en, data32);
|
||||
}
|
||||
|
||||
static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *value)
|
||||
{
|
||||
struct altera_pcie *pcie = bus->sysdata;
|
||||
|
||||
if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
|
||||
*value = 0xffffffff;
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
return _altera_pcie_cfg_read(pcie, bus->number, devfn, where, size,
|
||||
value);
|
||||
}
|
||||
|
||||
static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 value)
|
||||
{
|
||||
struct altera_pcie *pcie = bus->sysdata;
|
||||
|
||||
if (altera_pcie_hide_rc_bar(bus, devfn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size,
|
||||
value);
|
||||
}
|
||||
|
||||
static struct pci_ops altera_pcie_ops = {
|
||||
@ -387,6 +371,90 @@ static struct pci_ops altera_pcie_ops = {
|
||||
.write = altera_pcie_cfg_write,
|
||||
};
|
||||
|
||||
static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno,
|
||||
unsigned int devfn, int offset, u16 *value)
|
||||
{
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
ret = _altera_pcie_cfg_read(pcie, busno, devfn,
|
||||
PCIE_CAP_OFFSET + offset, sizeof(*value),
|
||||
&data);
|
||||
*value = data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno,
|
||||
unsigned int devfn, int offset, u16 value)
|
||||
{
|
||||
return _altera_pcie_cfg_write(pcie, busno, devfn,
|
||||
PCIE_CAP_OFFSET + offset, sizeof(value),
|
||||
value);
|
||||
}
|
||||
|
||||
static void altera_wait_link_retrain(struct altera_pcie *pcie)
|
||||
{
|
||||
u16 reg16;
|
||||
unsigned long start_jiffies;
|
||||
|
||||
/* Wait for link training end. */
|
||||
start_jiffies = jiffies;
|
||||
for (;;) {
|
||||
altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
|
||||
PCI_EXP_LNKSTA, ®16);
|
||||
if (!(reg16 & PCI_EXP_LNKSTA_LT))
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) {
|
||||
dev_err(&pcie->pdev->dev, "link retrain timeout\n");
|
||||
break;
|
||||
}
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
/* Wait for link is up */
|
||||
start_jiffies = jiffies;
|
||||
for (;;) {
|
||||
if (altera_pcie_link_is_up(pcie))
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) {
|
||||
dev_err(&pcie->pdev->dev, "link up timeout\n");
|
||||
break;
|
||||
}
|
||||
udelay(100);
|
||||
}
|
||||
}
|
||||
|
||||
static void altera_pcie_retrain(struct altera_pcie *pcie)
|
||||
{
|
||||
u16 linkcap, linkstat, linkctl;
|
||||
|
||||
if (!altera_pcie_link_is_up(pcie))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
|
||||
* current speed is 2.5 GB/s.
|
||||
*/
|
||||
altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKCAP,
|
||||
&linkcap);
|
||||
if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
|
||||
return;
|
||||
|
||||
altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKSTA,
|
||||
&linkstat);
|
||||
if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
|
||||
altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
|
||||
PCI_EXP_LNKCTL, &linkctl);
|
||||
linkctl |= PCI_EXP_LNKCTL_RL;
|
||||
altera_write_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
|
||||
PCI_EXP_LNKCTL, linkctl);
|
||||
|
||||
altera_wait_link_retrain(pcie);
|
||||
}
|
||||
}
|
||||
|
||||
static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
@ -508,6 +576,11 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void altera_pcie_host_init(struct altera_pcie *pcie)
|
||||
{
|
||||
altera_pcie_retrain(pcie);
|
||||
}
|
||||
|
||||
static int altera_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct altera_pcie *pcie;
|
||||
@ -545,6 +618,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
|
||||
cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
|
||||
/* enable all interrupts */
|
||||
cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
|
||||
altera_pcie_host_init(pcie);
|
||||
|
||||
bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops,
|
||||
pcie, &pcie->resources);
|
||||
@ -568,7 +642,6 @@ static const struct of_device_id altera_pcie_of_match[] = {
|
||||
{ .compatible = "altr,pcie-root-port-1.0", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, altera_pcie_of_match);
|
||||
|
||||
static struct platform_driver altera_pcie_driver = {
|
||||
.probe = altera_pcie_probe,
|
||||
@ -583,8 +656,4 @@ static int altera_pcie_init(void)
|
||||
{
|
||||
return platform_driver_register(&altera_pcie_driver);
|
||||
}
|
||||
module_init(altera_pcie_init);
|
||||
|
||||
MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
|
||||
MODULE_DESCRIPTION("Altera PCIe host controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
device_initcall(altera_pcie_init);
|
||||
|
@ -191,8 +191,8 @@ static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg)
|
||||
return dw_handle_msi_irq(pp);
|
||||
}
|
||||
|
||||
static int __init artpec6_add_pcie_port(struct pcie_port *pp,
|
||||
struct platform_device *pdev)
|
||||
static int artpec6_add_pcie_port(struct pcie_port *pp,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -100,9 +100,6 @@ static int dw_plat_pcie_probe(struct platform_device *pdev)
|
||||
pp->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(dw_plat_pcie->mem_base))
|
||||
return PTR_ERR(dw_plat_pcie->mem_base);
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
@ -26,7 +25,17 @@
|
||||
|
||||
#include "pcie-designware.h"
|
||||
|
||||
/* Synopsis specific PCIE configuration registers */
|
||||
/* Parameters for the waiting for link up routine */
|
||||
#define LINK_WAIT_MAX_RETRIES 10
|
||||
#define LINK_WAIT_USLEEP_MIN 90000
|
||||
#define LINK_WAIT_USLEEP_MAX 100000
|
||||
|
||||
/* Parameters for the waiting for iATU enabled routine */
|
||||
#define LINK_WAIT_MAX_IATU_RETRIES 5
|
||||
#define LINK_WAIT_IATU_MIN 9000
|
||||
#define LINK_WAIT_IATU_MAX 10000
|
||||
|
||||
/* Synopsys-specific PCIe configuration registers */
|
||||
#define PCIE_PORT_LINK_CONTROL 0x710
|
||||
#define PORT_LINK_MODE_MASK (0x3f << 16)
|
||||
#define PORT_LINK_MODE_1_LANES (0x1 << 16)
|
||||
@ -51,6 +60,7 @@
|
||||
#define PCIE_ATU_VIEWPORT 0x900
|
||||
#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
|
||||
#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
|
||||
#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
|
||||
#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
|
||||
#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
|
||||
#define PCIE_ATU_CR1 0x904
|
||||
@ -70,10 +80,26 @@
|
||||
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
/*
|
||||
* iATU Unroll-specific register definitions
|
||||
* From 4.80 core version the address translation will be made by unroll
|
||||
*/
|
||||
#define PCIE_ATU_UNR_REGION_CTRL1 0x00
|
||||
#define PCIE_ATU_UNR_REGION_CTRL2 0x04
|
||||
#define PCIE_ATU_UNR_LOWER_BASE 0x08
|
||||
#define PCIE_ATU_UNR_UPPER_BASE 0x0C
|
||||
#define PCIE_ATU_UNR_LIMIT 0x10
|
||||
#define PCIE_ATU_UNR_LOWER_TARGET 0x14
|
||||
#define PCIE_ATU_UNR_UPPER_TARGET 0x18
|
||||
|
||||
/* Register address builder */
|
||||
#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((0x3 << 20) | (region << 9))
|
||||
|
||||
/* PCIe Port Logic registers */
|
||||
#define PLR_OFFSET 0x700
|
||||
#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
|
||||
#define PCIE_PHY_DEBUG_R1_LINK_UP 0x00000010
|
||||
#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4)
|
||||
#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29)
|
||||
|
||||
static struct pci_ops dw_pcie_ops;
|
||||
|
||||
@ -115,12 +141,12 @@ int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static inline void dw_pcie_readl_rc(struct pcie_port *pp, u32 reg, u32 *val)
|
||||
static inline u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
|
||||
{
|
||||
if (pp->ops->readl_rc)
|
||||
pp->ops->readl_rc(pp, pp->dbi_base + reg, val);
|
||||
else
|
||||
*val = readl(pp->dbi_base + reg);
|
||||
return pp->ops->readl_rc(pp, pp->dbi_base + reg);
|
||||
|
||||
return readl(pp->dbi_base + reg);
|
||||
}
|
||||
|
||||
static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
|
||||
@ -131,6 +157,27 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
|
||||
writel(val, pp->dbi_base + reg);
|
||||
}
|
||||
|
||||
static inline u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
|
||||
{
|
||||
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
|
||||
|
||||
if (pp->ops->readl_rc)
|
||||
return pp->ops->readl_rc(pp, pp->dbi_base + offset + reg);
|
||||
|
||||
return readl(pp->dbi_base + offset + reg);
|
||||
}
|
||||
|
||||
static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
|
||||
|
||||
if (pp->ops->writel_rc)
|
||||
pp->ops->writel_rc(pp, val, pp->dbi_base + offset + reg);
|
||||
else
|
||||
writel(val, pp->dbi_base + offset + reg);
|
||||
}
|
||||
|
||||
static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
|
||||
u32 *val)
|
||||
{
|
||||
@ -152,24 +199,57 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
|
||||
static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
|
||||
int type, u64 cpu_addr, u64 pci_addr, u32 size)
|
||||
{
|
||||
u32 val;
|
||||
u32 retries, val;
|
||||
|
||||
dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
|
||||
PCIE_ATU_VIEWPORT);
|
||||
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE);
|
||||
dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE);
|
||||
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
|
||||
PCIE_ATU_LIMIT);
|
||||
dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET);
|
||||
dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
|
||||
dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
|
||||
if (pp->iatu_unroll_enabled) {
|
||||
dw_pcie_writel_unroll(pp, index,
|
||||
lower_32_bits(cpu_addr), PCIE_ATU_UNR_LOWER_BASE);
|
||||
dw_pcie_writel_unroll(pp, index,
|
||||
upper_32_bits(cpu_addr), PCIE_ATU_UNR_UPPER_BASE);
|
||||
dw_pcie_writel_unroll(pp, index,
|
||||
lower_32_bits(cpu_addr + size - 1), PCIE_ATU_UNR_LIMIT);
|
||||
dw_pcie_writel_unroll(pp, index,
|
||||
lower_32_bits(pci_addr), PCIE_ATU_UNR_LOWER_TARGET);
|
||||
dw_pcie_writel_unroll(pp, index,
|
||||
upper_32_bits(pci_addr), PCIE_ATU_UNR_UPPER_TARGET);
|
||||
dw_pcie_writel_unroll(pp, index,
|
||||
type, PCIE_ATU_UNR_REGION_CTRL1);
|
||||
dw_pcie_writel_unroll(pp, index,
|
||||
PCIE_ATU_ENABLE, PCIE_ATU_UNR_REGION_CTRL2);
|
||||
} else {
|
||||
dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
|
||||
PCIE_ATU_VIEWPORT);
|
||||
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr),
|
||||
PCIE_ATU_LOWER_BASE);
|
||||
dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr),
|
||||
PCIE_ATU_UPPER_BASE);
|
||||
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
|
||||
PCIE_ATU_LIMIT);
|
||||
dw_pcie_writel_rc(pp, lower_32_bits(pci_addr),
|
||||
PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_rc(pp, upper_32_bits(pci_addr),
|
||||
PCIE_ATU_UPPER_TARGET);
|
||||
dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
|
||||
dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure ATU enable takes effect before any subsequent config
|
||||
* and I/O accesses.
|
||||
*/
|
||||
dw_pcie_readl_rc(pp, PCIE_ATU_CR2, &val);
|
||||
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
|
||||
if (pp->iatu_unroll_enabled)
|
||||
val = dw_pcie_readl_unroll(pp, index,
|
||||
PCIE_ATU_UNR_REGION_CTRL2);
|
||||
else
|
||||
val = dw_pcie_readl_rc(pp, PCIE_ATU_CR2);
|
||||
|
||||
if (val == PCIE_ATU_ENABLE)
|
||||
return;
|
||||
|
||||
usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
|
||||
}
|
||||
dev_err(pp->dev, "iATU is not being enabled\n");
|
||||
}
|
||||
|
||||
static struct irq_chip dw_msi_irq_chip = {
|
||||
@ -412,7 +492,8 @@ int dw_pcie_link_up(struct pcie_port *pp)
|
||||
return pp->ops->link_up(pp);
|
||||
|
||||
val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
|
||||
return val & PCIE_PHY_DEBUG_R1_LINK_UP;
|
||||
return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) &&
|
||||
(!(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING)));
|
||||
}
|
||||
|
||||
static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
|
||||
@ -428,6 +509,17 @@ static const struct irq_domain_ops msi_domain_ops = {
|
||||
.map = dw_pcie_msi_map,
|
||||
};
|
||||
|
||||
static u8 dw_pcie_iatu_unroll_enabled(struct pcie_port *pp)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT);
|
||||
if (val == 0xffffffff)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dw_pcie_host_init(struct pcie_port *pp)
|
||||
{
|
||||
struct device_node *np = pp->dev->of_node;
|
||||
@ -436,7 +528,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
||||
struct resource *cfg_res;
|
||||
int i, ret;
|
||||
LIST_HEAD(res);
|
||||
struct resource_entry *win;
|
||||
struct resource_entry *win, *tmp;
|
||||
|
||||
cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
|
||||
if (cfg_res) {
|
||||
@ -457,17 +549,20 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
||||
goto error;
|
||||
|
||||
/* Get the I/O and memory ranges from DT */
|
||||
resource_list_for_each_entry(win, &res) {
|
||||
resource_list_for_each_entry_safe(win, tmp, &res) {
|
||||
switch (resource_type(win->res)) {
|
||||
case IORESOURCE_IO:
|
||||
pp->io = win->res;
|
||||
pp->io->name = "I/O";
|
||||
pp->io_size = resource_size(pp->io);
|
||||
pp->io_bus_addr = pp->io->start - win->offset;
|
||||
ret = pci_remap_iospace(pp->io, pp->io_base);
|
||||
if (ret)
|
||||
ret = pci_remap_iospace(win->res, pp->io_base);
|
||||
if (ret) {
|
||||
dev_warn(pp->dev, "error %d: failed to map resource %pR\n",
|
||||
ret, pp->io);
|
||||
ret, win->res);
|
||||
resource_list_destroy_entry(win);
|
||||
} else {
|
||||
pp->io = win->res;
|
||||
pp->io->name = "I/O";
|
||||
pp->io_size = resource_size(pp->io);
|
||||
pp->io_bus_addr = pp->io->start - win->offset;
|
||||
}
|
||||
break;
|
||||
case IORESOURCE_MEM:
|
||||
pp->mem = win->res;
|
||||
@ -524,6 +619,10 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
||||
if (ret)
|
||||
pp->lanes = 0;
|
||||
|
||||
ret = of_property_read_u32(np, "num-viewport", &pp->num_viewport);
|
||||
if (ret)
|
||||
pp->num_viewport = 2;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
if (!pp->ops->msi_host_init) {
|
||||
pp->irq_domain = irq_domain_add_linear(pp->dev->of_node,
|
||||
@ -544,6 +643,8 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
||||
}
|
||||
}
|
||||
|
||||
pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp);
|
||||
|
||||
if (pp->ops->host_init)
|
||||
pp->ops->host_init(pp);
|
||||
|
||||
@ -609,13 +710,14 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
va_cfg_base = pp->va_cfg1_base;
|
||||
}
|
||||
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
|
||||
type, cpu_addr,
|
||||
busdev, cfg_size);
|
||||
ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
pp->io_bus_addr, pp->io_size);
|
||||
if (pp->num_viewport <= 2)
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
pp->io_bus_addr, pp->io_size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -646,13 +748,14 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
va_cfg_base = pp->va_cfg1_base;
|
||||
}
|
||||
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
|
||||
type, cpu_addr,
|
||||
busdev, cfg_size);
|
||||
ret = dw_pcie_cfg_write(va_cfg_base + where, size, val);
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
pp->io_bus_addr, pp->io_size);
|
||||
if (pp->num_viewport <= 2)
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
pp->io_bus_addr, pp->io_size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -670,13 +773,6 @@ static int dw_pcie_valid_config(struct pcie_port *pp,
|
||||
if (bus->number == pp->root_bus_nr && dev > 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* do not read more than one device on the bus directly attached
|
||||
* to RC's (Virtual Bridge's) DS side.
|
||||
*/
|
||||
if (bus->primary == pp->root_bus_nr && dev > 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -720,7 +816,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
u32 val;
|
||||
|
||||
/* set the number of lanes */
|
||||
dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val);
|
||||
val = dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL);
|
||||
val &= ~PORT_LINK_MODE_MASK;
|
||||
switch (pp->lanes) {
|
||||
case 1:
|
||||
@ -742,7 +838,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
|
||||
|
||||
/* set link width speed control register */
|
||||
dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, &val);
|
||||
val = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
|
||||
switch (pp->lanes) {
|
||||
case 1:
|
||||
@ -765,19 +861,19 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1);
|
||||
|
||||
/* setup interrupt pins */
|
||||
dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE, &val);
|
||||
val = dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE);
|
||||
val &= 0xffff00ff;
|
||||
val |= 0x00000100;
|
||||
dw_pcie_writel_rc(pp, val, PCI_INTERRUPT_LINE);
|
||||
|
||||
/* setup bus numbers */
|
||||
dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val);
|
||||
val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
|
||||
val &= 0xff000000;
|
||||
val |= 0x00010100;
|
||||
dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
|
||||
|
||||
/* setup command register */
|
||||
dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
|
||||
val = dw_pcie_readl_rc(pp, PCI_COMMAND);
|
||||
val &= 0xffff0000;
|
||||
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
|
||||
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
|
||||
@ -788,10 +884,15 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
* uses its own address translation component rather than ATU, so
|
||||
* we should not program the ATU here.
|
||||
*/
|
||||
if (!pp->ops->rd_other_conf)
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
|
||||
if (!pp->ops->rd_other_conf) {
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
|
||||
PCIE_ATU_TYPE_MEM, pp->mem_base,
|
||||
pp->mem_bus_addr, pp->mem_size);
|
||||
if (pp->num_viewport > 2)
|
||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX2,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
pp->io_bus_addr, pp->io_size);
|
||||
}
|
||||
|
||||
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
|
||||
|
||||
@ -802,7 +903,3 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
val |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
|
||||
MODULE_DESCRIPTION("Designware PCIe host controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -22,11 +22,6 @@
|
||||
#define MAX_MSI_IRQS 32
|
||||
#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32)
|
||||
|
||||
/* Parameters for the waiting for link up routine */
|
||||
#define LINK_WAIT_MAX_RETRIES 10
|
||||
#define LINK_WAIT_USLEEP_MIN 90000
|
||||
#define LINK_WAIT_USLEEP_MAX 100000
|
||||
|
||||
struct pcie_port {
|
||||
struct device *dev;
|
||||
u8 root_bus_nr;
|
||||
@ -49,16 +44,17 @@ struct pcie_port {
|
||||
struct resource *busn;
|
||||
int irq;
|
||||
u32 lanes;
|
||||
u32 num_viewport;
|
||||
struct pcie_host_ops *ops;
|
||||
int msi_irq;
|
||||
struct irq_domain *irq_domain;
|
||||
unsigned long msi_data;
|
||||
u8 iatu_unroll_enabled;
|
||||
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
|
||||
};
|
||||
|
||||
struct pcie_host_ops {
|
||||
void (*readl_rc)(struct pcie_port *pp,
|
||||
void __iomem *dbi_base, u32 *val);
|
||||
u32 (*readl_rc)(struct pcie_port *pp, void __iomem *dbi_base);
|
||||
void (*writel_rc)(struct pcie_port *pp,
|
||||
u32 val, void __iomem *dbi_base);
|
||||
int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
|
||||
|
@ -1,7 +1,11 @@
|
||||
/*
|
||||
* Qualcomm PCIe root complex driver
|
||||
*
|
||||
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||
* Copyright 2015 Linaro Limited.
|
||||
*
|
||||
* Author: Stanimir Varbanov <svarbanov@mm-sol.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
@ -19,7 +23,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pci.h>
|
||||
@ -570,37 +574,19 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_pcie *pcie = platform_get_drvdata(pdev);
|
||||
|
||||
qcom_ep_reset_assert(pcie);
|
||||
phy_power_off(pcie->phy);
|
||||
phy_exit(pcie->phy);
|
||||
pcie->ops->deinit(pcie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_pcie_match[] = {
|
||||
{ .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 },
|
||||
{ .compatible = "qcom,pcie-apq8064", .data = &ops_v0 },
|
||||
{ .compatible = "qcom,pcie-apq8084", .data = &ops_v1 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_pcie_match);
|
||||
|
||||
static struct platform_driver qcom_pcie_driver = {
|
||||
.probe = qcom_pcie_probe,
|
||||
.remove = qcom_pcie_remove,
|
||||
.driver = {
|
||||
.name = "qcom-pcie",
|
||||
.suppress_bind_attrs = true,
|
||||
.of_match_table = qcom_pcie_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(qcom_pcie_driver);
|
||||
|
||||
MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
|
||||
MODULE_DESCRIPTION("Qualcomm PCIe root complex driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
builtin_platform_driver(qcom_pcie_driver);
|
||||
|
@ -84,8 +84,18 @@
|
||||
#define IDSETR1 0x011004
|
||||
#define TLCTLR 0x011048
|
||||
#define MACSR 0x011054
|
||||
#define SPCHGFIN (1 << 4)
|
||||
#define SPCHGFAIL (1 << 6)
|
||||
#define SPCHGSUC (1 << 7)
|
||||
#define LINK_SPEED (0xf << 16)
|
||||
#define LINK_SPEED_2_5GTS (1 << 16)
|
||||
#define LINK_SPEED_5_0GTS (2 << 16)
|
||||
#define MACCTLR 0x011058
|
||||
#define SPEED_CHANGE (1 << 24)
|
||||
#define SCRAMBLE_DISABLE (1 << 27)
|
||||
#define MACS2R 0x011078
|
||||
#define MACCGSPSETR 0x011084
|
||||
#define SPCNGRSN (1 << 31)
|
||||
|
||||
/* R-Car H1 PHY */
|
||||
#define H1_PCIEPHYADRR 0x04000c
|
||||
@ -385,11 +395,67 @@ static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pci)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
|
||||
{
|
||||
unsigned int timeout = 1000;
|
||||
u32 macsr;
|
||||
|
||||
if ((rcar_pci_read_reg(pcie, MACS2R) & LINK_SPEED) != LINK_SPEED_5_0GTS)
|
||||
return;
|
||||
|
||||
if (rcar_pci_read_reg(pcie, MACCTLR) & SPEED_CHANGE) {
|
||||
dev_err(pcie->dev, "Speed change already in progress\n");
|
||||
return;
|
||||
}
|
||||
|
||||
macsr = rcar_pci_read_reg(pcie, MACSR);
|
||||
if ((macsr & LINK_SPEED) == LINK_SPEED_5_0GTS)
|
||||
goto done;
|
||||
|
||||
/* Set target link speed to 5.0 GT/s */
|
||||
rcar_rmw32(pcie, EXPCAP(12), PCI_EXP_LNKSTA_CLS,
|
||||
PCI_EXP_LNKSTA_CLS_5_0GB);
|
||||
|
||||
/* Set speed change reason as intentional factor */
|
||||
rcar_rmw32(pcie, MACCGSPSETR, SPCNGRSN, 0);
|
||||
|
||||
/* Clear SPCHGFIN, SPCHGSUC, and SPCHGFAIL */
|
||||
if (macsr & (SPCHGFIN | SPCHGSUC | SPCHGFAIL))
|
||||
rcar_pci_write_reg(pcie, macsr, MACSR);
|
||||
|
||||
/* Start link speed change */
|
||||
rcar_rmw32(pcie, MACCTLR, SPEED_CHANGE, SPEED_CHANGE);
|
||||
|
||||
while (timeout--) {
|
||||
macsr = rcar_pci_read_reg(pcie, MACSR);
|
||||
if (macsr & SPCHGFIN) {
|
||||
/* Clear the interrupt bits */
|
||||
rcar_pci_write_reg(pcie, macsr, MACSR);
|
||||
|
||||
if (macsr & SPCHGFAIL)
|
||||
dev_err(pcie->dev, "Speed change failed\n");
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
msleep(1);
|
||||
};
|
||||
|
||||
dev_err(pcie->dev, "Speed change timed out\n");
|
||||
|
||||
done:
|
||||
dev_info(pcie->dev, "Current link speed is %s GT/s\n",
|
||||
(macsr & LINK_SPEED) == LINK_SPEED_5_0GTS ? "5" : "2.5");
|
||||
}
|
||||
|
||||
static int rcar_pcie_enable(struct rcar_pcie *pcie)
|
||||
{
|
||||
struct pci_bus *bus, *child;
|
||||
LIST_HEAD(res);
|
||||
|
||||
/* Try setting 5 GT/s link speed */
|
||||
rcar_pcie_force_speedup(pcie);
|
||||
|
||||
rcar_pcie_setup(&res, pcie);
|
||||
|
||||
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
||||
@ -608,6 +674,18 @@ static int rcar_msi_alloc(struct rcar_msi *chip)
|
||||
return msi;
|
||||
}
|
||||
|
||||
static int rcar_msi_alloc_region(struct rcar_msi *chip, int no_irqs)
|
||||
{
|
||||
int msi;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
msi = bitmap_find_free_region(chip->used, INT_PCI_MSI_NR,
|
||||
order_base_2(no_irqs));
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return msi;
|
||||
}
|
||||
|
||||
static void rcar_msi_free(struct rcar_msi *chip, unsigned long irq)
|
||||
{
|
||||
mutex_lock(&chip->lock);
|
||||
@ -665,7 +743,7 @@ static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
|
||||
if (hwirq < 0)
|
||||
return hwirq;
|
||||
|
||||
irq = irq_create_mapping(msi->domain, hwirq);
|
||||
irq = irq_find_mapping(msi->domain, hwirq);
|
||||
if (!irq) {
|
||||
rcar_msi_free(msi, hwirq);
|
||||
return -EINVAL;
|
||||
@ -682,6 +760,58 @@ static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_msi_setup_irqs(struct msi_controller *chip,
|
||||
struct pci_dev *pdev, int nvec, int type)
|
||||
{
|
||||
struct rcar_pcie *pcie = container_of(chip, struct rcar_pcie, msi.chip);
|
||||
struct rcar_msi *msi = to_rcar_msi(chip);
|
||||
struct msi_desc *desc;
|
||||
struct msi_msg msg;
|
||||
unsigned int irq;
|
||||
int hwirq;
|
||||
int i;
|
||||
|
||||
/* MSI-X interrupts are not supported */
|
||||
if (type == PCI_CAP_ID_MSIX)
|
||||
return -EINVAL;
|
||||
|
||||
WARN_ON(!list_is_singular(&pdev->dev.msi_list));
|
||||
desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list);
|
||||
|
||||
hwirq = rcar_msi_alloc_region(msi, nvec);
|
||||
if (hwirq < 0)
|
||||
return -ENOSPC;
|
||||
|
||||
irq = irq_find_mapping(msi->domain, hwirq);
|
||||
if (!irq)
|
||||
return -ENOSPC;
|
||||
|
||||
for (i = 0; i < nvec; i++) {
|
||||
/*
|
||||
* irq_create_mapping() called from rcar_pcie_probe() pre-
|
||||
* allocates descs, so there is no need to allocate descs here.
|
||||
* We can therefore assume that if irq_find_mapping() above
|
||||
* returns non-zero, then the descs are also successfully
|
||||
* allocated.
|
||||
*/
|
||||
if (irq_set_msi_desc_off(irq, i, desc)) {
|
||||
/* TODO: clear */
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
desc->nvec_used = nvec;
|
||||
desc->msi_attrib.multiple = order_base_2(nvec);
|
||||
|
||||
msg.address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE;
|
||||
msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR);
|
||||
msg.data = hwirq;
|
||||
|
||||
pci_write_msi_msg(irq, &msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
|
||||
{
|
||||
struct rcar_msi *msi = to_rcar_msi(chip);
|
||||
@ -716,12 +846,13 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
|
||||
struct platform_device *pdev = to_platform_device(pcie->dev);
|
||||
struct rcar_msi *msi = &pcie->msi;
|
||||
unsigned long base;
|
||||
int err;
|
||||
int err, i;
|
||||
|
||||
mutex_init(&msi->lock);
|
||||
|
||||
msi->chip.dev = pcie->dev;
|
||||
msi->chip.setup_irq = rcar_msi_setup_irq;
|
||||
msi->chip.setup_irqs = rcar_msi_setup_irqs;
|
||||
msi->chip.teardown_irq = rcar_msi_teardown_irq;
|
||||
|
||||
msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
|
||||
@ -731,6 +862,9 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < INT_PCI_MSI_NR; i++)
|
||||
irq_create_mapping(msi->domain, i);
|
||||
|
||||
/* Two irqs are for MSI, but they are also used for non-MSI irqs */
|
||||
err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq,
|
||||
IRQF_SHARED | IRQF_NO_THREAD,
|
||||
@ -775,6 +909,10 @@ static int rcar_pcie_get_resources(struct platform_device *pdev,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pcie->base = devm_ioremap_resource(&pdev->dev, &res);
|
||||
if (IS_ERR(pcie->base))
|
||||
return PTR_ERR(pcie->base);
|
||||
|
||||
pcie->clk = devm_clk_get(&pdev->dev, "pcie");
|
||||
if (IS_ERR(pcie->clk)) {
|
||||
dev_err(pcie->dev, "cannot get platform clock\n");
|
||||
@ -782,7 +920,7 @@ static int rcar_pcie_get_resources(struct platform_device *pdev,
|
||||
}
|
||||
err = clk_prepare_enable(pcie->clk);
|
||||
if (err)
|
||||
goto fail_clk;
|
||||
return err;
|
||||
|
||||
pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
|
||||
if (IS_ERR(pcie->bus_clk)) {
|
||||
@ -792,7 +930,7 @@ static int rcar_pcie_get_resources(struct platform_device *pdev,
|
||||
}
|
||||
err = clk_prepare_enable(pcie->bus_clk);
|
||||
if (err)
|
||||
goto err_map_reg;
|
||||
goto fail_clk;
|
||||
|
||||
i = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (!i) {
|
||||
@ -810,12 +948,6 @@ static int rcar_pcie_get_resources(struct platform_device *pdev,
|
||||
}
|
||||
pcie->msi.irq2 = i;
|
||||
|
||||
pcie->base = devm_ioremap_resource(&pdev->dev, &res);
|
||||
if (IS_ERR(pcie->base)) {
|
||||
err = PTR_ERR(pcie->base);
|
||||
goto err_map_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_map_reg:
|
||||
@ -865,12 +997,16 @@ static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie,
|
||||
* Set up 64-bit inbound regions as the range parser doesn't
|
||||
* distinguish between 32 and 64-bit types.
|
||||
*/
|
||||
rcar_pci_write_reg(pcie, lower_32_bits(pci_addr), PCIEPRAR(idx));
|
||||
rcar_pci_write_reg(pcie, lower_32_bits(pci_addr),
|
||||
PCIEPRAR(idx));
|
||||
rcar_pci_write_reg(pcie, lower_32_bits(cpu_addr), PCIELAR(idx));
|
||||
rcar_pci_write_reg(pcie, lower_32_bits(mask) | flags, PCIELAMR(idx));
|
||||
rcar_pci_write_reg(pcie, lower_32_bits(mask) | flags,
|
||||
PCIELAMR(idx));
|
||||
|
||||
rcar_pci_write_reg(pcie, upper_32_bits(pci_addr), PCIEPRAR(idx+1));
|
||||
rcar_pci_write_reg(pcie, upper_32_bits(cpu_addr), PCIELAR(idx+1));
|
||||
rcar_pci_write_reg(pcie, upper_32_bits(pci_addr),
|
||||
PCIEPRAR(idx + 1));
|
||||
rcar_pci_write_reg(pcie, upper_32_bits(cpu_addr),
|
||||
PCIELAR(idx + 1));
|
||||
rcar_pci_write_reg(pcie, 0, PCIELAMR(idx + 1));
|
||||
|
||||
pci_addr += size;
|
||||
@ -919,6 +1055,7 @@ static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie,
|
||||
/* Get the dma-ranges from DT */
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
u64 end = range.cpu_addr + range.size - 1;
|
||||
|
||||
dev_dbg(pcie->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
|
||||
range.flags, range.cpu_addr, end, range.pci_addr);
|
||||
|
||||
@ -932,9 +1069,12 @@ static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie,
|
||||
|
||||
static const struct of_device_id rcar_pcie_of_match[] = {
|
||||
{ .compatible = "renesas,pcie-r8a7779", .data = rcar_pcie_hw_init_h1 },
|
||||
{ .compatible = "renesas,pcie-rcar-gen2", .data = rcar_pcie_hw_init_gen2 },
|
||||
{ .compatible = "renesas,pcie-r8a7790", .data = rcar_pcie_hw_init_gen2 },
|
||||
{ .compatible = "renesas,pcie-r8a7791", .data = rcar_pcie_hw_init_gen2 },
|
||||
{ .compatible = "renesas,pcie-rcar-gen2",
|
||||
.data = rcar_pcie_hw_init_gen2 },
|
||||
{ .compatible = "renesas,pcie-r8a7790",
|
||||
.data = rcar_pcie_hw_init_gen2 },
|
||||
{ .compatible = "renesas,pcie-r8a7791",
|
||||
.data = rcar_pcie_hw_init_gen2 },
|
||||
{ .compatible = "renesas,pcie-r8a7795", .data = rcar_pcie_hw_init },
|
||||
{},
|
||||
};
|
||||
@ -945,9 +1085,10 @@ static int rcar_pcie_parse_request_of_pci_ranges(struct rcar_pcie *pci)
|
||||
struct device *dev = pci->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
resource_size_t iobase;
|
||||
struct resource_entry *win;
|
||||
struct resource_entry *win, *tmp;
|
||||
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, &iobase);
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
|
||||
&iobase);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -955,14 +1096,17 @@ static int rcar_pcie_parse_request_of_pci_ranges(struct rcar_pcie *pci)
|
||||
if (err)
|
||||
goto out_release_res;
|
||||
|
||||
resource_list_for_each_entry(win, &pci->resources) {
|
||||
resource_list_for_each_entry_safe(win, tmp, &pci->resources) {
|
||||
struct resource *res = win->res;
|
||||
|
||||
if (resource_type(res) == IORESOURCE_IO) {
|
||||
err = pci_remap_iospace(res, iobase);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_warn(dev, "error %d: failed to map resource %pR\n",
|
||||
err, res);
|
||||
|
||||
resource_list_destroy_entry(win);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -998,8 +1142,8 @@ static int rcar_pcie_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
|
||||
if (err)
|
||||
err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
of_id = of_match_device(rcar_pcie_of_match, pcie->dev);
|
||||
|
1229
drivers/pci/host/pcie-rockchip.c
Normal file
1229
drivers/pci/host/pcie-rockchip.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/phy/phy.h>
|
||||
@ -355,7 +355,6 @@ static const struct of_device_id spear13xx_pcie_of_match[] = {
|
||||
{ .compatible = "st,spear1340-pcie", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spear13xx_pcie_of_match);
|
||||
|
||||
static struct platform_driver spear13xx_pcie_driver = {
|
||||
.probe = spear13xx_pcie_probe,
|
||||
@ -365,14 +364,8 @@ static struct platform_driver spear13xx_pcie_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
/* SPEAr13xx PCIe driver does not allow module unload */
|
||||
|
||||
static int __init spear13xx_pcie_init(void)
|
||||
{
|
||||
return platform_driver_register(&spear13xx_pcie_driver);
|
||||
}
|
||||
module_init(spear13xx_pcie_init);
|
||||
|
||||
MODULE_DESCRIPTION("ST Microelectronics SPEAr13xx PCIe host controller driver");
|
||||
MODULE_AUTHOR("Pratyush Anand <pratyush.anand@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
device_initcall(spear13xx_pcie_init);
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
@ -85,10 +85,15 @@
|
||||
#define MSGF_MISC_SR_MASTER_ERR BIT(5)
|
||||
#define MSGF_MISC_SR_I_ADDR_ERR BIT(6)
|
||||
#define MSGF_MISC_SR_E_ADDR_ERR BIT(7)
|
||||
#define MSGF_MISC_SR_UR_DETECT BIT(20)
|
||||
|
||||
#define MSGF_MISC_SR_PCIE_CORE GENMASK(18, 16)
|
||||
#define MSGF_MISC_SR_PCIE_CORE_ERR GENMASK(31, 22)
|
||||
#define MSGF_MISC_SR_FATAL_AER BIT(16)
|
||||
#define MSGF_MISC_SR_NON_FATAL_AER BIT(17)
|
||||
#define MSGF_MISC_SR_CORR_AER BIT(18)
|
||||
#define MSGF_MISC_SR_UR_DETECT BIT(20)
|
||||
#define MSGF_MISC_SR_NON_FATAL_DEV BIT(22)
|
||||
#define MSGF_MISC_SR_FATAL_DEV BIT(23)
|
||||
#define MSGF_MISC_SR_LINK_DOWN BIT(24)
|
||||
#define MSGF_MSIC_SR_LINK_AUTO_BWIDTH BIT(25)
|
||||
#define MSGF_MSIC_SR_LINK_BWIDTH BIT(26)
|
||||
|
||||
#define MSGF_MISC_SR_MASKALL (MSGF_MISC_SR_RXMSG_AVAIL | \
|
||||
MSGF_MISC_SR_RXMSG_OVER | \
|
||||
@ -96,9 +101,15 @@
|
||||
MSGF_MISC_SR_MASTER_ERR | \
|
||||
MSGF_MISC_SR_I_ADDR_ERR | \
|
||||
MSGF_MISC_SR_E_ADDR_ERR | \
|
||||
MSGF_MISC_SR_FATAL_AER | \
|
||||
MSGF_MISC_SR_NON_FATAL_AER | \
|
||||
MSGF_MISC_SR_CORR_AER | \
|
||||
MSGF_MISC_SR_UR_DETECT | \
|
||||
MSGF_MISC_SR_PCIE_CORE | \
|
||||
MSGF_MISC_SR_PCIE_CORE_ERR)
|
||||
MSGF_MISC_SR_NON_FATAL_DEV | \
|
||||
MSGF_MISC_SR_FATAL_DEV | \
|
||||
MSGF_MISC_SR_LINK_DOWN | \
|
||||
MSGF_MSIC_SR_LINK_AUTO_BWIDTH | \
|
||||
MSGF_MSIC_SR_LINK_BWIDTH)
|
||||
|
||||
/* Legacy interrupt status mask bits */
|
||||
#define MSGF_LEG_SR_INTA BIT(0)
|
||||
@ -109,8 +120,8 @@
|
||||
MSGF_LEG_SR_INTC | MSGF_LEG_SR_INTD)
|
||||
|
||||
/* MSI interrupt status mask bits */
|
||||
#define MSGF_MSI_SR_LO_MASK BIT(0)
|
||||
#define MSGF_MSI_SR_HI_MASK BIT(0)
|
||||
#define MSGF_MSI_SR_LO_MASK GENMASK(31, 0)
|
||||
#define MSGF_MSI_SR_HI_MASK GENMASK(31, 0)
|
||||
|
||||
#define MSII_PRESENT BIT(0)
|
||||
#define MSII_ENABLE BIT(0)
|
||||
@ -291,8 +302,29 @@ static irqreturn_t nwl_pcie_misc_handler(int irq, void *data)
|
||||
dev_err(pcie->dev,
|
||||
"In Misc Egress address translation error\n");
|
||||
|
||||
if (misc_stat & MSGF_MISC_SR_PCIE_CORE_ERR)
|
||||
dev_err(pcie->dev, "PCIe Core error\n");
|
||||
if (misc_stat & MSGF_MISC_SR_FATAL_AER)
|
||||
dev_err(pcie->dev, "Fatal Error in AER Capability\n");
|
||||
|
||||
if (misc_stat & MSGF_MISC_SR_NON_FATAL_AER)
|
||||
dev_err(pcie->dev, "Non-Fatal Error in AER Capability\n");
|
||||
|
||||
if (misc_stat & MSGF_MISC_SR_CORR_AER)
|
||||
dev_err(pcie->dev, "Correctable Error in AER Capability\n");
|
||||
|
||||
if (misc_stat & MSGF_MISC_SR_UR_DETECT)
|
||||
dev_err(pcie->dev, "Unsupported request Detected\n");
|
||||
|
||||
if (misc_stat & MSGF_MISC_SR_NON_FATAL_DEV)
|
||||
dev_err(pcie->dev, "Non-Fatal Error Detected\n");
|
||||
|
||||
if (misc_stat & MSGF_MISC_SR_FATAL_DEV)
|
||||
dev_err(pcie->dev, "Fatal Error Detected\n");
|
||||
|
||||
if (misc_stat & MSGF_MSIC_SR_LINK_AUTO_BWIDTH)
|
||||
dev_info(pcie->dev, "Link Autonomous Bandwidth Management Status bit set\n");
|
||||
|
||||
if (misc_stat & MSGF_MSIC_SR_LINK_BWIDTH)
|
||||
dev_info(pcie->dev, "Link Bandwidth Management Status bit set\n");
|
||||
|
||||
/* Clear misc interrupt status */
|
||||
nwl_bridge_writel(pcie, misc_stat, MSGF_MISC_STATUS);
|
||||
@ -459,40 +491,6 @@ static const struct irq_domain_ops dev_msi_domain_ops = {
|
||||
.free = nwl_irq_domain_free,
|
||||
};
|
||||
|
||||
static void nwl_msi_free_irq_domain(struct nwl_pcie *pcie)
|
||||
{
|
||||
struct nwl_msi *msi = &pcie->msi;
|
||||
|
||||
if (msi->irq_msi0)
|
||||
irq_set_chained_handler_and_data(msi->irq_msi0, NULL, NULL);
|
||||
if (msi->irq_msi1)
|
||||
irq_set_chained_handler_and_data(msi->irq_msi1, NULL, NULL);
|
||||
|
||||
if (msi->msi_domain)
|
||||
irq_domain_remove(msi->msi_domain);
|
||||
if (msi->dev_domain)
|
||||
irq_domain_remove(msi->dev_domain);
|
||||
|
||||
kfree(msi->bitmap);
|
||||
msi->bitmap = NULL;
|
||||
}
|
||||
|
||||
static void nwl_pcie_free_irq_domain(struct nwl_pcie *pcie)
|
||||
{
|
||||
int i;
|
||||
u32 irq;
|
||||
|
||||
for (i = 0; i < INTX_NUM; i++) {
|
||||
irq = irq_find_mapping(pcie->legacy_irq_domain, i + 1);
|
||||
if (irq > 0)
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
if (pcie->legacy_irq_domain)
|
||||
irq_domain_remove(pcie->legacy_irq_domain);
|
||||
|
||||
nwl_msi_free_irq_domain(pcie);
|
||||
}
|
||||
|
||||
static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
|
||||
{
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
@ -867,25 +865,12 @@ error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nwl_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct nwl_pcie *pcie = platform_get_drvdata(pdev);
|
||||
|
||||
nwl_pcie_free_irq_domain(pcie);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver nwl_pcie_driver = {
|
||||
.driver = {
|
||||
.name = "nwl-pcie",
|
||||
.suppress_bind_attrs = true,
|
||||
.of_match_table = nwl_pcie_of_match,
|
||||
},
|
||||
.probe = nwl_pcie_probe,
|
||||
.remove = nwl_pcie_remove,
|
||||
};
|
||||
module_platform_driver(nwl_pcie_driver);
|
||||
|
||||
MODULE_AUTHOR("Xilinx, Inc");
|
||||
MODULE_DESCRIPTION("NWL PCIe driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
builtin_platform_driver(nwl_pcie_driver);
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
@ -101,7 +101,8 @@
|
||||
* @msi_pages: MSI pages
|
||||
* @root_busno: Root Bus number
|
||||
* @dev: Device pointer
|
||||
* @irq_domain: IRQ domain pointer
|
||||
* @msi_domain: MSI IRQ domain pointer
|
||||
* @leg_domain: Legacy IRQ domain pointer
|
||||
* @resources: Bus Resources
|
||||
*/
|
||||
struct xilinx_pcie_port {
|
||||
@ -110,7 +111,8 @@ struct xilinx_pcie_port {
|
||||
unsigned long msi_pages;
|
||||
u8 root_busno;
|
||||
struct device *dev;
|
||||
struct irq_domain *irq_domain;
|
||||
struct irq_domain *msi_domain;
|
||||
struct irq_domain *leg_domain;
|
||||
struct list_head resources;
|
||||
};
|
||||
|
||||
@ -168,13 +170,6 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
|
||||
if (bus->number == port->root_busno && devfn > 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Do not read more than one device on the bus directly attached
|
||||
* to RC.
|
||||
*/
|
||||
if (bus->primary == port->root_busno && devfn > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -219,13 +214,15 @@ static void xilinx_pcie_destroy_msi(unsigned int irq)
|
||||
{
|
||||
struct msi_desc *msi;
|
||||
struct xilinx_pcie_port *port;
|
||||
struct irq_data *d = irq_get_irq_data(irq);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
if (!test_bit(irq, msi_irq_in_use)) {
|
||||
if (!test_bit(hwirq, msi_irq_in_use)) {
|
||||
msi = irq_get_msi_desc(irq);
|
||||
port = msi_desc_to_pci_sysdata(msi);
|
||||
dev_err(port->dev, "Trying to free unused MSI#%d\n", irq);
|
||||
} else {
|
||||
clear_bit(irq, msi_irq_in_use);
|
||||
clear_bit(hwirq, msi_irq_in_use);
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,6 +254,7 @@ static void xilinx_msi_teardown_irq(struct msi_controller *chip,
|
||||
unsigned int irq)
|
||||
{
|
||||
xilinx_pcie_destroy_msi(irq);
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,7 +279,7 @@ static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip,
|
||||
if (hwirq < 0)
|
||||
return hwirq;
|
||||
|
||||
irq = irq_create_mapping(port->irq_domain, hwirq);
|
||||
irq = irq_create_mapping(port->msi_domain, hwirq);
|
||||
if (!irq)
|
||||
return -EINVAL;
|
||||
|
||||
@ -432,7 +430,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
|
||||
/* Check whether interrupt valid */
|
||||
if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
|
||||
dev_warn(port->dev, "RP Intr FIFO1 read error\n");
|
||||
return IRQ_HANDLED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(val & XILINX_PCIE_RPIFR1_MSI_INTR)) {
|
||||
@ -443,7 +441,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
|
||||
/* Handle INTx Interrupt */
|
||||
val = ((val & XILINX_PCIE_RPIFR1_INTR_MASK) >>
|
||||
XILINX_PCIE_RPIFR1_INTR_SHIFT) + 1;
|
||||
generic_handle_irq(irq_find_mapping(port->irq_domain,
|
||||
generic_handle_irq(irq_find_mapping(port->leg_domain,
|
||||
val));
|
||||
}
|
||||
}
|
||||
@ -454,7 +452,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
|
||||
|
||||
if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
|
||||
dev_warn(port->dev, "RP Intr FIFO1 read error\n");
|
||||
return IRQ_HANDLED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (val & XILINX_PCIE_RPIFR1_MSI_INTR) {
|
||||
@ -499,41 +497,13 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
|
||||
if (status & XILINX_PCIE_INTR_MST_ERRP)
|
||||
dev_warn(port->dev, "Master error poison\n");
|
||||
|
||||
error:
|
||||
/* Clear the Interrupt Decode register */
|
||||
pcie_write(port, status, XILINX_PCIE_REG_IDR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_free_irq_domain - Free IRQ domain
|
||||
* @port: PCIe port information
|
||||
*/
|
||||
static void xilinx_pcie_free_irq_domain(struct xilinx_pcie_port *port)
|
||||
{
|
||||
int i;
|
||||
u32 irq, num_irqs;
|
||||
|
||||
/* Free IRQ Domain */
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
|
||||
free_pages(port->msi_pages, 0);
|
||||
|
||||
num_irqs = XILINX_NUM_MSI_IRQS;
|
||||
} else {
|
||||
/* INTx */
|
||||
num_irqs = 4;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_irqs; i++) {
|
||||
irq = irq_find_mapping(port->irq_domain, i);
|
||||
if (irq > 0)
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
|
||||
irq_domain_remove(port->irq_domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_init_irq_domain - Initialize IRQ domain
|
||||
* @port: PCIe port information
|
||||
@ -553,21 +523,21 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
port->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
|
||||
port->leg_domain = irq_domain_add_linear(pcie_intc_node, 4,
|
||||
&intx_domain_ops,
|
||||
port);
|
||||
if (!port->irq_domain) {
|
||||
if (!port->leg_domain) {
|
||||
dev_err(dev, "Failed to get a INTx IRQ domain\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Setup MSI */
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
port->irq_domain = irq_domain_add_linear(node,
|
||||
port->msi_domain = irq_domain_add_linear(node,
|
||||
XILINX_NUM_MSI_IRQS,
|
||||
&msi_domain_ops,
|
||||
&xilinx_pcie_msi_chip);
|
||||
if (!port->irq_domain) {
|
||||
if (!port->msi_domain) {
|
||||
dev_err(dev, "Failed to get a MSI IRQ domain\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -724,21 +694,6 @@ error:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_pcie_remove - Remove function
|
||||
* @pdev: Platform device pointer
|
||||
*
|
||||
* Return: '0' always
|
||||
*/
|
||||
static int xilinx_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xilinx_pcie_port *port = platform_get_drvdata(pdev);
|
||||
|
||||
xilinx_pcie_free_irq_domain(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id xilinx_pcie_of_match[] = {
|
||||
{ .compatible = "xlnx,axi-pcie-host-1.00.a", },
|
||||
{}
|
||||
@ -751,10 +706,5 @@ static struct platform_driver xilinx_pcie_driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = xilinx_pcie_probe,
|
||||
.remove = xilinx_pcie_remove,
|
||||
};
|
||||
module_platform_driver(xilinx_pcie_driver);
|
||||
|
||||
MODULE_AUTHOR("Xilinx Inc");
|
||||
MODULE_DESCRIPTION("Xilinx AXI PCIe driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
builtin_platform_driver(xilinx_pcie_driver);
|
||||
|
@ -58,16 +58,11 @@ struct vmd_irq {
|
||||
/**
|
||||
* struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector
|
||||
* @irq_list: the list of irq's the VMD one demuxes to.
|
||||
* @vmd_vector: the h/w IRQ assigned to the VMD.
|
||||
* @index: index into the VMD MSI-X table; used for message routing.
|
||||
* @count: number of child IRQs assigned to this vector; used to track
|
||||
* sharing.
|
||||
*/
|
||||
struct vmd_irq_list {
|
||||
struct list_head irq_list;
|
||||
struct vmd_dev *vmd;
|
||||
unsigned int vmd_vector;
|
||||
unsigned int index;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
@ -78,7 +73,6 @@ struct vmd_dev {
|
||||
char __iomem *cfgbar;
|
||||
|
||||
int msix_count;
|
||||
struct msix_entry *msix_entries;
|
||||
struct vmd_irq_list *irqs;
|
||||
|
||||
struct pci_sysdata sysdata;
|
||||
@ -97,6 +91,12 @@ static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
|
||||
return container_of(bus->sysdata, struct vmd_dev, sysdata);
|
||||
}
|
||||
|
||||
static inline unsigned int index_from_irqs(struct vmd_dev *vmd,
|
||||
struct vmd_irq_list *irqs)
|
||||
{
|
||||
return irqs - vmd->irqs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drivers managing a device in a VMD domain allocate their own IRQs as before,
|
||||
* but the MSI entry for the hardware it's driving will be programmed with a
|
||||
@ -109,9 +109,11 @@ static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
{
|
||||
struct vmd_irq *vmdirq = data->chip_data;
|
||||
struct vmd_irq_list *irq = vmdirq->irq;
|
||||
struct vmd_dev *vmd = irq_data_get_irq_handler_data(data);
|
||||
|
||||
msg->address_hi = MSI_ADDR_BASE_HI;
|
||||
msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_DEST_ID(irq->index);
|
||||
msg->address_lo = MSI_ADDR_BASE_LO |
|
||||
MSI_ADDR_DEST_ID(index_from_irqs(vmd, irq));
|
||||
msg->data = 0;
|
||||
}
|
||||
|
||||
@ -200,6 +202,7 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
|
||||
struct msi_desc *desc = arg->desc;
|
||||
struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(desc)->bus);
|
||||
struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL);
|
||||
unsigned int index, vector;
|
||||
|
||||
if (!vmdirq)
|
||||
return -ENOMEM;
|
||||
@ -207,9 +210,11 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
|
||||
INIT_LIST_HEAD(&vmdirq->node);
|
||||
vmdirq->irq = vmd_next_irq(vmd, desc);
|
||||
vmdirq->virq = virq;
|
||||
index = index_from_irqs(vmd, vmdirq->irq);
|
||||
vector = pci_irq_vector(vmd->dev, index);
|
||||
|
||||
irq_domain_set_info(domain, virq, vmdirq->irq->vmd_vector, info->chip,
|
||||
vmdirq, handle_untracked_irq, vmd, NULL);
|
||||
irq_domain_set_info(domain, virq, vector, info->chip, vmdirq,
|
||||
handle_untracked_irq, vmd, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -219,6 +224,8 @@ static void vmd_msi_free(struct irq_domain *domain,
|
||||
struct vmd_irq *vmdirq = irq_get_chip_data(virq);
|
||||
unsigned long flags;
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
/* XXX: Potential optimization to rebalance */
|
||||
raw_spin_lock_irqsave(&list_lock, flags);
|
||||
vmdirq->irq->count--;
|
||||
@ -602,6 +609,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd)
|
||||
.parent = res,
|
||||
};
|
||||
|
||||
sd->vmd_domain = true;
|
||||
sd->domain = vmd_find_free_domain();
|
||||
if (sd->domain < 0)
|
||||
return sd->domain;
|
||||
@ -677,30 +685,19 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
if (vmd->msix_count < 0)
|
||||
return -ENODEV;
|
||||
|
||||
vmd->msix_count = pci_alloc_irq_vectors(dev, 1, vmd->msix_count,
|
||||
PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
|
||||
if (vmd->msix_count < 0)
|
||||
return vmd->msix_count;
|
||||
|
||||
vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs),
|
||||
GFP_KERNEL);
|
||||
if (!vmd->irqs)
|
||||
return -ENOMEM;
|
||||
|
||||
vmd->msix_entries = devm_kcalloc(&dev->dev, vmd->msix_count,
|
||||
sizeof(*vmd->msix_entries),
|
||||
GFP_KERNEL);
|
||||
if (!vmd->msix_entries)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < vmd->msix_count; i++)
|
||||
vmd->msix_entries[i].entry = i;
|
||||
|
||||
vmd->msix_count = pci_enable_msix_range(vmd->dev, vmd->msix_entries, 1,
|
||||
vmd->msix_count);
|
||||
if (vmd->msix_count < 0)
|
||||
return vmd->msix_count;
|
||||
|
||||
for (i = 0; i < vmd->msix_count; i++) {
|
||||
INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
|
||||
vmd->irqs[i].vmd_vector = vmd->msix_entries[i].vector;
|
||||
vmd->irqs[i].index = i;
|
||||
|
||||
err = devm_request_irq(&dev->dev, vmd->irqs[i].vmd_vector,
|
||||
err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i),
|
||||
vmd_irq, 0, "vmd", &vmd->irqs[i]);
|
||||
if (err)
|
||||
return err;
|
@ -101,10 +101,8 @@ int cpci_unconfigure_slot(struct slot *slot);
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_PCI_CPCI
|
||||
int cpci_hotplug_init(int debug);
|
||||
void cpci_hotplug_exit(void);
|
||||
#else
|
||||
static inline int cpci_hotplug_init(int debug) { return 0; }
|
||||
static inline void cpci_hotplug_exit(void) { }
|
||||
#endif
|
||||
|
||||
#endif /* _CPCI_HOTPLUG_H */
|
||||
|
@ -719,13 +719,3 @@ cpci_hotplug_init(int debug)
|
||||
cpci_debug = debug;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit
|
||||
cpci_hotplug_exit(void)
|
||||
{
|
||||
/*
|
||||
* Clean everything up.
|
||||
*/
|
||||
cpci_hp_stop();
|
||||
cpci_hp_unregister_controller(controller);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/module.h> /* try_module_get & module_put */
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
@ -537,17 +537,11 @@ static int __init pci_hotplug_init(void)
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
return result;
|
||||
}
|
||||
device_initcall(pci_hotplug_init);
|
||||
|
||||
static void __exit pci_hotplug_exit(void)
|
||||
{
|
||||
cpci_hotplug_exit();
|
||||
}
|
||||
|
||||
module_init(pci_hotplug_init);
|
||||
module_exit(pci_hotplug_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
/*
|
||||
* not really modular, but the easiest way to keep compat with existing
|
||||
* bootargs behaviour is to continue using module_param here.
|
||||
*/
|
||||
module_param(debug, bool, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
||||
|
@ -152,6 +152,9 @@ bool pciehp_check_link_active(struct controller *ctrl);
|
||||
void pciehp_release_ctrl(struct controller *ctrl);
|
||||
int pciehp_reset_slot(struct slot *slot, int probe);
|
||||
|
||||
int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status);
|
||||
int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status);
|
||||
|
||||
static inline const char *slot_name(struct slot *slot)
|
||||
{
|
||||
return hotplug_slot_name(slot->hotplug_slot);
|
||||
|
@ -27,7 +27,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
@ -47,10 +46,10 @@ static bool pciehp_force;
|
||||
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
|
||||
#define DRIVER_DESC "PCI Express Hot Plug Controller Driver"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* not really modular, but the easiest way to keep compat with existing
|
||||
* bootargs behaviour is to continue using module_param here.
|
||||
*/
|
||||
module_param(pciehp_debug, bool, 0644);
|
||||
module_param(pciehp_poll_mode, bool, 0644);
|
||||
module_param(pciehp_poll_time, int, 0644);
|
||||
@ -114,6 +113,9 @@ static int init_slot(struct controller *ctrl)
|
||||
if (ATTN_LED(ctrl)) {
|
||||
ops->get_attention_status = get_attention_status;
|
||||
ops->set_attention_status = set_attention_status;
|
||||
} else if (ctrl->pcie->port->hotplug_user_indicators) {
|
||||
ops->get_attention_status = pciehp_get_raw_indicator_status;
|
||||
ops->set_attention_status = pciehp_set_raw_indicator_status;
|
||||
}
|
||||
|
||||
/* register this slot with the hotplug pci core */
|
||||
@ -337,13 +339,4 @@ static int __init pcied_init(void)
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit pcied_cleanup(void)
|
||||
{
|
||||
dbg("unload_pciehpd()\n");
|
||||
pcie_port_service_unregister(&hpdriver_portdrv);
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
|
||||
}
|
||||
|
||||
module_init(pcied_init);
|
||||
module_exit(pcied_cleanup);
|
||||
device_initcall(pcied_init);
|
||||
|
@ -106,7 +106,7 @@ static int board_added(struct slot *p_slot)
|
||||
|
||||
/* Check for a power fault */
|
||||
if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
|
||||
ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
|
||||
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot));
|
||||
retval = -EIO;
|
||||
goto err_exit;
|
||||
}
|
||||
@ -120,6 +120,7 @@ static int board_added(struct slot *p_slot)
|
||||
}
|
||||
|
||||
pciehp_green_led_on(p_slot);
|
||||
pciehp_set_attention_status(p_slot, 0);
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
@ -253,11 +254,11 @@ static void handle_button_press_event(struct slot *p_slot)
|
||||
pciehp_get_power_status(p_slot, &getstatus);
|
||||
if (getstatus) {
|
||||
p_slot->state = BLINKINGOFF_STATE;
|
||||
ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
|
||||
slot_name(p_slot));
|
||||
} else {
|
||||
p_slot->state = BLINKINGON_STATE;
|
||||
ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",
|
||||
ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
|
||||
slot_name(p_slot));
|
||||
}
|
||||
/* blink green LED and turn off amber */
|
||||
@ -272,14 +273,14 @@ static void handle_button_press_event(struct slot *p_slot)
|
||||
* press the attention again before the 5 sec. limit
|
||||
* expires to cancel hot-add or hot-remove
|
||||
*/
|
||||
ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
|
||||
ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
|
||||
cancel_delayed_work(&p_slot->work);
|
||||
if (p_slot->state == BLINKINGOFF_STATE)
|
||||
pciehp_green_led_on(p_slot);
|
||||
else
|
||||
pciehp_green_led_off(p_slot);
|
||||
pciehp_set_attention_status(p_slot, 0);
|
||||
ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
|
||||
slot_name(p_slot));
|
||||
p_slot->state = STATIC_STATE;
|
||||
break;
|
||||
@ -290,28 +291,16 @@ static void handle_button_press_event(struct slot *p_slot)
|
||||
* this means that the previous attention button action
|
||||
* to hot-add or hot-remove is undergoing
|
||||
*/
|
||||
ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
|
||||
ctrl_info(ctrl, "Slot(%s): Button ignored\n",
|
||||
slot_name(p_slot));
|
||||
break;
|
||||
default:
|
||||
ctrl_warn(ctrl, "ignoring invalid state %#x\n", p_slot->state);
|
||||
ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
|
||||
slot_name(p_slot), p_slot->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: This function must be called with slot->lock held
|
||||
*/
|
||||
static void handle_surprise_event(struct slot *p_slot)
|
||||
{
|
||||
u8 getstatus;
|
||||
|
||||
pciehp_get_adapter_status(p_slot, &getstatus);
|
||||
if (!getstatus)
|
||||
pciehp_queue_power_work(p_slot, DISABLE_REQ);
|
||||
else
|
||||
pciehp_queue_power_work(p_slot, ENABLE_REQ);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: This function must be called with slot->lock held
|
||||
*/
|
||||
@ -330,31 +319,27 @@ static void handle_link_event(struct slot *p_slot, u32 event)
|
||||
break;
|
||||
case POWERON_STATE:
|
||||
if (event == INT_LINK_UP) {
|
||||
ctrl_info(ctrl,
|
||||
"Link Up event ignored on slot(%s): already powering on\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n",
|
||||
slot_name(p_slot));
|
||||
} else {
|
||||
ctrl_info(ctrl,
|
||||
"Link Down event queued on slot(%s): currently getting powered on\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n",
|
||||
slot_name(p_slot));
|
||||
pciehp_queue_power_work(p_slot, DISABLE_REQ);
|
||||
}
|
||||
break;
|
||||
case POWEROFF_STATE:
|
||||
if (event == INT_LINK_UP) {
|
||||
ctrl_info(ctrl,
|
||||
"Link Up event queued on slot(%s): currently getting powered off\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n",
|
||||
slot_name(p_slot));
|
||||
pciehp_queue_power_work(p_slot, ENABLE_REQ);
|
||||
} else {
|
||||
ctrl_info(ctrl,
|
||||
"Link Down event ignored on slot(%s): already powering off\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n",
|
||||
slot_name(p_slot));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ctrl_err(ctrl, "ignoring invalid state %#x on slot(%s)\n",
|
||||
p_slot->state, slot_name(p_slot));
|
||||
ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
|
||||
slot_name(p_slot), p_slot->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -377,14 +362,14 @@ static void interrupt_event_handler(struct work_struct *work)
|
||||
pciehp_green_led_off(p_slot);
|
||||
break;
|
||||
case INT_PRESENCE_ON:
|
||||
handle_surprise_event(p_slot);
|
||||
pciehp_queue_power_work(p_slot, ENABLE_REQ);
|
||||
break;
|
||||
case INT_PRESENCE_OFF:
|
||||
/*
|
||||
* Regardless of surprise capability, we need to
|
||||
* definitely remove a card that has been pulled out!
|
||||
*/
|
||||
handle_surprise_event(p_slot);
|
||||
pciehp_queue_power_work(p_slot, DISABLE_REQ);
|
||||
break;
|
||||
case INT_LINK_UP:
|
||||
case INT_LINK_DOWN:
|
||||
@ -404,18 +389,17 @@ static void interrupt_event_handler(struct work_struct *work)
|
||||
int pciehp_enable_slot(struct slot *p_slot)
|
||||
{
|
||||
u8 getstatus = 0;
|
||||
int rc;
|
||||
struct controller *ctrl = p_slot->ctrl;
|
||||
|
||||
pciehp_get_adapter_status(p_slot, &getstatus);
|
||||
if (!getstatus) {
|
||||
ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
|
||||
ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot));
|
||||
return -ENODEV;
|
||||
}
|
||||
if (MRL_SENS(p_slot->ctrl)) {
|
||||
pciehp_get_latch_status(p_slot, &getstatus);
|
||||
if (getstatus) {
|
||||
ctrl_info(ctrl, "Latch open on slot(%s)\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Latch open\n",
|
||||
slot_name(p_slot));
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -424,19 +408,13 @@ int pciehp_enable_slot(struct slot *p_slot)
|
||||
if (POWER_CTRL(p_slot->ctrl)) {
|
||||
pciehp_get_power_status(p_slot, &getstatus);
|
||||
if (getstatus) {
|
||||
ctrl_info(ctrl, "Already enabled on slot(%s)\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
|
||||
slot_name(p_slot));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
pciehp_get_latch_status(p_slot, &getstatus);
|
||||
|
||||
rc = board_added(p_slot);
|
||||
if (rc)
|
||||
pciehp_get_latch_status(p_slot, &getstatus);
|
||||
|
||||
return rc;
|
||||
return board_added(p_slot);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -453,7 +431,7 @@ int pciehp_disable_slot(struct slot *p_slot)
|
||||
if (POWER_CTRL(p_slot->ctrl)) {
|
||||
pciehp_get_power_status(p_slot, &getstatus);
|
||||
if (!getstatus) {
|
||||
ctrl_info(ctrl, "Already disabled on slot(%s)\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
|
||||
slot_name(p_slot));
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -481,17 +459,17 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
|
||||
p_slot->state = STATIC_STATE;
|
||||
break;
|
||||
case POWERON_STATE:
|
||||
ctrl_info(ctrl, "Slot %s is already in powering on state\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
|
||||
slot_name(p_slot));
|
||||
break;
|
||||
case BLINKINGOFF_STATE:
|
||||
case POWEROFF_STATE:
|
||||
ctrl_info(ctrl, "Already enabled on slot %s\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
|
||||
slot_name(p_slot));
|
||||
break;
|
||||
default:
|
||||
ctrl_err(ctrl, "invalid state %#x on slot %s\n",
|
||||
p_slot->state, slot_name(p_slot));
|
||||
ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
|
||||
slot_name(p_slot), p_slot->state);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&p_slot->lock);
|
||||
@ -518,17 +496,17 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
|
||||
p_slot->state = STATIC_STATE;
|
||||
break;
|
||||
case POWEROFF_STATE:
|
||||
ctrl_info(ctrl, "Slot %s is already in powering off state\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
|
||||
slot_name(p_slot));
|
||||
break;
|
||||
case BLINKINGON_STATE:
|
||||
case POWERON_STATE:
|
||||
ctrl_info(ctrl, "Already disabled on slot %s\n",
|
||||
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
|
||||
slot_name(p_slot));
|
||||
break;
|
||||
default:
|
||||
ctrl_err(ctrl, "invalid state %#x on slot %s\n",
|
||||
p_slot->state, slot_name(p_slot));
|
||||
ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
|
||||
slot_name(p_slot), p_slot->state);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&p_slot->lock);
|
||||
|
@ -355,6 +355,18 @@ static int pciehp_link_enable(struct controller *ctrl)
|
||||
return __pciehp_link_set(ctrl, true);
|
||||
}
|
||||
|
||||
int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
||||
u8 *status)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
struct pci_dev *pdev = ctrl_dev(slot->ctrl);
|
||||
u16 slot_ctrl;
|
||||
|
||||
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
|
||||
*status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pciehp_get_attention_status(struct slot *slot, u8 *status)
|
||||
{
|
||||
struct controller *ctrl = slot->ctrl;
|
||||
@ -431,6 +443,17 @@ int pciehp_query_power_fault(struct slot *slot)
|
||||
return !!(slot_status & PCI_EXP_SLTSTA_PFD);
|
||||
}
|
||||
|
||||
int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
||||
u8 status)
|
||||
{
|
||||
struct slot *slot = hotplug_slot->private;
|
||||
struct controller *ctrl = slot->ctrl;
|
||||
|
||||
pcie_write_cmd_nowait(ctrl, status << 6,
|
||||
PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pciehp_set_attention_status(struct slot *slot, u8 value)
|
||||
{
|
||||
struct controller *ctrl = slot->ctrl;
|
||||
@ -535,14 +558,14 @@ void pciehp_power_off_slot(struct slot *slot)
|
||||
PCI_EXP_SLTCTL_PWR_OFF);
|
||||
}
|
||||
|
||||
static irqreturn_t pcie_isr(int irq, void *dev_id)
|
||||
static irqreturn_t pciehp_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct controller *ctrl = (struct controller *)dev_id;
|
||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||
struct pci_bus *subordinate = pdev->subordinate;
|
||||
struct pci_dev *dev;
|
||||
struct slot *slot = ctrl->slot;
|
||||
u16 detected, intr_loc;
|
||||
u16 status, events;
|
||||
u8 present;
|
||||
bool link;
|
||||
|
||||
@ -550,36 +573,31 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
|
||||
if (pdev->current_state == PCI_D3cold)
|
||||
return IRQ_NONE;
|
||||
|
||||
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status);
|
||||
if (status == (u16) ~0) {
|
||||
ctrl_info(ctrl, "%s: no response from device\n", __func__);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* In order to guarantee that all interrupt events are
|
||||
* serviced, we need to re-inspect Slot Status register after
|
||||
* clearing what is presumed to be the last pending interrupt.
|
||||
* Slot Status contains plain status bits as well as event
|
||||
* notification bits; right now we only want the event bits.
|
||||
*/
|
||||
intr_loc = 0;
|
||||
do {
|
||||
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected);
|
||||
if (detected == (u16) ~0) {
|
||||
ctrl_info(ctrl, "%s: no response from device\n",
|
||||
__func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
events = status & (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
|
||||
PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC |
|
||||
PCI_EXP_SLTSTA_DLLSC);
|
||||
if (!events)
|
||||
return IRQ_NONE;
|
||||
|
||||
detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
|
||||
PCI_EXP_SLTSTA_PDC |
|
||||
PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
|
||||
detected &= ~intr_loc;
|
||||
intr_loc |= detected;
|
||||
if (!intr_loc)
|
||||
return IRQ_NONE;
|
||||
if (detected)
|
||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
|
||||
intr_loc);
|
||||
} while (detected);
|
||||
/* Capture link status before clearing interrupts */
|
||||
if (events & PCI_EXP_SLTSTA_DLLSC)
|
||||
link = pciehp_check_link_active(ctrl);
|
||||
|
||||
ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", intr_loc);
|
||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events);
|
||||
ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events);
|
||||
|
||||
/* Check Command Complete Interrupt Pending */
|
||||
if (intr_loc & PCI_EXP_SLTSTA_CC) {
|
||||
if (events & PCI_EXP_SLTSTA_CC) {
|
||||
ctrl->cmd_busy = 0;
|
||||
smp_mb();
|
||||
wake_up(&ctrl->queue);
|
||||
@ -589,42 +607,38 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
|
||||
list_for_each_entry(dev, &subordinate->devices, bus_list) {
|
||||
if (dev->ignore_hotplug) {
|
||||
ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n",
|
||||
intr_loc, pci_name(dev));
|
||||
events, pci_name(dev));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(intr_loc & ~PCI_EXP_SLTSTA_CC))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* Check Attention Button Pressed */
|
||||
if (intr_loc & PCI_EXP_SLTSTA_ABP) {
|
||||
ctrl_info(ctrl, "Button pressed on Slot(%s)\n",
|
||||
if (events & PCI_EXP_SLTSTA_ABP) {
|
||||
ctrl_info(ctrl, "Slot(%s): Attention button pressed\n",
|
||||
slot_name(slot));
|
||||
pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
|
||||
}
|
||||
|
||||
/* Check Presence Detect Changed */
|
||||
if (intr_loc & PCI_EXP_SLTSTA_PDC) {
|
||||
pciehp_get_adapter_status(slot, &present);
|
||||
ctrl_info(ctrl, "Card %spresent on Slot(%s)\n",
|
||||
present ? "" : "not ", slot_name(slot));
|
||||
if (events & PCI_EXP_SLTSTA_PDC) {
|
||||
present = !!(status & PCI_EXP_SLTSTA_PDS);
|
||||
ctrl_info(ctrl, "Slot(%s): Card %spresent\n", slot_name(slot),
|
||||
present ? "" : "not ");
|
||||
pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON :
|
||||
INT_PRESENCE_OFF);
|
||||
}
|
||||
|
||||
/* Check Power Fault Detected */
|
||||
if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
|
||||
if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
|
||||
ctrl->power_fault_detected = 1;
|
||||
ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(slot));
|
||||
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot));
|
||||
pciehp_queue_interrupt_event(slot, INT_POWER_FAULT);
|
||||
}
|
||||
|
||||
if (intr_loc & PCI_EXP_SLTSTA_DLLSC) {
|
||||
link = pciehp_check_link_active(ctrl);
|
||||
ctrl_info(ctrl, "slot(%s): Link %s event\n",
|
||||
slot_name(slot), link ? "Up" : "Down");
|
||||
if (events & PCI_EXP_SLTSTA_DLLSC) {
|
||||
ctrl_info(ctrl, "Slot(%s): Link %s\n", slot_name(slot),
|
||||
link ? "Up" : "Down");
|
||||
pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP :
|
||||
INT_LINK_DOWN);
|
||||
}
|
||||
@ -632,6 +646,25 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t pcie_isr(int irq, void *dev_id)
|
||||
{
|
||||
irqreturn_t rc, handled = IRQ_NONE;
|
||||
|
||||
/*
|
||||
* To guarantee that all interrupt events are serviced, we need to
|
||||
* re-inspect Slot Status register after clearing what is presumed
|
||||
* to be the last pending interrupt.
|
||||
*/
|
||||
do {
|
||||
rc = pciehp_isr(irq, dev_id);
|
||||
if (rc == IRQ_HANDLED)
|
||||
handled = IRQ_HANDLED;
|
||||
} while (rc == IRQ_HANDLED);
|
||||
|
||||
/* Return IRQ_HANDLED if we handled one or more events */
|
||||
return handled;
|
||||
}
|
||||
|
||||
void pcie_enable_notification(struct controller *ctrl)
|
||||
{
|
||||
u16 cmd, mask;
|
||||
@ -804,6 +837,10 @@ struct controller *pcie_init(struct pcie_device *dev)
|
||||
}
|
||||
ctrl->pcie = dev;
|
||||
pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
|
||||
|
||||
if (pdev->hotplug_user_indicators)
|
||||
slot_cap &= ~(PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_PIP);
|
||||
|
||||
ctrl->slot_cap = slot_cap;
|
||||
mutex_init(&ctrl->ctrl_lock);
|
||||
init_waitqueue_head(&ctrl->queue);
|
||||
|
@ -136,7 +136,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset)
|
||||
virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
|
||||
virtfn->vendor = dev->vendor;
|
||||
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
|
||||
pci_setup_device(virtfn);
|
||||
rc = pci_setup_device(virtfn);
|
||||
if (rc)
|
||||
goto failed0;
|
||||
|
||||
virtfn->dev.parent = dev->dev.parent;
|
||||
virtfn->physfn = pci_dev_get(dev);
|
||||
virtfn->is_virtfn = 1;
|
||||
|
@ -452,6 +452,27 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||
return error;
|
||||
}
|
||||
|
||||
static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
|
||||
static const pci_power_t state_conv[] = {
|
||||
[ACPI_STATE_D0] = PCI_D0,
|
||||
[ACPI_STATE_D1] = PCI_D1,
|
||||
[ACPI_STATE_D2] = PCI_D2,
|
||||
[ACPI_STATE_D3_HOT] = PCI_D3hot,
|
||||
[ACPI_STATE_D3_COLD] = PCI_D3cold,
|
||||
};
|
||||
int state;
|
||||
|
||||
if (!adev || !acpi_device_power_manageable(adev))
|
||||
return PCI_UNKNOWN;
|
||||
|
||||
if (acpi_device_get_power(adev, &state) || state == ACPI_STATE_UNKNOWN)
|
||||
return PCI_UNKNOWN;
|
||||
|
||||
return state_conv[state];
|
||||
}
|
||||
|
||||
static bool acpi_pci_can_wakeup(struct pci_dev *dev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
|
||||
@ -534,6 +555,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
|
||||
static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
|
||||
.is_manageable = acpi_pci_power_manageable,
|
||||
.set_state = acpi_pci_set_power_state,
|
||||
.get_state = acpi_pci_get_power_state,
|
||||
.choose_state = acpi_pci_choose_state,
|
||||
.sleep_wake = acpi_pci_sleep_wake,
|
||||
.run_wake = acpi_pci_run_wake,
|
||||
|
@ -466,7 +466,6 @@ static void pci_device_shutdown(struct device *dev)
|
||||
pci_msi_shutdown(pci_dev);
|
||||
pci_msix_shutdown(pci_dev);
|
||||
|
||||
#ifdef CONFIG_KEXEC_CORE
|
||||
/*
|
||||
* If this is a kexec reboot, turn off Bus Master bit on the
|
||||
* device to tell it to not continue to do DMA. Don't touch
|
||||
@ -476,7 +475,6 @@ static void pci_device_shutdown(struct device *dev)
|
||||
*/
|
||||
if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot))
|
||||
pci_clear_master(pci_dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -684,8 +682,19 @@ static int pci_pm_prepare(struct device *dev)
|
||||
|
||||
static void pci_pm_complete(struct device *dev)
|
||||
{
|
||||
pci_dev_complete_resume(to_pci_dev(dev));
|
||||
pm_complete_with_resume_check(dev);
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
|
||||
pci_dev_complete_resume(pci_dev);
|
||||
pm_generic_complete(dev);
|
||||
|
||||
/* Resume device if platform firmware has put it in reset-power-on */
|
||||
if (dev->power.direct_complete && pm_resume_via_firmware()) {
|
||||
pci_power_t pre_sleep_state = pci_dev->current_state;
|
||||
|
||||
pci_update_current_state(pci_dev, pci_dev->current_state);
|
||||
if (pci_dev->current_state < pre_sleep_state)
|
||||
pm_request_resume(dev);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !CONFIG_PM_SLEEP */
|
||||
|
@ -576,8 +576,9 @@ static const struct pci_platform_pm_ops *pci_platform_pm;
|
||||
|
||||
int pci_set_platform_pm(const struct pci_platform_pm_ops *ops)
|
||||
{
|
||||
if (!ops->is_manageable || !ops->set_state || !ops->choose_state ||
|
||||
!ops->sleep_wake || !ops->run_wake || !ops->need_resume)
|
||||
if (!ops->is_manageable || !ops->set_state || !ops->get_state ||
|
||||
!ops->choose_state || !ops->sleep_wake || !ops->run_wake ||
|
||||
!ops->need_resume)
|
||||
return -EINVAL;
|
||||
pci_platform_pm = ops;
|
||||
return 0;
|
||||
@ -594,6 +595,11 @@ static inline int platform_pci_set_power_state(struct pci_dev *dev,
|
||||
return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS;
|
||||
}
|
||||
|
||||
static inline pci_power_t platform_pci_get_power_state(struct pci_dev *dev)
|
||||
{
|
||||
return pci_platform_pm ? pci_platform_pm->get_state(dev) : PCI_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
|
||||
{
|
||||
return pci_platform_pm ?
|
||||
@ -725,26 +731,25 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_update_current_state - Read PCI power state of given device from its
|
||||
* PCI PM registers and cache it
|
||||
* pci_update_current_state - Read power state of given device and cache it
|
||||
* @dev: PCI device to handle.
|
||||
* @state: State to cache in case the device doesn't have the PM capability
|
||||
*
|
||||
* The power state is read from the PMCSR register, which however is
|
||||
* inaccessible in D3cold. The platform firmware is therefore queried first
|
||||
* to detect accessibility of the register. In case the platform firmware
|
||||
* reports an incorrect state or the device isn't power manageable by the
|
||||
* platform at all, we try to detect D3cold by testing accessibility of the
|
||||
* vendor ID in config space.
|
||||
*/
|
||||
void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
|
||||
{
|
||||
if (dev->pm_cap) {
|
||||
if (platform_pci_get_power_state(dev) == PCI_D3cold ||
|
||||
!pci_device_is_present(dev)) {
|
||||
dev->current_state = PCI_D3cold;
|
||||
} else if (dev->pm_cap) {
|
||||
u16 pmcsr;
|
||||
|
||||
/*
|
||||
* Configuration space is not accessible for device in
|
||||
* D3cold, so just keep or set D3cold for safety
|
||||
*/
|
||||
if (dev->current_state == PCI_D3cold)
|
||||
return;
|
||||
if (state == PCI_D3cold) {
|
||||
dev->current_state = PCI_D3cold;
|
||||
return;
|
||||
}
|
||||
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
|
||||
} else {
|
||||
@ -1983,9 +1988,22 @@ static pci_power_t pci_target_state(struct pci_dev *dev)
|
||||
default:
|
||||
target_state = state;
|
||||
}
|
||||
} else if (!dev->pm_cap) {
|
||||
|
||||
return target_state;
|
||||
}
|
||||
|
||||
if (!dev->pm_cap)
|
||||
target_state = PCI_D0;
|
||||
} else if (device_may_wakeup(&dev->dev)) {
|
||||
|
||||
/*
|
||||
* If the device is in D3cold even though it's not power-manageable by
|
||||
* the platform, it may have been powered down by non-standard means.
|
||||
* Best to let it slumber.
|
||||
*/
|
||||
if (dev->current_state == PCI_D3cold)
|
||||
target_state = PCI_D3cold;
|
||||
|
||||
if (device_may_wakeup(&dev->dev)) {
|
||||
/*
|
||||
* Find the deepest state from which the device can generate
|
||||
* wake-up events, make it the target state and enable device
|
||||
@ -4983,6 +5001,13 @@ static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev)
|
||||
|
||||
spin_lock(&resource_alignment_lock);
|
||||
p = resource_alignment_param;
|
||||
if (!*p)
|
||||
goto out;
|
||||
if (pci_has_flag(PCI_PROBE_ONLY)) {
|
||||
pr_info_once("PCI: Ignoring requested alignments (PCI_PROBE_ONLY)\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
count = 0;
|
||||
if (sscanf(p, "%d%n", &align_order, &count) == 1 &&
|
||||
@ -5047,6 +5072,7 @@ static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev)
|
||||
}
|
||||
p++;
|
||||
}
|
||||
out:
|
||||
spin_unlock(&resource_alignment_lock);
|
||||
return align;
|
||||
}
|
||||
@ -5065,6 +5091,15 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
|
||||
resource_size_t align, size;
|
||||
u16 command;
|
||||
|
||||
/*
|
||||
* VF BARs are read-only zero according to SR-IOV spec r1.1, sec
|
||||
* 3.4.1.11. Their resources are allocated from the space
|
||||
* described by the VF BARx register in the PF's SR-IOV capability.
|
||||
* We can't influence their alignment here.
|
||||
*/
|
||||
if (dev->is_virtfn)
|
||||
return;
|
||||
|
||||
/* check if specified PCI is target device to reassign */
|
||||
align = pci_specified_resource_alignment(dev);
|
||||
if (!align)
|
||||
@ -5087,6 +5122,12 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
|
||||
r = &dev->resource[i];
|
||||
if (!(r->flags & IORESOURCE_MEM))
|
||||
continue;
|
||||
if (r->flags & IORESOURCE_PCI_FIXED) {
|
||||
dev_info(&dev->dev, "Ignoring requested alignment for BAR%d: %pR\n",
|
||||
i, r);
|
||||
continue;
|
||||
}
|
||||
|
||||
size = resource_size(r);
|
||||
if (size < align) {
|
||||
size = align;
|
||||
|
@ -42,6 +42,8 @@ int pci_probe_reset_function(struct pci_dev *dev);
|
||||
*
|
||||
* @set_state: invokes the platform firmware to set the device's power state
|
||||
*
|
||||
* @get_state: queries the platform firmware for a device's current power state
|
||||
*
|
||||
* @choose_state: returns PCI power state of given device preferred by the
|
||||
* platform; to be used during system-wide transitions from a
|
||||
* sleeping state to the working state and vice versa
|
||||
@ -62,6 +64,7 @@ int pci_probe_reset_function(struct pci_dev *dev);
|
||||
struct pci_platform_pm_ops {
|
||||
bool (*is_manageable)(struct pci_dev *dev);
|
||||
int (*set_state)(struct pci_dev *dev, pci_power_t state);
|
||||
pci_power_t (*get_state)(struct pci_dev *dev);
|
||||
pci_power_t (*choose_state)(struct pci_dev *dev);
|
||||
int (*sleep_wake)(struct pci_dev *dev, bool enable);
|
||||
int (*run_wake)(struct pci_dev *dev, bool enable);
|
||||
@ -332,6 +335,12 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
|
||||
|
||||
void pci_enable_acs(struct pci_dev *dev);
|
||||
|
||||
#ifdef CONFIG_PCIE_PTM
|
||||
void pci_ptm_init(struct pci_dev *dev);
|
||||
#else
|
||||
static inline void pci_ptm_init(struct pci_dev *dev) { }
|
||||
#endif
|
||||
|
||||
struct pci_dev_reset_methods {
|
||||
u16 vendor;
|
||||
u16 device;
|
||||
|
@ -92,3 +92,14 @@ config PCIE_DPC
|
||||
will be handled by the DPC driver. If your system doesn't
|
||||
have this capability or you do not want to use this feature,
|
||||
it is safe to answer N.
|
||||
|
||||
config PCIE_PTM
|
||||
bool "PCIe Precision Time Measurement support"
|
||||
default n
|
||||
depends on PCIEPORTBUS
|
||||
help
|
||||
This enables PCI Express Precision Time Measurement (PTM)
|
||||
support.
|
||||
|
||||
This is only useful if you have devices that support PTM, but it
|
||||
is safe to enable even if you don't.
|
||||
|
@ -16,3 +16,4 @@ obj-$(CONFIG_PCIEAER) += aer/
|
||||
obj-$(CONFIG_PCIE_PME) += pme.o
|
||||
|
||||
obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o
|
||||
obj-$(CONFIG_PCIE_PTM) += ptm.o
|
||||
|
@ -15,7 +15,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/sched.h>
|
||||
@ -37,9 +36,6 @@
|
||||
#define DRIVER_VERSION "v1.0"
|
||||
#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
|
||||
#define DRIVER_DESC "Root Port Advanced Error Reporting Driver"
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int aer_probe(struct pcie_device *dev);
|
||||
static void aer_remove(struct pcie_device *dev);
|
||||
@ -70,7 +66,7 @@ static int pcie_aer_disable;
|
||||
|
||||
void pci_no_aer(void)
|
||||
{
|
||||
pcie_aer_disable = 1; /* has priority over 'forceload' */
|
||||
pcie_aer_disable = 1;
|
||||
}
|
||||
|
||||
bool pci_aer_available(void)
|
||||
@ -134,7 +130,7 @@ static void aer_enable_rootport(struct aer_rpc *rpc)
|
||||
pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
|
||||
SYSTEM_ERROR_INTR_ON_MESG_MASK);
|
||||
|
||||
aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
|
||||
aer_pos = pdev->aer_cap;
|
||||
/* Clear error status */
|
||||
pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32);
|
||||
pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
|
||||
@ -173,7 +169,7 @@ static void aer_disable_rootport(struct aer_rpc *rpc)
|
||||
*/
|
||||
set_downstream_devices_error_reporting(pdev, false);
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
|
||||
pos = pdev->aer_cap;
|
||||
/* Disable Root's interrupt in response to error messages */
|
||||
pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32);
|
||||
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
||||
@ -200,7 +196,7 @@ irqreturn_t aer_irq(int irq, void *context)
|
||||
unsigned long flags;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev->port, PCI_EXT_CAP_ID_ERR);
|
||||
pos = pdev->port->aer_cap;
|
||||
/*
|
||||
* Must lock access to Root Error Status Reg, Root Error ID Reg,
|
||||
* and Root error producer/consumer index
|
||||
@ -294,7 +290,6 @@ static void aer_remove(struct pcie_device *dev)
|
||||
/**
|
||||
* aer_probe - initialize resources
|
||||
* @dev: pointer to the pcie_dev data structure
|
||||
* @id: pointer to the service id data structure
|
||||
*
|
||||
* Invoked when PCI Express bus loads AER service driver.
|
||||
*/
|
||||
@ -304,11 +299,6 @@ static int aer_probe(struct pcie_device *dev)
|
||||
struct aer_rpc *rpc;
|
||||
struct device *device = &dev->device;
|
||||
|
||||
/* Init */
|
||||
status = aer_init(dev);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Alloc rpc data structure */
|
||||
rpc = aer_alloc_rpc(dev);
|
||||
if (!rpc) {
|
||||
@ -343,7 +333,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
||||
u32 reg32;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
pos = dev->aer_cap;
|
||||
|
||||
/* Disable Root's interrupt in response to error messages */
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32);
|
||||
@ -396,7 +386,7 @@ static void aer_error_resume(struct pci_dev *dev)
|
||||
pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16);
|
||||
|
||||
/* Clean AER Root Error Status */
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
pos = dev->aer_cap;
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
|
||||
if (dev->error_state == pci_channel_io_normal)
|
||||
@ -417,16 +407,4 @@ static int __init aer_service_init(void)
|
||||
return -ENXIO;
|
||||
return pcie_port_service_register(&aerdriver);
|
||||
}
|
||||
|
||||
/**
|
||||
* aer_service_exit - unregister AER root service driver
|
||||
*
|
||||
* Invoked when AER root service driver is unloaded.
|
||||
*/
|
||||
static void __exit aer_service_exit(void)
|
||||
{
|
||||
pcie_port_service_unregister(&aerdriver);
|
||||
}
|
||||
|
||||
module_init(aer_service_init);
|
||||
module_exit(aer_service_exit);
|
||||
device_initcall(aer_service_init);
|
||||
|
@ -60,6 +60,7 @@ struct aer_rpc {
|
||||
struct pcie_device *rpd; /* Root Port device */
|
||||
struct work_struct dpc_handler;
|
||||
struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX];
|
||||
struct aer_err_info e_info;
|
||||
unsigned short prod_idx; /* Error Producer Index */
|
||||
unsigned short cons_idx; /* Error Consumer Index */
|
||||
int isr;
|
||||
@ -105,7 +106,6 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
|
||||
}
|
||||
|
||||
extern struct bus_type pcie_port_bus_type;
|
||||
int aer_init(struct pcie_device *dev);
|
||||
void aer_isr(struct work_struct *work);
|
||||
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
|
||||
void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info);
|
||||
@ -121,11 +121,4 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void pcie_aer_force_firmware_first(struct pci_dev *pci_dev,
|
||||
int enable)
|
||||
{
|
||||
pci_dev->__aer_firmware_first = !!enable;
|
||||
pci_dev->__aer_firmware_first_valid = 1;
|
||||
}
|
||||
#endif /* _AERDRV_H_ */
|
||||
|
@ -27,11 +27,6 @@
|
||||
#include <linux/kfifo.h>
|
||||
#include "aerdrv.h"
|
||||
|
||||
static bool forceload;
|
||||
static bool nosourceid;
|
||||
module_param(forceload, bool, 0);
|
||||
module_param(nosourceid, bool, 0);
|
||||
|
||||
#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
|
||||
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
|
||||
|
||||
@ -40,7 +35,7 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev)
|
||||
if (pcie_aer_get_firmware_first(dev))
|
||||
return -EIO;
|
||||
|
||||
if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
|
||||
if (!dev->aer_cap)
|
||||
return -EIO;
|
||||
|
||||
return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS);
|
||||
@ -62,7 +57,7 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
|
||||
int pos;
|
||||
u32 status;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
pos = dev->aer_cap;
|
||||
if (!pos)
|
||||
return -EIO;
|
||||
|
||||
@ -83,7 +78,7 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
|
||||
if (!pci_is_pcie(dev))
|
||||
return -ENODEV;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
pos = dev->aer_cap;
|
||||
if (!pos)
|
||||
return -EIO;
|
||||
|
||||
@ -102,6 +97,12 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_aer_init(struct pci_dev *dev)
|
||||
{
|
||||
dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
return pci_cleanup_aer_error_status_regs(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* add_error_device - list device to be handled
|
||||
* @e_info: pointer to error info
|
||||
@ -132,7 +133,8 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
|
||||
* When bus id is equal to 0, it might be a bad id
|
||||
* reported by root port.
|
||||
*/
|
||||
if (!nosourceid && (PCI_BUS_NUM(e_info->id) != 0)) {
|
||||
if ((PCI_BUS_NUM(e_info->id) != 0) &&
|
||||
!(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) {
|
||||
/* Device ID match? */
|
||||
if (e_info->id == ((dev->bus->number << 8) | dev->devfn))
|
||||
return true;
|
||||
@ -144,10 +146,10 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
|
||||
|
||||
/*
|
||||
* When either
|
||||
* 1) nosourceid==y;
|
||||
* 2) bus id is equal to 0. Some ports might lose the bus
|
||||
* 1) bus id is equal to 0. Some ports might lose the bus
|
||||
* id of error source id;
|
||||
* 3) There are multiple errors and prior id comparing fails;
|
||||
* 2) bus flag PCI_BUS_FLAGS_NO_AERSID is set
|
||||
* 3) There are multiple errors and prior ID comparing fails;
|
||||
* We check AER status registers to find possible reporter.
|
||||
*/
|
||||
if (atomic_read(&dev->enable_cnt) == 0)
|
||||
@ -158,7 +160,7 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
|
||||
if (!(reg16 & PCI_EXP_AER_FLAGS))
|
||||
return false;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
pos = dev->aer_cap;
|
||||
if (!pos)
|
||||
return false;
|
||||
|
||||
@ -555,7 +557,7 @@ static void handle_error_source(struct pcie_device *aerdev,
|
||||
* Correctable error does not need software intervention.
|
||||
* No need to go through error recovery process.
|
||||
*/
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
pos = dev->aer_cap;
|
||||
if (pos)
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
|
||||
info->status);
|
||||
@ -647,7 +649,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
|
||||
info->status = 0;
|
||||
info->tlp_header_valid = 0;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
pos = dev->aer_cap;
|
||||
|
||||
/* The device might not support AER */
|
||||
if (!pos)
|
||||
@ -715,15 +717,8 @@ static inline void aer_process_err_devices(struct pcie_device *p_device,
|
||||
static void aer_isr_one_error(struct pcie_device *p_device,
|
||||
struct aer_err_source *e_src)
|
||||
{
|
||||
struct aer_err_info *e_info;
|
||||
|
||||
/* struct aer_err_info might be big, so we allocate it with slab */
|
||||
e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL);
|
||||
if (!e_info) {
|
||||
dev_printk(KERN_DEBUG, &p_device->port->dev,
|
||||
"Can't allocate mem when processing AER errors\n");
|
||||
return;
|
||||
}
|
||||
struct aer_rpc *rpc = get_service_data(p_device);
|
||||
struct aer_err_info *e_info = &rpc->e_info;
|
||||
|
||||
/*
|
||||
* There is a possibility that both correctable error and
|
||||
@ -762,8 +757,6 @@ static void aer_isr_one_error(struct pcie_device *p_device,
|
||||
if (find_source_device(p_device->port, e_info))
|
||||
aer_process_err_devices(p_device, e_info);
|
||||
}
|
||||
|
||||
kfree(e_info);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -812,19 +805,3 @@ void aer_isr(struct work_struct *work)
|
||||
aer_isr_one_error(p_device, &e_src);
|
||||
mutex_unlock(&rpc->rpc_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* aer_init - provide AER initialization
|
||||
* @dev: pointer to AER pcie device
|
||||
*
|
||||
* Invoked when AER service driver is loaded.
|
||||
*/
|
||||
int aer_init(struct pcie_device *dev)
|
||||
{
|
||||
if (forceload) {
|
||||
dev_printk(KERN_DEBUG, &dev->device,
|
||||
"aerdrv forceload requested.\n");
|
||||
pcie_aer_force_firmware_first(dev->port, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -219,15 +219,13 @@ int cper_severity_to_aer(int cper_severity)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cper_severity_to_aer);
|
||||
|
||||
void cper_print_aer(struct pci_dev *dev, int cper_severity,
|
||||
void cper_print_aer(struct pci_dev *dev, int aer_severity,
|
||||
struct aer_capability_regs *aer)
|
||||
{
|
||||
int aer_severity, layer, agent, status_strs_size, tlp_header_valid = 0;
|
||||
int layer, agent, status_strs_size, tlp_header_valid = 0;
|
||||
u32 status, mask;
|
||||
const char **status_strs;
|
||||
|
||||
aer_severity = cper_severity_to_aer(cper_severity);
|
||||
|
||||
if (aer_severity == AER_CORRECTABLE) {
|
||||
status = aer->cor_status;
|
||||
mask = aer->cor_mask;
|
||||
|
@ -1,5 +1,7 @@
|
||||
/*
|
||||
* PCI Express Downstream Port Containment services driver
|
||||
* Author: Keith Busch <keith.busch@intel.com>
|
||||
*
|
||||
* Copyright (C) 2016 Intel Corp.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
@ -9,7 +11,7 @@
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pcieport_if.h>
|
||||
|
||||
@ -143,16 +145,4 @@ static int __init dpc_service_init(void)
|
||||
{
|
||||
return pcie_port_service_register(&dpcdriver);
|
||||
}
|
||||
|
||||
static void __exit dpc_service_exit(void)
|
||||
{
|
||||
pcie_port_service_unregister(&dpcdriver);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("PCI Express Downstream Port Containment driver");
|
||||
MODULE_AUTHOR("Keith Busch <keith.busch@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.1");
|
||||
|
||||
module_init(dpc_service_init);
|
||||
module_exit(dpc_service_exit);
|
||||
device_initcall(dpc_service_init);
|
||||
|
@ -10,7 +10,6 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
@ -449,17 +448,6 @@ static int pcie_pme_resume(struct pcie_device *srv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_pme_remove - Prepare PCIe PME service device for removal.
|
||||
* @srv - PCIe service device to remove.
|
||||
*/
|
||||
static void pcie_pme_remove(struct pcie_device *srv)
|
||||
{
|
||||
pcie_pme_suspend(srv);
|
||||
free_irq(srv->irq, srv);
|
||||
kfree(get_service_data(srv));
|
||||
}
|
||||
|
||||
static struct pcie_port_service_driver pcie_pme_driver = {
|
||||
.name = "pcie_pme",
|
||||
.port_type = PCI_EXP_TYPE_ROOT_PORT,
|
||||
@ -468,7 +456,6 @@ static struct pcie_port_service_driver pcie_pme_driver = {
|
||||
.probe = pcie_pme_probe,
|
||||
.suspend = pcie_pme_suspend,
|
||||
.resume = pcie_pme_resume,
|
||||
.remove = pcie_pme_remove,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -478,5 +465,4 @@ static int __init pcie_pme_service_init(void)
|
||||
{
|
||||
return pcie_port_service_register(&pcie_pme_driver);
|
||||
}
|
||||
|
||||
module_init(pcie_pme_service_init);
|
||||
device_initcall(pcie_pme_service_init);
|
||||
|
@ -1,12 +1,13 @@
|
||||
/*
|
||||
* File: portdrv_pci.c
|
||||
* Purpose: PCI Express Port Bus Driver
|
||||
* Author: Tom Nguyen <tom.l.nguyen@intel.com>
|
||||
* Version: v1.0
|
||||
*
|
||||
* Copyright (C) 2004 Intel
|
||||
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
@ -21,16 +22,6 @@
|
||||
#include "portdrv.h"
|
||||
#include "aer/aerdrv.h"
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.0"
|
||||
#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
|
||||
#define DRIVER_DESC "PCIe Port Bus Driver"
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* If this switch is set, PCIe port native services should not be enabled. */
|
||||
bool pcie_ports_disabled;
|
||||
|
||||
@ -341,7 +332,6 @@ static const struct pci_device_id port_pci_ids[] = { {
|
||||
PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0),
|
||||
}, { /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, port_pci_ids);
|
||||
|
||||
static const struct pci_error_handlers pcie_portdrv_err_handler = {
|
||||
.error_detected = pcie_portdrv_error_detected,
|
||||
@ -406,5 +396,4 @@ static int __init pcie_portdrv_init(void)
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
module_init(pcie_portdrv_init);
|
||||
device_initcall(pcie_portdrv_init);
|
||||
|
142
drivers/pci/pcie/ptm.c
Normal file
142
drivers/pci/pcie/ptm.c
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* PCI Express Precision Time Measurement
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include "../pci.h"
|
||||
|
||||
static void pci_ptm_info(struct pci_dev *dev)
|
||||
{
|
||||
char clock_desc[8];
|
||||
|
||||
switch (dev->ptm_granularity) {
|
||||
case 0:
|
||||
snprintf(clock_desc, sizeof(clock_desc), "unknown");
|
||||
break;
|
||||
case 255:
|
||||
snprintf(clock_desc, sizeof(clock_desc), ">254ns");
|
||||
break;
|
||||
default:
|
||||
snprintf(clock_desc, sizeof(clock_desc), "%udns",
|
||||
dev->ptm_granularity);
|
||||
break;
|
||||
}
|
||||
dev_info(&dev->dev, "PTM enabled%s, %s granularity\n",
|
||||
dev->ptm_root ? " (root)" : "", clock_desc);
|
||||
}
|
||||
|
||||
void pci_ptm_init(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
u32 cap, ctrl;
|
||||
u8 local_clock;
|
||||
struct pci_dev *ups;
|
||||
|
||||
if (!pci_is_pcie(dev))
|
||||
return;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Enable PTM only on interior devices (root ports, switch ports,
|
||||
* etc.) on the assumption that it causes no link traffic until an
|
||||
* endpoint enables it.
|
||||
*/
|
||||
if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT ||
|
||||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END))
|
||||
return;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
|
||||
local_clock = (cap & PCI_PTM_GRANULARITY_MASK) >> 8;
|
||||
|
||||
/*
|
||||
* There's no point in enabling PTM unless it's enabled in the
|
||||
* upstream device or this device can be a PTM Root itself. Per
|
||||
* the spec recommendation (PCIe r3.1, sec 7.32.3), select the
|
||||
* furthest upstream Time Source as the PTM Root.
|
||||
*/
|
||||
ups = pci_upstream_bridge(dev);
|
||||
if (ups && ups->ptm_enabled) {
|
||||
ctrl = PCI_PTM_CTRL_ENABLE;
|
||||
if (ups->ptm_granularity == 0)
|
||||
dev->ptm_granularity = 0;
|
||||
else if (ups->ptm_granularity > local_clock)
|
||||
dev->ptm_granularity = ups->ptm_granularity;
|
||||
} else {
|
||||
if (cap & PCI_PTM_CAP_ROOT) {
|
||||
ctrl = PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT;
|
||||
dev->ptm_root = 1;
|
||||
dev->ptm_granularity = local_clock;
|
||||
} else
|
||||
return;
|
||||
}
|
||||
|
||||
ctrl |= dev->ptm_granularity << 8;
|
||||
pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl);
|
||||
dev->ptm_enabled = 1;
|
||||
|
||||
pci_ptm_info(dev);
|
||||
}
|
||||
|
||||
int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
|
||||
{
|
||||
int pos;
|
||||
u32 cap, ctrl;
|
||||
struct pci_dev *ups;
|
||||
|
||||
if (!pci_is_pcie(dev))
|
||||
return -EINVAL;
|
||||
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
|
||||
if (!pos)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
|
||||
if (!(cap & PCI_PTM_CAP_REQ))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* For a PCIe Endpoint, PTM is only useful if the endpoint can
|
||||
* issue PTM requests to upstream devices that have PTM enabled.
|
||||
*
|
||||
* For Root Complex Integrated Endpoints, there is no upstream
|
||||
* device, so there must be some implementation-specific way to
|
||||
* associate the endpoint with a time source.
|
||||
*/
|
||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_ENDPOINT) {
|
||||
ups = pci_upstream_bridge(dev);
|
||||
if (!ups || !ups->ptm_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
dev->ptm_granularity = ups->ptm_granularity;
|
||||
} else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
|
||||
dev->ptm_granularity = 0;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
ctrl = PCI_PTM_CTRL_ENABLE;
|
||||
ctrl |= dev->ptm_granularity << 8;
|
||||
pci_write_config_dword(dev, pos + PCI_PTM_CTRL, ctrl);
|
||||
dev->ptm_enabled = 1;
|
||||
|
||||
pci_ptm_info(dev);
|
||||
|
||||
if (granularity)
|
||||
*granularity = dev->ptm_granularity;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_enable_ptm);
|
@ -1666,7 +1666,11 @@ static void pci_init_capabilities(struct pci_dev *dev)
|
||||
/* Enable ACS P2P upstream forwarding */
|
||||
pci_enable_acs(dev);
|
||||
|
||||
pci_cleanup_aer_error_status_regs(dev);
|
||||
/* Precision Time Measurement */
|
||||
pci_ptm_init(dev);
|
||||
|
||||
/* Advanced Error Reporting */
|
||||
pci_aer_init(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3198,6 +3198,7 @@ static void quirk_no_bus_reset(struct pci_dev *dev)
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0032, quirk_no_bus_reset);
|
||||
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);
|
||||
|
||||
static void quirk_no_pm_reset(struct pci_dev *dev)
|
||||
{
|
||||
@ -4431,3 +4432,20 @@ static void quirk_intel_qat_vf_cap(struct pci_dev *pdev)
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap);
|
||||
|
||||
/*
|
||||
* VMD-enabled root ports will change the source ID for all messages
|
||||
* to the VMD device. Rather than doing device matching with the source
|
||||
* ID, the AER driver should traverse the child device tree, reading
|
||||
* AER registers to find the faulting device.
|
||||
*/
|
||||
static void quirk_no_aersid(struct pci_dev *pdev)
|
||||
{
|
||||
/* VMD Domain */
|
||||
if (pdev->bus->sysdata && pci_domain_nr(pdev->bus) >= 0x10000)
|
||||
pdev->bus->bus_flags |= PCI_BUS_FLAGS_NO_AERSID;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2030, quirk_no_aersid);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2031, quirk_no_aersid);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2032, quirk_no_aersid);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2033, quirk_no_aersid);
|
||||
|
@ -63,7 +63,7 @@ static inline int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
void cper_print_aer(struct pci_dev *dev, int cper_severity,
|
||||
void cper_print_aer(struct pci_dev *dev, int aer_severity,
|
||||
struct aer_capability_regs *aer);
|
||||
int cper_severity_to_aer(int cper_severity);
|
||||
void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
|
||||
|
@ -187,8 +187,9 @@ enum pci_irq_reroute_variant {
|
||||
|
||||
typedef unsigned short __bitwise pci_bus_flags_t;
|
||||
enum pci_bus_flags {
|
||||
PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1,
|
||||
PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2,
|
||||
PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1,
|
||||
PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2,
|
||||
PCI_BUS_FLAGS_NO_AERSID = (__force pci_bus_flags_t) 4,
|
||||
};
|
||||
|
||||
/* These values come from the PCI Express Spec */
|
||||
@ -268,6 +269,9 @@ struct pci_dev {
|
||||
unsigned int class; /* 3 bytes: (base,sub,prog-if) */
|
||||
u8 revision; /* PCI revision, low byte of class word */
|
||||
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
|
||||
#ifdef CONFIG_PCIEAER
|
||||
u16 aer_cap; /* AER capability offset */
|
||||
#endif
|
||||
u8 pcie_cap; /* PCIe capability offset */
|
||||
u8 msi_cap; /* MSI capability offset */
|
||||
u8 msix_cap; /* MSI-X capability offset */
|
||||
@ -308,6 +312,9 @@ struct pci_dev {
|
||||
powered on/off by the
|
||||
corresponding bridge */
|
||||
unsigned int ignore_hotplug:1; /* Ignore hotplug events */
|
||||
unsigned int hotplug_user_indicators:1; /* SlotCtl indicators
|
||||
controlled exclusively by
|
||||
user sysfs */
|
||||
unsigned int d3_delay; /* D3->D0 transition time in ms */
|
||||
unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */
|
||||
|
||||
@ -367,6 +374,12 @@ struct pci_dev {
|
||||
int rom_attr_enabled; /* has display of the rom attribute been enabled? */
|
||||
struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
|
||||
struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
|
||||
|
||||
#ifdef CONFIG_PCIE_PTM
|
||||
unsigned int ptm_root:1;
|
||||
unsigned int ptm_enabled:1;
|
||||
u8 ptm_granularity;
|
||||
#endif
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
const struct attribute_group **msi_irq_groups;
|
||||
#endif
|
||||
@ -1368,9 +1381,11 @@ static inline bool pcie_aspm_support_enabled(void) { return false; }
|
||||
#ifdef CONFIG_PCIEAER
|
||||
void pci_no_aer(void);
|
||||
bool pci_aer_available(void);
|
||||
int pci_aer_init(struct pci_dev *dev);
|
||||
#else
|
||||
static inline void pci_no_aer(void) { }
|
||||
static inline bool pci_aer_available(void) { return false; }
|
||||
static inline int pci_aer_init(struct pci_dev *d) { return -ENODEV; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIE_ECRC
|
||||
@ -1402,6 +1417,13 @@ static inline void pci_disable_ats(struct pci_dev *d) { }
|
||||
static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIE_PTM
|
||||
int pci_enable_ptm(struct pci_dev *dev, u8 *granularity);
|
||||
#else
|
||||
static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
|
||||
{ return -EINVAL; }
|
||||
#endif
|
||||
|
||||
void pci_cfg_access_lock(struct pci_dev *dev);
|
||||
bool pci_cfg_access_trylock(struct pci_dev *dev);
|
||||
void pci_cfg_access_unlock(struct pci_dev *dev);
|
||||
|
@ -671,7 +671,8 @@
|
||||
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
|
||||
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
|
||||
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
|
||||
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DPC
|
||||
#define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */
|
||||
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
|
||||
|
||||
#define PCI_EXT_CAP_DSN_SIZEOF 12
|
||||
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
|
||||
@ -964,4 +965,13 @@
|
||||
|
||||
#define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */
|
||||
|
||||
/* Precision Time Measurement */
|
||||
#define PCI_PTM_CAP 0x04 /* PTM Capability */
|
||||
#define PCI_PTM_CAP_REQ 0x00000001 /* Requester capable */
|
||||
#define PCI_PTM_CAP_ROOT 0x00000004 /* Root capable */
|
||||
#define PCI_PTM_GRANULARITY_MASK 0x0000FF00 /* Clock granularity */
|
||||
#define PCI_PTM_CTRL 0x08 /* PTM Control */
|
||||
#define PCI_PTM_CTRL_ENABLE 0x00000001 /* PTM enable */
|
||||
#define PCI_PTM_CTRL_ROOT 0x00000002 /* Root select */
|
||||
|
||||
#endif /* LINUX_PCI_REGS_H */
|
||||
|
Loading…
Reference in New Issue
Block a user