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:
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user