pci-v5.1-changes
-----BEGIN PGP SIGNATURE----- iQJIBAABCgAyFiEEgMe7l+5h9hnxdsnuWYigwDrT+vwFAlyCpL0UHGJoZWxnYWFz QGdvb2dsZS5jb20ACgkQWYigwDrT+vzoHw//ZyFbwekF0mV3RZwcV35LkScIOw0d O1DgjJo8UbuV51+/foQeUZ8IzjHlybQhoFdJupPuw+LyaDUkwqjAmdtY8J/FjWSm AJeVzu6gMF0Z9kwwGO4NyqX2EWluTD0xNLgf8g+fe3p1MtEuH6VCrqe+hk3wma0K CrSIKWY/sO408SpAaWiLTEZmVT+hXiP9hJw1qTrbqKLtyWa4oCjErdoyUDsA01+5 gPndKC/3pu6q6q9Dd94582HuQaE2dKHWQXx6Fzd/tdCyYffpbOUAUNP3aRXaTKrS MwKxOF3y7yUnz5RbxRgopwNVf5WyXhCnnPZRLaSxqnTSZCY6FCUi3l6RpVyWu2Ha iztBbkTP/x6WV3VWg810qgQKQ9wl8oALMkoOfR6lWCR7MTuJnMXJtbrz0jWpEC2O ZPwK9fAxFj2/3e13hx88O7Ek8kfajTPM8T15K79pvpljfqa0BD9SrhPyQ5ssmxj4 idz4yIFCATULKszPXA1QbfC1/xCDveQOEPSerL3eACXsLN17vfpOwOT9vWJm6bpr 6u5ggM2dEA07eI1ANnY6twn5g0kSYU9qISNQO98tA86IvaCnME0Z+k+SCwUNIM9U ep9k0NdAGDNsYOfdVEEY0fYGT9k+9f9w8AfZLNvh0N3s7mGQQ35jf0Z75jj/jsor cbMcPAN2jOCyFVs= =vf9L -----END PGP SIGNATURE----- Merge tag 'pci-v5.1-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci Pull PCI updates from Bjorn Helgaas: - Use match_string() instead of reimplementing it (Andy Shevchenko) - Enable SERR# forwarding for all bridges (Bharat Kumar Gogada) - Use Latency Tolerance Reporting if already enabled by platform (Bjorn Helgaas) - Save/restore LTR info for suspend/resume (Bjorn Helgaas) - Fix DPC use of uninitialized data (Dongdong Liu) - Probe bridge window attributes only once at enumeration-time to fix device accesses during rescan (Bjorn Helgaas) - Return BAR size (not "size -1 ") from pci_size() to simplify code (Du Changbin) - Use config header type (not class code) identify bridges more reliably (Honghui Zhang) - Work around Intel Denverton incorrect Trace Hub BAR size reporting (Alexander Shishkin) - Reorder pciehp cached state/hardware state updates to avoid missed interrupts (Mika Westerberg) - Turn ibmphp semaphores into completions or mutexes (Arnd Bergmann) - Mark expected switch fall-through (Mathieu Malaterre) - Use of_node_name_eq() for node name comparisons (Rob Herring) - Add ACS and pciehp quirks for HXT SD4800 (Shunyong Yang) - Consolidate Rohm Vendor ID definitions (Andy Shevchenko) - Use u32 (not __u32) for things not exposed to userspace (Logan Gunthorpe) - Fix locking semantics of bus and slot reset interfaces (Alex Williamson) - Update PCIEPORTBUS Kconfig help text (Hou Zhiqiang) - Allow portdrv to claim subtractive decode Ports so PCIe services will work for them (Honghui Zhang) - Report PCIe links that become degraded at run-time (Alexandru Gagniuc) - Blacklist Gigabyte X299 Root Port power management to fix Thunderbolt hotplug (Mika Westerberg) - Revert runtime PM suspend/resume callbacks that broke PME on network cable plug (Mika Westerberg) - Disable Data Link State Changed interrupts to prevent wakeup immediately after suspend (Mika Westerberg) - Extend altera to support Stratix 10 (Ley Foon Tan) - Allow building altera driver on ARM64 (Ley Foon Tan) - Replace Douglas with Tom Joseph as Cadence PCI host/endpoint maintainer (Lorenzo Pieralisi) - Add DT support for R-Car RZ/G2E (R8A774C0) (Fabrizio Castro) - Add dra72x/dra74x/dra76x SoC compatible strings (Kishon Vijay Abraham I) - Enable x2 mode support for dra72x/dra74x/dra76x SoC (Kishon Vijay Abraham I) - Configure dra7xx PHY to PCIe mode (Kishon Vijay Abraham I) - Simplify dwc (remove unnecessary header includes, name variables consistently, reduce inverted logic, etc) (Gustavo Pimentel) - Add i.MX8MQ support (Andrey Smirnov) - Add message to help debug dwc MSI-X mask bit errors (Gustavo Pimentel) - Work around imx7d PCIe PLL erratum (Trent Piepho) - Don't assert qcom reset GPIO during probe (Bjorn Andersson) - Skip dwc MSI init if MSIs have been disabled (Lucas Stach) - Use memcpy_fromio()/memcpy_toio() instead of plain memcpy() in PCI endpoint framework (Wen Yang) - Add interface to discover supported endpoint features to replace a bitfield that wasn't flexible enough (Kishon Vijay Abraham I) - Implement the new supported-feature interface for designware-plat, dra7xx, rockchip, cadence (Kishon Vijay Abraham I) - Fix issues with 64-bit BAR in endpoints (Kishon Vijay Abraham I) - Add layerscape endpoint mode support (Xiaowei Bao) - Remove duplicate struct hv_vp_set in favor of struct hv_vpset (Maya Nakamura) - Rework hv_irq_unmask() to use cpumask_to_vpset() instead of open-coded reimplementation (Maya Nakamura) - Align Hyper-V struct retarget_msi_interrupt arguments (Maya Nakamura) - Fix mediatek MMIO size computation to enable full size of available MMIO space (Honghui Zhang) - Fix mediatek DMA window size computation to allow endpoint DMA access to full DRAM address range (Honghui Zhang) - Fix mvebu prefetchable BAR regression caused by common bridge emulation that assumed all bridges had prefetchable windows (Thomas Petazzoni) - Make advk_pci_bridge_emul_ops static (Wei Yongjun) - Configure MPS settings for VMD root ports (Jon Derrick) * tag 'pci-v5.1-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (92 commits) PCI: Update PCIEPORTBUS Kconfig help text PCI: Fix "try" semantics of bus and slot reset PCI/LINK: Report degraded links via link bandwidth notification dt-bindings: PCI: altera: Add altr,pcie-root-port-2.0 PCI: altera: Enable driver on ARM64 PCI: altera: Add Stratix 10 PCIe support PCI/PME: Fix possible use-after-free on remove PCI: aardvark: Make symbol 'advk_pci_bridge_emul_ops' static PCI: dwc: skip MSI init if MSIs have been explicitly disabled PCI: hv: Refactor hv_irq_unmask() to use cpumask_to_vpset() PCI: hv: Replace hv_vp_set with hv_vpset PCI: hv: Add __aligned(8) to struct retarget_msi_interrupt PCI: mediatek: Enlarge PCIe2AHB window size to support 4GB DRAM PCI: mediatek: Fix memory mapped IO range size computation PCI: dwc: Remove superfluous shifting in definitions PCI: dwc: Make use of GENMASK/FIELD_PREP PCI: dwc: Make use of BIT() in constant definitions PCI: dwc: Share code for dw_pcie_rd/wr_other_conf() PCI: dwc: Make use of IS_ALIGNED() PCI: imx6: Add code to request/control "pcie_aux" clock for i.MX8MQ ...
This commit is contained in:
commit
2901752c14
@ -1,11 +1,13 @@
|
||||
* Altera PCIe controller
|
||||
|
||||
Required properties:
|
||||
- compatible : should contain "altr,pcie-root-port-1.0"
|
||||
- compatible : should contain "altr,pcie-root-port-1.0" or "altr,pcie-root-port-2.0"
|
||||
- reg: a list of physical base address and length for TXS and CRA.
|
||||
For "altr,pcie-root-port-2.0", additional HIP base address and length.
|
||||
- reg-names: must include the following entries:
|
||||
"Txs": TX slave port region
|
||||
"Cra": Control register access region
|
||||
"Hip": Hard IP region (if "altr,pcie-root-port-2.0")
|
||||
- interrupts: specifies the interrupt source of the parent interrupt
|
||||
controller. The format of the interrupt specifier depends
|
||||
on the parent interrupt controller.
|
||||
|
@ -9,6 +9,7 @@ Required properties:
|
||||
- "fsl,imx6sx-pcie",
|
||||
- "fsl,imx6qp-pcie"
|
||||
- "fsl,imx7d-pcie"
|
||||
- "fsl,imx8mq-pcie"
|
||||
- reg: base address and length of the PCIe controller
|
||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||
entry for each entry in the interrupt-names property.
|
||||
@ -45,7 +46,7 @@ Additional required properties for imx6sx-pcie:
|
||||
PCIE_PHY power domains
|
||||
- power-domain-names: Must be "pcie", "pcie_phy"
|
||||
|
||||
Additional required properties for imx7d-pcie:
|
||||
Additional required properties for imx7d-pcie and imx8mq-pcie:
|
||||
- power-domains: Must be set to a phandle pointing to PCIE_PHY power domain
|
||||
- resets: Must contain phandles to PCIe-related reset lines exposed by SRC
|
||||
IP block
|
||||
@ -53,6 +54,11 @@ Additional required properties for imx7d-pcie:
|
||||
- "pciephy"
|
||||
- "apps"
|
||||
- "turnoff"
|
||||
- fsl,imx7d-pcie-phy: A phandle to an fsl,imx7d-pcie-phy node.
|
||||
|
||||
Additional required properties for imx8mq-pcie:
|
||||
- clock-names: Must include the following additional entries:
|
||||
- "pcie_aux"
|
||||
|
||||
Example:
|
||||
|
||||
@ -79,3 +85,13 @@ Example:
|
||||
clocks = <&clks 144>, <&clks 206>, <&clks 189>;
|
||||
clock-names = "pcie", "pcie_bus", "pcie_phy";
|
||||
};
|
||||
|
||||
* Freescale i.MX7d PCIe PHY
|
||||
|
||||
This is the PHY associated with the IMX7d PCIe controller. It's used by the
|
||||
PCI-e controller via the fsl,imx7d-pcie-phy phandle.
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
- "fsl,imx7d-pcie-phy"
|
||||
- reg: base address and length of the PCIe PHY controller
|
||||
|
@ -13,6 +13,7 @@ information.
|
||||
|
||||
Required properties:
|
||||
- compatible: should contain the platform identifier such as:
|
||||
RC mode:
|
||||
"fsl,ls1021a-pcie"
|
||||
"fsl,ls2080a-pcie", "fsl,ls2085a-pcie"
|
||||
"fsl,ls2088a-pcie"
|
||||
@ -20,6 +21,8 @@ Required properties:
|
||||
"fsl,ls1046a-pcie"
|
||||
"fsl,ls1043a-pcie"
|
||||
"fsl,ls1012a-pcie"
|
||||
EP mode:
|
||||
"fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep"
|
||||
- reg: base addresses and lengths of the PCIe controller register blocks.
|
||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||
entry for each entry in the interrupt-names property.
|
||||
|
@ -3,6 +3,7 @@
|
||||
Required properties:
|
||||
compatible: "renesas,pcie-r8a7743" for the R8A7743 SoC;
|
||||
"renesas,pcie-r8a7744" for the R8A7744 SoC;
|
||||
"renesas,pcie-r8a774c0" for the R8A774C0 SoC;
|
||||
"renesas,pcie-r8a7779" for the R8A7779 SoC;
|
||||
"renesas,pcie-r8a7790" for the R8A7790 SoC;
|
||||
"renesas,pcie-r8a7791" for the R8A7791 SoC;
|
||||
@ -13,7 +14,8 @@ compatible: "renesas,pcie-r8a7743" for the R8A7743 SoC;
|
||||
"renesas,pcie-r8a77990" for the R8A77990 SoC;
|
||||
"renesas,pcie-rcar-gen2" for a generic R-Car Gen2 or
|
||||
RZ/G1 compatible device.
|
||||
"renesas,pcie-rcar-gen3" for a generic R-Car Gen3 compatible device.
|
||||
"renesas,pcie-rcar-gen3" for a generic R-Car Gen3 or
|
||||
RZ/G2 compatible device.
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first
|
||||
|
@ -1,14 +1,21 @@
|
||||
TI PCI Controllers
|
||||
|
||||
PCIe DesignWare Controller
|
||||
- compatible: Should be "ti,dra7-pcie" for RC
|
||||
Should be "ti,dra7-pcie-ep" for EP
|
||||
- compatible: Should be "ti,dra7-pcie" for RC (deprecated)
|
||||
Should be "ti,dra7-pcie-ep" for EP (deprecated)
|
||||
Should be "ti,dra746-pcie-rc" for dra74x/dra76 in RC mode
|
||||
Should be "ti,dra746-pcie-ep" for dra74x/dra76 in EP mode
|
||||
Should be "ti,dra726-pcie-rc" for dra72x in RC mode
|
||||
Should be "ti,dra726-pcie-ep" for dra72x in EP mode
|
||||
- phys : list of PHY specifiers (used by generic PHY framework)
|
||||
- phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the
|
||||
number of PHYs as specified in *phys* property.
|
||||
- ti,hwmods : Name of the hwmod associated to the pcie, "pcie<X>",
|
||||
where <X> is the instance number of the pcie from the HW spec.
|
||||
- num-lanes as specified in ../designware-pcie.txt
|
||||
- ti,syscon-lane-sel : phandle/offset pair. Phandle to the system control
|
||||
module and the register offset to specify lane
|
||||
selection.
|
||||
|
||||
HOST MODE
|
||||
=========
|
||||
|
@ -11830,7 +11830,7 @@ F: Documentation/devicetree/bindings/pci/pci-armada8k.txt
|
||||
F: drivers/pci/controller/dwc/pcie-armada8k.c
|
||||
|
||||
PCI DRIVER FOR CADENCE PCIE IP
|
||||
M: Alan Douglas <adouglas@cadence.com>
|
||||
M: Tom Joseph <tjoseph@cadence.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/pci/cdns,*.txt
|
||||
|
@ -96,6 +96,14 @@
|
||||
};
|
||||
};
|
||||
|
||||
&aips2 {
|
||||
pcie_phy: pcie-phy@306d0000 {
|
||||
compatible = "fsl,imx7d-pcie-phy";
|
||||
reg = <0x306d0000 0x10000>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
&aips3 {
|
||||
usbotg2: usb@30b20000 {
|
||||
compatible = "fsl,imx7d-usb", "fsl,imx27-usb";
|
||||
@ -173,6 +181,7 @@
|
||||
<&src IMX7_RESET_PCIE_CTRL_APPS_EN>,
|
||||
<&src IMX7_RESET_PCIE_CTRL_APPS_TURNOFF>;
|
||||
reset-names = "pciephy", "apps", "turnoff";
|
||||
fsl,imx7d-pcie-phy = <&pcie_phy>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
@ -666,6 +666,17 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie_ep@3400000 {
|
||||
compatible = "fsl,ls1046a-pcie-ep","fsl,ls-pcie-ep";
|
||||
reg = <0x00 0x03400000 0x0 0x00100000
|
||||
0x40 0x00000000 0x8 0x00000000>;
|
||||
reg-names = "regs", "addr_space";
|
||||
num-ib-windows = <6>;
|
||||
num-ob-windows = <8>;
|
||||
num-lanes = <2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie@3500000 {
|
||||
compatible = "fsl,ls1046a-pcie";
|
||||
reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */
|
||||
@ -693,6 +704,17 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie_ep@3500000 {
|
||||
compatible = "fsl,ls1046a-pcie-ep","fsl,ls-pcie-ep";
|
||||
reg = <0x00 0x03500000 0x0 0x00100000
|
||||
0x48 0x00000000 0x8 0x00000000>;
|
||||
reg-names = "regs", "addr_space";
|
||||
num-ib-windows = <6>;
|
||||
num-ob-windows = <8>;
|
||||
num-lanes = <2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie@3600000 {
|
||||
compatible = "fsl,ls1046a-pcie";
|
||||
reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */
|
||||
@ -720,6 +742,17 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie_ep@3600000 {
|
||||
compatible = "fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep";
|
||||
reg = <0x00 0x03600000 0x0 0x00100000
|
||||
0x50 0x00000000 0x8 0x00000000>;
|
||||
reg-names = "regs", "addr_space";
|
||||
num-ib-windows = <6>;
|
||||
num-ob-windows = <8>;
|
||||
num-lanes = <2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
qdma: dma-controller@8380000 {
|
||||
compatible = "fsl,ls1046a-qdma", "fsl,ls1021a-qdma";
|
||||
reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */
|
||||
@ -740,7 +773,6 @@
|
||||
queue-sizes = <64 64>;
|
||||
big-endian;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
reserved-memory {
|
||||
|
@ -96,6 +96,7 @@ void __percpu **hyperv_pcpu_input_arg;
|
||||
EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg);
|
||||
|
||||
u32 hv_max_vp_index;
|
||||
EXPORT_SYMBOL_GPL(hv_max_vp_index);
|
||||
|
||||
static int hv_cpu_init(unsigned int cpu)
|
||||
{
|
||||
|
@ -635,6 +635,22 @@ static void quirk_no_aersid(struct pci_dev *pdev)
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
|
||||
PCI_CLASS_BRIDGE_PCI, 8, quirk_no_aersid);
|
||||
|
||||
static void quirk_intel_th_dnv(struct pci_dev *dev)
|
||||
{
|
||||
struct resource *r = &dev->resource[4];
|
||||
|
||||
/*
|
||||
* Denverton reports 2k of RTIT_BAR (intel_th resource 4), which
|
||||
* appears to be 4 MB in reality.
|
||||
*/
|
||||
if (r->end == r->start + 0x7ff) {
|
||||
r->start = 0;
|
||||
r->end = 0x3fffff;
|
||||
r->flags |= IORESOURCE_UNSET;
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x19e1, quirk_intel_th_dnv);
|
||||
|
||||
#ifdef CONFIG_PHYS_ADDR_T_64BIT
|
||||
|
||||
#define AMD_141b_MMIO_BASE(x) (0x80 + (x) * 0x8)
|
||||
|
@ -972,7 +972,6 @@ static void pch_dma_remove(struct pci_dev *pdev)
|
||||
}
|
||||
|
||||
/* PCI Device ID of DMA device */
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
#define PCI_DEVICE_ID_EG20T_PCH_DMA_8CH 0x8810
|
||||
#define PCI_DEVICE_ID_EG20T_PCH_DMA_4CH 0x8815
|
||||
#define PCI_DEVICE_ID_ML7213_DMA1_8CH 0x8026
|
||||
|
@ -31,8 +31,6 @@
|
||||
|
||||
#define IOH_IRQ_BASE 0
|
||||
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
|
||||
struct ioh_reg_comn {
|
||||
u32 ien;
|
||||
u32 istatus;
|
||||
|
@ -437,7 +437,6 @@ static int __maybe_unused pch_gpio_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
|
||||
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
static const struct pci_device_id pch_gpio_pcidev_id[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014) },
|
||||
|
@ -177,7 +177,6 @@ static wait_queue_head_t pch_event;
|
||||
static DEFINE_MUTEX(pch_mutex);
|
||||
|
||||
/* Definition for ML7213 by LAPIS Semiconductor */
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
#define PCI_DEVICE_ID_ML7213_I2C 0x802D
|
||||
#define PCI_DEVICE_ID_ML7223_I2C 0x8010
|
||||
#define PCI_DEVICE_ID_ML7831_I2C 0x8817
|
||||
|
@ -64,7 +64,6 @@
|
||||
#define CLKCFG_UARTCLKSEL (1 << 18)
|
||||
|
||||
/* Macros for ML7213 */
|
||||
#define PCI_VENDOR_ID_ROHM 0x10db
|
||||
#define PCI_DEVICE_ID_ROHM_ML7213_PHUB 0x801A
|
||||
|
||||
/* Macros for ML7223 */
|
||||
|
@ -788,6 +788,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
|
||||
static const struct pci_device_id pci_endpoint_test_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 0xedda) },
|
||||
{ }
|
||||
};
|
||||
|
@ -27,7 +27,6 @@
|
||||
#define DRV_VERSION "1.01"
|
||||
const char pch_driver_version[] = DRV_VERSION;
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_IOH1_GBE 0x8802 /* Pci device ID */
|
||||
#define PCH_GBE_MAR_ENTRIES 16
|
||||
#define PCH_GBE_SHORT_PKT 64
|
||||
#define DSC_INIT16 0xC000
|
||||
@ -37,11 +36,9 @@ const char pch_driver_version[] = DRV_VERSION;
|
||||
#define PCH_GBE_PCI_BAR 1
|
||||
#define PCH_GBE_RESERVE_MEMORY 0x200000 /* 2MB */
|
||||
|
||||
/* Macros for ML7223 */
|
||||
#define PCI_VENDOR_ID_ROHM 0x10db
|
||||
#define PCI_DEVICE_ID_ROHM_ML7223_GBE 0x8013
|
||||
#define PCI_DEVICE_ID_INTEL_IOH1_GBE 0x8802
|
||||
|
||||
/* Macros for ML7831 */
|
||||
#define PCI_DEVICE_ID_ROHM_ML7223_GBE 0x8013
|
||||
#define PCI_DEVICE_ID_ROHM_ML7831_GBE 0x8802
|
||||
|
||||
#define PCH_GBE_TX_WEIGHT 64
|
||||
|
@ -175,7 +175,7 @@ config PCIE_IPROC_MSI
|
||||
|
||||
config PCIE_ALTERA
|
||||
bool "Altera PCIe controller"
|
||||
depends on ARM || NIOS2 || COMPILE_TEST
|
||||
depends on ARM || NIOS2 || ARM64 || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on Altera
|
||||
FPGA.
|
||||
|
@ -89,8 +89,8 @@ config PCI_EXYNOS
|
||||
select PCIE_DW_HOST
|
||||
|
||||
config PCI_IMX6
|
||||
bool "Freescale i.MX6/7 PCIe controller"
|
||||
depends on SOC_IMX6Q || SOC_IMX7D || (ARM && COMPILE_TEST)
|
||||
bool "Freescale i.MX6/7/8 PCIe controller"
|
||||
depends on SOC_IMX6Q || SOC_IMX7D || (ARM64 && ARCH_MXC) || COMPILE_TEST
|
||||
depends on PCI_MSI_IRQ_DOMAIN
|
||||
select PCIE_DW_HOST
|
||||
|
||||
|
@ -8,7 +8,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
|
||||
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
|
||||
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
|
||||
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
|
||||
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
|
||||
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o
|
||||
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
|
||||
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
|
||||
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
|
||||
|
@ -81,6 +81,10 @@
|
||||
#define MSI_REQ_GRANT BIT(0)
|
||||
#define MSI_VECTOR_SHIFT 7
|
||||
|
||||
#define PCIE_1LANE_2LANE_SELECTION BIT(13)
|
||||
#define PCIE_B1C0_MODE_SEL BIT(2)
|
||||
#define PCIE_B0_B1_TSYNCEN BIT(0)
|
||||
|
||||
struct dra7xx_pcie {
|
||||
struct dw_pcie *pci;
|
||||
void __iomem *base; /* DT ti_conf */
|
||||
@ -93,6 +97,7 @@ struct dra7xx_pcie {
|
||||
|
||||
struct dra7xx_pcie_of_data {
|
||||
enum dw_pcie_device_mode mode;
|
||||
u32 b1co_mode_sel_mask;
|
||||
};
|
||||
|
||||
#define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev)
|
||||
@ -389,9 +394,22 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features dra7xx_pcie_epc_features = {
|
||||
.linkup_notifier = true,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
|
||||
{
|
||||
return &dra7xx_pcie_epc_features;
|
||||
}
|
||||
|
||||
static struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.ep_init = dra7xx_pcie_ep_init,
|
||||
.raise_irq = dra7xx_pcie_raise_irq,
|
||||
.get_features = dra7xx_pcie_get_features,
|
||||
};
|
||||
|
||||
static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,
|
||||
@ -499,6 +517,10 @@ static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < phy_count; i++) {
|
||||
ret = phy_set_mode(dra7xx->phy[i], PHY_MODE_PCIE);
|
||||
if (ret < 0)
|
||||
goto err_phy;
|
||||
|
||||
ret = phy_init(dra7xx->phy[i]);
|
||||
if (ret < 0)
|
||||
goto err_phy;
|
||||
@ -529,6 +551,26 @@ static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = {
|
||||
.mode = DW_PCIE_EP_TYPE,
|
||||
};
|
||||
|
||||
static const struct dra7xx_pcie_of_data dra746_pcie_rc_of_data = {
|
||||
.b1co_mode_sel_mask = BIT(2),
|
||||
.mode = DW_PCIE_RC_TYPE,
|
||||
};
|
||||
|
||||
static const struct dra7xx_pcie_of_data dra726_pcie_rc_of_data = {
|
||||
.b1co_mode_sel_mask = GENMASK(3, 2),
|
||||
.mode = DW_PCIE_RC_TYPE,
|
||||
};
|
||||
|
||||
static const struct dra7xx_pcie_of_data dra746_pcie_ep_of_data = {
|
||||
.b1co_mode_sel_mask = BIT(2),
|
||||
.mode = DW_PCIE_EP_TYPE,
|
||||
};
|
||||
|
||||
static const struct dra7xx_pcie_of_data dra726_pcie_ep_of_data = {
|
||||
.b1co_mode_sel_mask = GENMASK(3, 2),
|
||||
.mode = DW_PCIE_EP_TYPE,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_dra7xx_pcie_match[] = {
|
||||
{
|
||||
.compatible = "ti,dra7-pcie",
|
||||
@ -538,6 +580,22 @@ static const struct of_device_id of_dra7xx_pcie_match[] = {
|
||||
.compatible = "ti,dra7-pcie-ep",
|
||||
.data = &dra7xx_pcie_ep_of_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra746-pcie-rc",
|
||||
.data = &dra746_pcie_rc_of_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra726-pcie-rc",
|
||||
.data = &dra726_pcie_rc_of_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra746-pcie-ep",
|
||||
.data = &dra746_pcie_ep_of_data,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra726-pcie-ep",
|
||||
.data = &dra726_pcie_ep_of_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
@ -583,6 +641,34 @@ static int dra7xx_pcie_unaligned_memaccess(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dra7xx_pcie_configure_two_lane(struct device *dev,
|
||||
u32 b1co_mode_sel_mask)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct regmap *pcie_syscon;
|
||||
unsigned int pcie_reg;
|
||||
u32 mask;
|
||||
u32 val;
|
||||
|
||||
pcie_syscon = syscon_regmap_lookup_by_phandle(np, "ti,syscon-lane-sel");
|
||||
if (IS_ERR(pcie_syscon)) {
|
||||
dev_err(dev, "unable to get ti,syscon-lane-sel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32_index(np, "ti,syscon-lane-sel", 1,
|
||||
&pcie_reg)) {
|
||||
dev_err(dev, "couldn't get lane selection reg offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask = b1co_mode_sel_mask | PCIE_B0_B1_TSYNCEN;
|
||||
val = PCIE_B1C0_MODE_SEL | PCIE_B0_B1_TSYNCEN;
|
||||
regmap_update_bits(pcie_syscon, pcie_reg, mask, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init dra7xx_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
u32 reg;
|
||||
@ -603,6 +689,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *match;
|
||||
const struct dra7xx_pcie_of_data *data;
|
||||
enum dw_pcie_device_mode mode;
|
||||
u32 b1co_mode_sel_mask;
|
||||
|
||||
match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev);
|
||||
if (!match)
|
||||
@ -610,6 +697,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
|
||||
|
||||
data = (struct dra7xx_pcie_of_data *)match->data;
|
||||
mode = (enum dw_pcie_device_mode)data->mode;
|
||||
b1co_mode_sel_mask = data->b1co_mode_sel_mask;
|
||||
|
||||
dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL);
|
||||
if (!dra7xx)
|
||||
@ -665,6 +753,12 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
|
||||
dra7xx->pci = pci;
|
||||
dra7xx->phy_count = phy_count;
|
||||
|
||||
if (phy_count == 2) {
|
||||
ret = dra7xx_pcie_configure_two_lane(dev, b1co_mode_sel_mask);
|
||||
if (ret < 0)
|
||||
dra7xx->phy_count = 1; /* Fallback to x1 lane mode */
|
||||
}
|
||||
|
||||
ret = dra7xx_pcie_enable_phy(dra7xx);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable phy\n");
|
||||
|
@ -8,6 +8,7 @@
|
||||
* Author: Sean Cross <xobs@kosagi.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
@ -18,6 +19,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -32,6 +34,12 @@
|
||||
|
||||
#include "pcie-designware.h"
|
||||
|
||||
#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9)
|
||||
#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10)
|
||||
#define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11)
|
||||
#define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8)
|
||||
#define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000
|
||||
|
||||
#define to_imx6_pcie(x) dev_get_drvdata((x)->dev)
|
||||
|
||||
enum imx6_pcie_variants {
|
||||
@ -39,6 +47,15 @@ enum imx6_pcie_variants {
|
||||
IMX6SX,
|
||||
IMX6QP,
|
||||
IMX7D,
|
||||
IMX8MQ,
|
||||
};
|
||||
|
||||
#define IMX6_PCIE_FLAG_IMX6_PHY BIT(0)
|
||||
#define IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE BIT(1)
|
||||
|
||||
struct imx6_pcie_drvdata {
|
||||
enum imx6_pcie_variants variant;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct imx6_pcie {
|
||||
@ -49,11 +66,12 @@ struct imx6_pcie {
|
||||
struct clk *pcie_phy;
|
||||
struct clk *pcie_inbound_axi;
|
||||
struct clk *pcie;
|
||||
struct clk *pcie_aux;
|
||||
struct regmap *iomuxc_gpr;
|
||||
u32 controller_id;
|
||||
struct reset_control *pciephy_reset;
|
||||
struct reset_control *apps_reset;
|
||||
struct reset_control *turnoff_reset;
|
||||
enum imx6_pcie_variants variant;
|
||||
u32 tx_deemph_gen1;
|
||||
u32 tx_deemph_gen2_3p5db;
|
||||
u32 tx_deemph_gen2_6db;
|
||||
@ -61,11 +79,13 @@ struct imx6_pcie {
|
||||
u32 tx_swing_low;
|
||||
int link_gen;
|
||||
struct regulator *vpcie;
|
||||
void __iomem *phy_base;
|
||||
|
||||
/* power domain for pcie */
|
||||
struct device *pd_pcie;
|
||||
/* power domain for pcie phy */
|
||||
struct device *pd_pcie_phy;
|
||||
const struct imx6_pcie_drvdata *drvdata;
|
||||
};
|
||||
|
||||
/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
|
||||
@ -101,7 +121,6 @@ struct imx6_pcie {
|
||||
#define PCIE_PHY_STAT_ACK_LOC 16
|
||||
|
||||
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
|
||||
#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
|
||||
|
||||
/* PHY registers (not memory-mapped) */
|
||||
#define PCIE_PHY_ATEOVRD 0x10
|
||||
@ -117,6 +136,23 @@ struct imx6_pcie {
|
||||
#define PCIE_PHY_RX_ASIC_OUT 0x100D
|
||||
#define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0)
|
||||
|
||||
/* iMX7 PCIe PHY registers */
|
||||
#define PCIE_PHY_CMN_REG4 0x14
|
||||
/* These are probably the bits that *aren't* DCC_FB_EN */
|
||||
#define PCIE_PHY_CMN_REG4_DCC_FB_EN 0x29
|
||||
|
||||
#define PCIE_PHY_CMN_REG15 0x54
|
||||
#define PCIE_PHY_CMN_REG15_DLY_4 BIT(2)
|
||||
#define PCIE_PHY_CMN_REG15_PLL_PD BIT(5)
|
||||
#define PCIE_PHY_CMN_REG15_OVRD_PLL_PD BIT(7)
|
||||
|
||||
#define PCIE_PHY_CMN_REG24 0x90
|
||||
#define PCIE_PHY_CMN_REG24_RX_EQ BIT(6)
|
||||
#define PCIE_PHY_CMN_REG24_RX_EQ_SEL BIT(3)
|
||||
|
||||
#define PCIE_PHY_CMN_REG26 0x98
|
||||
#define PCIE_PHY_CMN_REG26_ATT_MODE 0xBC
|
||||
|
||||
#define PHY_RX_OVRD_IN_LO 0x1005
|
||||
#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
|
||||
#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
|
||||
@ -251,6 +287,9 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY))
|
||||
return;
|
||||
|
||||
pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
|
||||
tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
|
||||
PHY_RX_OVRD_IN_LO_RX_PLL_EN);
|
||||
@ -264,6 +303,7 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
|
||||
pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
/* Added for PCI abort handling */
|
||||
static int imx6q_pcie_abort_handler(unsigned long addr,
|
||||
unsigned int fsr, struct pt_regs *regs)
|
||||
@ -297,6 +337,7 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int imx6_pcie_attach_pd(struct device *dev)
|
||||
{
|
||||
@ -342,8 +383,9 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
|
||||
{
|
||||
struct device *dev = imx6_pcie->pci->dev;
|
||||
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX7D:
|
||||
case IMX8MQ:
|
||||
reset_control_assert(imx6_pcie->pciephy_reset);
|
||||
reset_control_assert(imx6_pcie->apps_reset);
|
||||
break;
|
||||
@ -378,13 +420,20 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
|
||||
{
|
||||
WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ);
|
||||
return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
|
||||
}
|
||||
|
||||
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
|
||||
{
|
||||
struct dw_pcie *pci = imx6_pcie->pci;
|
||||
struct device *dev = pci->dev;
|
||||
unsigned int offset;
|
||||
int ret = 0;
|
||||
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX6SX:
|
||||
ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
|
||||
if (ret) {
|
||||
@ -412,6 +461,25 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
|
||||
break;
|
||||
case IMX7D:
|
||||
break;
|
||||
case IMX8MQ:
|
||||
ret = clk_prepare_enable(imx6_pcie->pcie_aux);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to enable pcie_aux clock\n");
|
||||
break;
|
||||
}
|
||||
|
||||
offset = imx6_pcie_grp_offset(imx6_pcie);
|
||||
/*
|
||||
* Set the over ride low and enabled
|
||||
* make sure that REF_CLK is turned on.
|
||||
*/
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, offset,
|
||||
IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE,
|
||||
0);
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, offset,
|
||||
IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
|
||||
IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -487,9 +555,32 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
|
||||
!imx6_pcie->gpio_active_high);
|
||||
}
|
||||
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX8MQ:
|
||||
reset_control_deassert(imx6_pcie->pciephy_reset);
|
||||
break;
|
||||
case IMX7D:
|
||||
reset_control_deassert(imx6_pcie->pciephy_reset);
|
||||
|
||||
/* Workaround for ERR010728, failure of PCI-e PLL VCO to
|
||||
* oscillate, especially when cold. This turns off "Duty-cycle
|
||||
* Corrector" and other mysterious undocumented things.
|
||||
*/
|
||||
if (likely(imx6_pcie->phy_base)) {
|
||||
/* De-assert DCC_FB_EN */
|
||||
writel(PCIE_PHY_CMN_REG4_DCC_FB_EN,
|
||||
imx6_pcie->phy_base + PCIE_PHY_CMN_REG4);
|
||||
/* Assert RX_EQS and RX_EQS_SEL */
|
||||
writel(PCIE_PHY_CMN_REG24_RX_EQ_SEL
|
||||
| PCIE_PHY_CMN_REG24_RX_EQ,
|
||||
imx6_pcie->phy_base + PCIE_PHY_CMN_REG24);
|
||||
/* Assert ATT_MODE */
|
||||
writel(PCIE_PHY_CMN_REG26_ATT_MODE,
|
||||
imx6_pcie->phy_base + PCIE_PHY_CMN_REG26);
|
||||
} else {
|
||||
dev_warn(dev, "Unable to apply ERR010728 workaround. DT missing fsl,imx7d-pcie-phy phandle ?\n");
|
||||
}
|
||||
|
||||
imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
|
||||
break;
|
||||
case IMX6SX:
|
||||
@ -523,9 +614,37 @@ err_pcie_phy:
|
||||
}
|
||||
}
|
||||
|
||||
static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie)
|
||||
{
|
||||
unsigned int mask, val;
|
||||
|
||||
if (imx6_pcie->drvdata->variant == IMX8MQ &&
|
||||
imx6_pcie->controller_id == 1) {
|
||||
mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE;
|
||||
val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE,
|
||||
PCI_EXP_TYPE_ROOT_PORT);
|
||||
} else {
|
||||
mask = IMX6Q_GPR12_DEVICE_TYPE;
|
||||
val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE,
|
||||
PCI_EXP_TYPE_ROOT_PORT);
|
||||
}
|
||||
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val);
|
||||
}
|
||||
|
||||
static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
|
||||
{
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX8MQ:
|
||||
/*
|
||||
* TODO: Currently this code assumes external
|
||||
* oscillator is being used
|
||||
*/
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr,
|
||||
imx6_pcie_grp_offset(imx6_pcie),
|
||||
IMX8MQ_GPR_PCIE_REF_USE_PAD,
|
||||
IMX8MQ_GPR_PCIE_REF_USE_PAD);
|
||||
break;
|
||||
case IMX7D:
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0);
|
||||
@ -561,8 +680,7 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
|
||||
break;
|
||||
}
|
||||
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||
IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
|
||||
imx6_pcie_configure_type(imx6_pcie);
|
||||
}
|
||||
|
||||
static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
|
||||
@ -571,6 +689,9 @@ static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
|
||||
int mult, div;
|
||||
u32 val;
|
||||
|
||||
if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY))
|
||||
return 0;
|
||||
|
||||
switch (phy_rate) {
|
||||
case 125000000:
|
||||
/*
|
||||
@ -647,7 +768,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
|
||||
{
|
||||
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
|
||||
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX6Q:
|
||||
case IMX6SX:
|
||||
case IMX6QP:
|
||||
@ -656,6 +777,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
|
||||
IMX6Q_GPR12_PCIE_CTL_2);
|
||||
break;
|
||||
case IMX7D:
|
||||
case IMX8MQ:
|
||||
reset_control_deassert(imx6_pcie->apps_reset);
|
||||
break;
|
||||
}
|
||||
@ -700,7 +822,8 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
|
||||
tmp |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
|
||||
|
||||
if (imx6_pcie->variant != IMX7D) {
|
||||
if (imx6_pcie->drvdata->flags &
|
||||
IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE) {
|
||||
/*
|
||||
* On i.MX7, DIRECT_SPEED_CHANGE behaves differently
|
||||
* from i.MX6 family when no link speed transition
|
||||
@ -797,7 +920,7 @@ static void imx6_pcie_ltssm_disable(struct device *dev)
|
||||
{
|
||||
struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
|
||||
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX6SX:
|
||||
case IMX6QP:
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||
@ -823,7 +946,7 @@ static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)
|
||||
}
|
||||
|
||||
/* Others poke directly at IOMUXC registers */
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX6SX:
|
||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||
IMX6SX_GPR12_PCIE_PM_TURN_OFF,
|
||||
@ -853,7 +976,7 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
|
||||
clk_disable_unprepare(imx6_pcie->pcie_phy);
|
||||
clk_disable_unprepare(imx6_pcie->pcie_bus);
|
||||
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX6SX:
|
||||
clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
|
||||
break;
|
||||
@ -862,6 +985,9 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
|
||||
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
|
||||
IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
|
||||
break;
|
||||
case IMX8MQ:
|
||||
clk_disable_unprepare(imx6_pcie->pcie_aux);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -869,8 +995,8 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
|
||||
|
||||
static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie)
|
||||
{
|
||||
return (imx6_pcie->variant == IMX7D ||
|
||||
imx6_pcie->variant == IMX6SX);
|
||||
return (imx6_pcie->drvdata->variant == IMX7D ||
|
||||
imx6_pcie->drvdata->variant == IMX6SX);
|
||||
}
|
||||
|
||||
static int imx6_pcie_suspend_noirq(struct device *dev)
|
||||
@ -919,6 +1045,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dw_pcie *pci;
|
||||
struct imx6_pcie *imx6_pcie;
|
||||
struct device_node *np;
|
||||
struct resource *dbi_base;
|
||||
struct device_node *node = dev->of_node;
|
||||
int ret;
|
||||
@ -936,8 +1063,24 @@ static int imx6_pcie_probe(struct platform_device *pdev)
|
||||
pci->ops = &dw_pcie_ops;
|
||||
|
||||
imx6_pcie->pci = pci;
|
||||
imx6_pcie->variant =
|
||||
(enum imx6_pcie_variants)of_device_get_match_data(dev);
|
||||
imx6_pcie->drvdata = of_device_get_match_data(dev);
|
||||
|
||||
/* Find the PHY if one is defined, only imx7d uses it */
|
||||
np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0);
|
||||
if (np) {
|
||||
struct resource res;
|
||||
|
||||
ret = of_address_to_resource(np, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to map PCIe PHY\n");
|
||||
return ret;
|
||||
}
|
||||
imx6_pcie->phy_base = devm_ioremap_resource(dev, &res);
|
||||
if (IS_ERR(imx6_pcie->phy_base)) {
|
||||
dev_err(dev, "Unable to map PCIe PHY\n");
|
||||
return PTR_ERR(imx6_pcie->phy_base);
|
||||
}
|
||||
}
|
||||
|
||||
dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
|
||||
@ -981,7 +1124,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(imx6_pcie->pcie);
|
||||
}
|
||||
|
||||
switch (imx6_pcie->variant) {
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX6SX:
|
||||
imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
|
||||
"pcie_inbound_axi");
|
||||
@ -990,7 +1133,17 @@ static int imx6_pcie_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(imx6_pcie->pcie_inbound_axi);
|
||||
}
|
||||
break;
|
||||
case IMX8MQ:
|
||||
imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux");
|
||||
if (IS_ERR(imx6_pcie->pcie_aux)) {
|
||||
dev_err(dev, "pcie_aux clock source missing or invalid\n");
|
||||
return PTR_ERR(imx6_pcie->pcie_aux);
|
||||
}
|
||||
/* fall through */
|
||||
case IMX7D:
|
||||
if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR)
|
||||
imx6_pcie->controller_id = 1;
|
||||
|
||||
imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev,
|
||||
"pciephy");
|
||||
if (IS_ERR(imx6_pcie->pciephy_reset)) {
|
||||
@ -1087,11 +1240,36 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
|
||||
imx6_pcie_assert_core_reset(imx6_pcie);
|
||||
}
|
||||
|
||||
static const struct imx6_pcie_drvdata drvdata[] = {
|
||||
[IMX6Q] = {
|
||||
.variant = IMX6Q,
|
||||
.flags = IMX6_PCIE_FLAG_IMX6_PHY |
|
||||
IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
|
||||
},
|
||||
[IMX6SX] = {
|
||||
.variant = IMX6SX,
|
||||
.flags = IMX6_PCIE_FLAG_IMX6_PHY |
|
||||
IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
|
||||
},
|
||||
[IMX6QP] = {
|
||||
.variant = IMX6QP,
|
||||
.flags = IMX6_PCIE_FLAG_IMX6_PHY |
|
||||
IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE,
|
||||
},
|
||||
[IMX7D] = {
|
||||
.variant = IMX7D,
|
||||
},
|
||||
[IMX8MQ] = {
|
||||
.variant = IMX8MQ,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct of_device_id imx6_pcie_of_match[] = {
|
||||
{ .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, },
|
||||
{ .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
|
||||
{ .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
|
||||
{ .compatible = "fsl,imx7d-pcie", .data = (void *)IMX7D, },
|
||||
{ .compatible = "fsl,imx6q-pcie", .data = &drvdata[IMX6Q], },
|
||||
{ .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], },
|
||||
{ .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], },
|
||||
{ .compatible = "fsl,imx7d-pcie", .data = &drvdata[IMX7D], },
|
||||
{ .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } ,
|
||||
{},
|
||||
};
|
||||
|
||||
@ -1108,6 +1286,7 @@ static struct platform_driver imx6_pcie_driver = {
|
||||
|
||||
static int __init imx6_pcie_init(void)
|
||||
{
|
||||
#ifdef CONFIG_ARM
|
||||
/*
|
||||
* Since probe() can be deferred we need to make sure that
|
||||
* hook_fault_code is not called after __init memory is freed
|
||||
@ -1117,6 +1296,7 @@ static int __init imx6_pcie_init(void)
|
||||
*/
|
||||
hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
|
||||
"external abort on non-linefetch");
|
||||
#endif
|
||||
|
||||
return platform_driver_register(&imx6_pcie_driver);
|
||||
}
|
||||
|
156
drivers/pci/controller/dwc/pci-layerscape-ep.c
Normal file
156
drivers/pci/controller/dwc/pci-layerscape-ep.c
Normal file
@ -0,0 +1,156 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PCIe controller EP driver for Freescale Layerscape SoCs
|
||||
*
|
||||
* Copyright (C) 2018 NXP Semiconductor.
|
||||
*
|
||||
* Author: Xiaowei Bao <xiaowei.bao@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/resource.h>
|
||||
|
||||
#include "pcie-designware.h"
|
||||
|
||||
#define PCIE_DBI2_OFFSET 0x1000 /* DBI2 base address*/
|
||||
|
||||
struct ls_pcie_ep {
|
||||
struct dw_pcie *pci;
|
||||
};
|
||||
|
||||
#define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev)
|
||||
|
||||
static int ls_pcie_establish_link(struct dw_pcie *pci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dw_pcie_ops ls_pcie_ep_ops = {
|
||||
.start_link = ls_pcie_establish_link,
|
||||
};
|
||||
|
||||
static const struct of_device_id ls_pcie_ep_of_match[] = {
|
||||
{ .compatible = "fsl,ls-pcie-ep",},
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct pci_epc_features ls_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
|
||||
{
|
||||
return &ls_pcie_epc_features;
|
||||
}
|
||||
|
||||
static void ls_pcie_ep_init(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
enum pci_barno bar;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++)
|
||||
dw_pcie_ep_reset_bar(pci, bar);
|
||||
}
|
||||
|
||||
static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
enum pci_epc_irq_type type, u16 interrupt_num)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
|
||||
switch (type) {
|
||||
case PCI_EPC_IRQ_LEGACY:
|
||||
return dw_pcie_ep_raise_legacy_irq(ep, func_no);
|
||||
case PCI_EPC_IRQ_MSI:
|
||||
return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
|
||||
case PCI_EPC_IRQ_MSIX:
|
||||
return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
|
||||
default:
|
||||
dev_err(pci->dev, "UNKNOWN IRQ type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.ep_init = ls_pcie_ep_init,
|
||||
.raise_irq = ls_pcie_ep_raise_irq,
|
||||
.get_features = ls_pcie_ep_get_features,
|
||||
};
|
||||
|
||||
static int __init ls_add_pcie_ep(struct ls_pcie_ep *pcie,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct dw_pcie *pci = pcie->pci;
|
||||
struct device *dev = pci->dev;
|
||||
struct dw_pcie_ep *ep;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
ep = &pci->ep;
|
||||
ep->ops = &pcie_ep_ops;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
ep->phys_base = res->start;
|
||||
ep->addr_size = resource_size(res);
|
||||
|
||||
ret = dw_pcie_ep_init(ep);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize endpoint\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init ls_pcie_ep_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dw_pcie *pci;
|
||||
struct ls_pcie_ep *pcie;
|
||||
struct resource *dbi_base;
|
||||
int ret;
|
||||
|
||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
||||
if (!pci)
|
||||
return -ENOMEM;
|
||||
|
||||
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
|
||||
if (IS_ERR(pci->dbi_base))
|
||||
return PTR_ERR(pci->dbi_base);
|
||||
|
||||
pci->dbi_base2 = pci->dbi_base + PCIE_DBI2_OFFSET;
|
||||
pci->dev = dev;
|
||||
pci->ops = &ls_pcie_ep_ops;
|
||||
pcie->pci = pci;
|
||||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
||||
ret = ls_add_pcie_ep(pcie, pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver ls_pcie_ep_driver = {
|
||||
.driver = {
|
||||
.name = "layerscape-pcie-ep",
|
||||
.of_match_table = ls_pcie_ep_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver_probe(ls_pcie_ep_driver, ls_pcie_ep_probe);
|
@ -355,6 +355,17 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
|
||||
return pci->ops->start_link(pci);
|
||||
}
|
||||
|
||||
static const struct pci_epc_features*
|
||||
dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
|
||||
{
|
||||
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
|
||||
if (!ep->ops->get_features)
|
||||
return NULL;
|
||||
|
||||
return ep->ops->get_features(ep);
|
||||
}
|
||||
|
||||
static const struct pci_epc_ops epc_ops = {
|
||||
.write_header = dw_pcie_ep_write_header,
|
||||
.set_bar = dw_pcie_ep_set_bar,
|
||||
@ -368,6 +379,7 @@ static const struct pci_epc_ops epc_ops = {
|
||||
.raise_irq = dw_pcie_ep_raise_irq,
|
||||
.start = dw_pcie_ep_start,
|
||||
.stop = dw_pcie_ep_stop,
|
||||
.get_features = dw_pcie_ep_get_features,
|
||||
};
|
||||
|
||||
int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
|
||||
@ -465,8 +477,10 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
|
||||
iounmap(msix_tbl);
|
||||
|
||||
if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)
|
||||
if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
|
||||
dev_dbg(pci->dev, "MSI-X entry ctrl set\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
|
||||
epc->mem->page_size);
|
||||
|
@ -120,9 +120,9 @@ static void dw_chained_msi_isr(struct irq_desc *desc)
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg)
|
||||
{
|
||||
struct pcie_port *pp = irq_data_get_irq_chip_data(data);
|
||||
struct pcie_port *pp = irq_data_get_irq_chip_data(d);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
u64 msi_target;
|
||||
|
||||
@ -135,61 +135,61 @@ static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
msg->address_hi = upper_32_bits(msi_target);
|
||||
|
||||
if (pp->ops->get_msi_data)
|
||||
msg->data = pp->ops->get_msi_data(pp, data->hwirq);
|
||||
msg->data = pp->ops->get_msi_data(pp, d->hwirq);
|
||||
else
|
||||
msg->data = data->hwirq;
|
||||
msg->data = d->hwirq;
|
||||
|
||||
dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",
|
||||
(int)data->hwirq, msg->address_hi, msg->address_lo);
|
||||
(int)d->hwirq, msg->address_hi, msg->address_lo);
|
||||
}
|
||||
|
||||
static int dw_pci_msi_set_affinity(struct irq_data *irq_data,
|
||||
static int dw_pci_msi_set_affinity(struct irq_data *d,
|
||||
const struct cpumask *mask, bool force)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void dw_pci_bottom_mask(struct irq_data *data)
|
||||
static void dw_pci_bottom_mask(struct irq_data *d)
|
||||
{
|
||||
struct pcie_port *pp = irq_data_get_irq_chip_data(data);
|
||||
struct pcie_port *pp = irq_data_get_irq_chip_data(d);
|
||||
unsigned int res, bit, ctrl;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
|
||||
if (pp->ops->msi_clear_irq) {
|
||||
pp->ops->msi_clear_irq(pp, data->hwirq);
|
||||
pp->ops->msi_clear_irq(pp, d->hwirq);
|
||||
} else {
|
||||
ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL;
|
||||
ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL;
|
||||
res = ctrl * MSI_REG_CTRL_BLOCK_SIZE;
|
||||
bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL;
|
||||
bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL;
|
||||
|
||||
pp->irq_status[ctrl] &= ~(1 << bit);
|
||||
pp->irq_mask[ctrl] |= BIT(bit);
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4,
|
||||
~pp->irq_status[ctrl]);
|
||||
pp->irq_mask[ctrl]);
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
}
|
||||
|
||||
static void dw_pci_bottom_unmask(struct irq_data *data)
|
||||
static void dw_pci_bottom_unmask(struct irq_data *d)
|
||||
{
|
||||
struct pcie_port *pp = irq_data_get_irq_chip_data(data);
|
||||
struct pcie_port *pp = irq_data_get_irq_chip_data(d);
|
||||
unsigned int res, bit, ctrl;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
|
||||
if (pp->ops->msi_set_irq) {
|
||||
pp->ops->msi_set_irq(pp, data->hwirq);
|
||||
pp->ops->msi_set_irq(pp, d->hwirq);
|
||||
} else {
|
||||
ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL;
|
||||
ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL;
|
||||
res = ctrl * MSI_REG_CTRL_BLOCK_SIZE;
|
||||
bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL;
|
||||
bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL;
|
||||
|
||||
pp->irq_status[ctrl] |= 1 << bit;
|
||||
pp->irq_mask[ctrl] &= ~BIT(bit);
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4,
|
||||
~pp->irq_status[ctrl]);
|
||||
pp->irq_mask[ctrl]);
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
@ -207,7 +207,7 @@ static void dw_pci_bottom_ack(struct irq_data *d)
|
||||
|
||||
raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, 1 << bit);
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, BIT(bit));
|
||||
|
||||
if (pp->ops->msi_irq_ack)
|
||||
pp->ops->msi_irq_ack(d->hwirq, pp);
|
||||
@ -255,13 +255,13 @@ static int dw_pcie_irq_domain_alloc(struct irq_domain *domain,
|
||||
static void dw_pcie_irq_domain_free(struct irq_domain *domain,
|
||||
unsigned int virq, unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *data = irq_domain_get_irq_data(domain, virq);
|
||||
struct pcie_port *pp = irq_data_get_irq_chip_data(data);
|
||||
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||
struct pcie_port *pp = irq_data_get_irq_chip_data(d);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
|
||||
bitmap_release_region(pp->msi_irq_in_use, data->hwirq,
|
||||
bitmap_release_region(pp->msi_irq_in_use, d->hwirq,
|
||||
order_base_2(nr_irqs));
|
||||
|
||||
raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
@ -439,7 +439,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
||||
if (ret)
|
||||
pci->num_viewport = 2;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI) && pci_msi_enabled()) {
|
||||
/*
|
||||
* If a specific SoC driver needs to change the
|
||||
* default number of vectors, it needs to implement
|
||||
@ -512,8 +512,9 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
u32 devfn, int where, int size, u32 *val)
|
||||
static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
u32 devfn, int where, int size, u32 *val,
|
||||
bool write)
|
||||
{
|
||||
int ret, type;
|
||||
u32 busdev, cfg_size;
|
||||
@ -521,9 +522,6 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
void __iomem *va_cfg_base;
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
|
||||
if (pp->ops->rd_other_conf)
|
||||
return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val);
|
||||
|
||||
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
|
||||
PCIE_ATU_FUNC(PCI_FUNC(devfn));
|
||||
|
||||
@ -542,7 +540,11 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
|
||||
type, cpu_addr,
|
||||
busdev, cfg_size);
|
||||
ret = dw_pcie_read(va_cfg_base + where, size, val);
|
||||
if (write)
|
||||
ret = dw_pcie_write(va_cfg_base + where, size, *val);
|
||||
else
|
||||
ret = dw_pcie_read(va_cfg_base + where, size, val);
|
||||
|
||||
if (pci->num_viewport <= 2)
|
||||
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
@ -551,43 +553,26 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
u32 devfn, int where, int size, u32 *val)
|
||||
{
|
||||
if (pp->ops->rd_other_conf)
|
||||
return pp->ops->rd_other_conf(pp, bus, devfn, where,
|
||||
size, val);
|
||||
|
||||
return dw_pcie_access_other_conf(pp, bus, devfn, where, size, val,
|
||||
false);
|
||||
}
|
||||
|
||||
static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||
u32 devfn, int where, int size, u32 val)
|
||||
{
|
||||
int ret, type;
|
||||
u32 busdev, cfg_size;
|
||||
u64 cpu_addr;
|
||||
void __iomem *va_cfg_base;
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
|
||||
if (pp->ops->wr_other_conf)
|
||||
return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val);
|
||||
return pp->ops->wr_other_conf(pp, bus, devfn, where,
|
||||
size, val);
|
||||
|
||||
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
|
||||
PCIE_ATU_FUNC(PCI_FUNC(devfn));
|
||||
|
||||
if (bus->parent->number == pp->root_bus_nr) {
|
||||
type = PCIE_ATU_TYPE_CFG0;
|
||||
cpu_addr = pp->cfg0_base;
|
||||
cfg_size = pp->cfg0_size;
|
||||
va_cfg_base = pp->va_cfg0_base;
|
||||
} else {
|
||||
type = PCIE_ATU_TYPE_CFG1;
|
||||
cpu_addr = pp->cfg1_base;
|
||||
cfg_size = pp->cfg1_size;
|
||||
va_cfg_base = pp->va_cfg1_base;
|
||||
}
|
||||
|
||||
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
|
||||
type, cpu_addr,
|
||||
busdev, cfg_size);
|
||||
ret = dw_pcie_write(va_cfg_base + where, size, val);
|
||||
if (pci->num_viewport <= 2)
|
||||
dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
|
||||
PCIE_ATU_TYPE_IO, pp->io_base,
|
||||
pp->io_bus_addr, pp->io_size);
|
||||
|
||||
return ret;
|
||||
return dw_pcie_access_other_conf(pp, bus, devfn, where, size, &val,
|
||||
true);
|
||||
}
|
||||
|
||||
static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus,
|
||||
@ -665,13 +650,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
|
||||
/* Initialize IRQ Status array */
|
||||
for (ctrl = 0; ctrl < num_ctrls; ctrl++) {
|
||||
pp->irq_mask[ctrl] = ~0;
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK +
|
||||
(ctrl * MSI_REG_CTRL_BLOCK_SIZE),
|
||||
4, ~0);
|
||||
4, pp->irq_mask[ctrl]);
|
||||
dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE +
|
||||
(ctrl * MSI_REG_CTRL_BLOCK_SIZE),
|
||||
4, ~0);
|
||||
pp->irq_status[ctrl] = 0;
|
||||
}
|
||||
|
||||
/* Setup RC BARs */
|
||||
|
@ -13,11 +13,9 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
@ -70,14 +68,10 @@ static const struct dw_pcie_ops dw_pcie_ops = {
|
||||
static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
struct pci_epc *epc = ep->epc;
|
||||
enum pci_barno bar;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++)
|
||||
dw_pcie_ep_reset_bar(pci, bar);
|
||||
|
||||
epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
|
||||
epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
|
||||
}
|
||||
|
||||
static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
@ -100,9 +94,22 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features dw_plat_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = true,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
|
||||
{
|
||||
return &dw_plat_pcie_epc_features;
|
||||
}
|
||||
|
||||
static struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.ep_init = dw_plat_pcie_ep_init,
|
||||
.raise_irq = dw_plat_pcie_ep_raise_irq,
|
||||
.get_features = dw_plat_pcie_get_features,
|
||||
};
|
||||
|
||||
static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie,
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
int dw_pcie_read(void __iomem *addr, int size, u32 *val)
|
||||
{
|
||||
if ((uintptr_t)addr & (size - 1)) {
|
||||
if (!IS_ALIGNED((uintptr_t)addr, size)) {
|
||||
*val = 0;
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
}
|
||||
@ -43,7 +43,7 @@ int dw_pcie_read(void __iomem *addr, int size, u32 *val)
|
||||
|
||||
int dw_pcie_write(void __iomem *addr, int size, u32 val)
|
||||
{
|
||||
if ((uintptr_t)addr & (size - 1))
|
||||
if (!IS_ALIGNED((uintptr_t)addr, size))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
if (size == 4)
|
||||
@ -306,7 +306,7 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
|
||||
}
|
||||
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index);
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~PCIE_ATU_ENABLE);
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, (u32)~PCIE_ATU_ENABLE);
|
||||
}
|
||||
|
||||
int dw_pcie_wait_for_link(struct dw_pcie *pci)
|
||||
|
@ -11,6 +11,7 @@
|
||||
#ifndef _PCIE_DESIGNWARE_H
|
||||
#define _PCIE_DESIGNWARE_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/msi.h>
|
||||
@ -30,23 +31,25 @@
|
||||
|
||||
/* 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)
|
||||
#define PORT_LINK_MODE_2_LANES (0x3 << 16)
|
||||
#define PORT_LINK_MODE_4_LANES (0x7 << 16)
|
||||
#define PORT_LINK_MODE_8_LANES (0xf << 16)
|
||||
#define PORT_LINK_MODE_MASK GENMASK(21, 16)
|
||||
#define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n)
|
||||
#define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1)
|
||||
#define PORT_LINK_MODE_2_LANES PORT_LINK_MODE(0x3)
|
||||
#define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7)
|
||||
#define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf)
|
||||
|
||||
#define PCIE_PORT_DEBUG0 0x728
|
||||
#define PORT_LOGIC_LTSSM_STATE_MASK 0x1f
|
||||
#define PORT_LOGIC_LTSSM_STATE_L0 0x11
|
||||
|
||||
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
|
||||
#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
|
||||
#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8)
|
||||
#define PORT_LOGIC_SPEED_CHANGE BIT(17)
|
||||
#define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n)
|
||||
#define PORT_LOGIC_LINK_WIDTH_1_LANES PORT_LOGIC_LINK_WIDTH(0x1)
|
||||
#define PORT_LOGIC_LINK_WIDTH_2_LANES PORT_LOGIC_LINK_WIDTH(0x2)
|
||||
#define PORT_LOGIC_LINK_WIDTH_4_LANES PORT_LOGIC_LINK_WIDTH(0x4)
|
||||
#define PORT_LOGIC_LINK_WIDTH_8_LANES PORT_LOGIC_LINK_WIDTH(0x8)
|
||||
|
||||
#define PCIE_MSI_ADDR_LO 0x820
|
||||
#define PCIE_MSI_ADDR_HI 0x824
|
||||
@ -55,30 +58,30 @@
|
||||
#define PCIE_MSI_INTR0_STATUS 0x830
|
||||
|
||||
#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_REGION_INBOUND BIT(31)
|
||||
#define PCIE_ATU_REGION_OUTBOUND 0
|
||||
#define PCIE_ATU_REGION_INDEX2 0x2
|
||||
#define PCIE_ATU_REGION_INDEX1 0x1
|
||||
#define PCIE_ATU_REGION_INDEX0 0x0
|
||||
#define PCIE_ATU_CR1 0x904
|
||||
#define PCIE_ATU_TYPE_MEM (0x0 << 0)
|
||||
#define PCIE_ATU_TYPE_IO (0x2 << 0)
|
||||
#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
|
||||
#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
|
||||
#define PCIE_ATU_TYPE_MEM 0x0
|
||||
#define PCIE_ATU_TYPE_IO 0x2
|
||||
#define PCIE_ATU_TYPE_CFG0 0x4
|
||||
#define PCIE_ATU_TYPE_CFG1 0x5
|
||||
#define PCIE_ATU_CR2 0x908
|
||||
#define PCIE_ATU_ENABLE (0x1 << 31)
|
||||
#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
|
||||
#define PCIE_ATU_ENABLE BIT(31)
|
||||
#define PCIE_ATU_BAR_MODE_ENABLE BIT(30)
|
||||
#define PCIE_ATU_LOWER_BASE 0x90C
|
||||
#define PCIE_ATU_UPPER_BASE 0x910
|
||||
#define PCIE_ATU_LIMIT 0x914
|
||||
#define PCIE_ATU_LOWER_TARGET 0x918
|
||||
#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
|
||||
#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
|
||||
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
|
||||
#define PCIE_ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x)
|
||||
#define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
|
||||
#define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
#define PCIE_MISC_CONTROL_1_OFF 0x8BC
|
||||
#define PCIE_DBI_RO_WR_EN (0x1 << 0)
|
||||
#define PCIE_DBI_RO_WR_EN BIT(0)
|
||||
|
||||
/*
|
||||
* iATU Unroll-specific register definitions
|
||||
@ -105,7 +108,7 @@
|
||||
((region) << 9)
|
||||
|
||||
#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \
|
||||
(((region) << 9) | (0x1 << 8))
|
||||
(((region) << 9) | BIT(8))
|
||||
|
||||
#define MAX_MSI_IRQS 256
|
||||
#define MAX_MSI_IRQS_PER_CTRL 32
|
||||
@ -177,7 +180,7 @@ struct pcie_port {
|
||||
struct irq_domain *msi_domain;
|
||||
dma_addr_t msi_data;
|
||||
u32 num_vectors;
|
||||
u32 irq_status[MAX_MSI_CTRLS];
|
||||
u32 irq_mask[MAX_MSI_CTRLS];
|
||||
raw_spinlock_t lock;
|
||||
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
|
||||
};
|
||||
@ -192,6 +195,7 @@ struct dw_pcie_ep_ops {
|
||||
void (*ep_init)(struct dw_pcie_ep *ep);
|
||||
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
|
||||
enum pci_epc_irq_type type, u16 interrupt_num);
|
||||
const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
|
||||
};
|
||||
|
||||
struct dw_pcie_ep {
|
||||
|
@ -1228,7 +1228,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
||||
|
||||
pcie->ops = of_device_get_match_data(dev);
|
||||
|
||||
pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
|
||||
pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(pcie->reset)) {
|
||||
ret = PTR_ERR(pcie->reset);
|
||||
goto err_pm_runtime_put;
|
||||
|
@ -466,7 +466,7 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
|
||||
}
|
||||
}
|
||||
|
||||
struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
|
||||
static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
|
||||
.read_pcie = advk_pci_bridge_emul_pcie_conf_read,
|
||||
.write_pcie = advk_pci_bridge_emul_pcie_conf_write,
|
||||
};
|
||||
@ -499,7 +499,7 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie)
|
||||
bridge->data = pcie;
|
||||
bridge->ops = &advk_pci_bridge_emul_ops;
|
||||
|
||||
pci_bridge_emul_init(bridge);
|
||||
pci_bridge_emul_init(bridge, 0);
|
||||
|
||||
}
|
||||
|
||||
|
@ -391,14 +391,6 @@ struct hv_interrupt_entry {
|
||||
u32 data;
|
||||
};
|
||||
|
||||
#define HV_VP_SET_BANK_COUNT_MAX 5 /* current implementation limit */
|
||||
|
||||
struct hv_vp_set {
|
||||
u64 format; /* 0 (HvGenericSetSparse4k) */
|
||||
u64 valid_banks;
|
||||
u64 masks[HV_VP_SET_BANK_COUNT_MAX];
|
||||
};
|
||||
|
||||
/*
|
||||
* flags for hv_device_interrupt_target.flags
|
||||
*/
|
||||
@ -410,7 +402,7 @@ struct hv_device_interrupt_target {
|
||||
u32 flags;
|
||||
union {
|
||||
u64 vp_mask;
|
||||
struct hv_vp_set vp_set;
|
||||
struct hv_vpset vp_set;
|
||||
};
|
||||
};
|
||||
|
||||
@ -420,7 +412,7 @@ struct retarget_msi_interrupt {
|
||||
struct hv_interrupt_entry int_entry;
|
||||
u64 reserved2;
|
||||
struct hv_device_interrupt_target int_target;
|
||||
} __packed;
|
||||
} __packed __aligned(8);
|
||||
|
||||
/*
|
||||
* Driver specific state.
|
||||
@ -460,12 +452,16 @@ struct hv_pcibus_device {
|
||||
struct msi_controller msi_chip;
|
||||
struct irq_domain *irq_domain;
|
||||
|
||||
/* hypercall arg, must not cross page boundary */
|
||||
struct retarget_msi_interrupt retarget_msi_interrupt_params;
|
||||
|
||||
spinlock_t retarget_msi_interrupt_lock;
|
||||
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
/* hypercall arg, must not cross page boundary */
|
||||
struct retarget_msi_interrupt retarget_msi_interrupt_params;
|
||||
|
||||
/*
|
||||
* Don't put anything here: retarget_msi_interrupt_params must be last
|
||||
*/
|
||||
};
|
||||
|
||||
/*
|
||||
@ -910,12 +906,12 @@ static void hv_irq_unmask(struct irq_data *data)
|
||||
struct retarget_msi_interrupt *params;
|
||||
struct hv_pcibus_device *hbus;
|
||||
struct cpumask *dest;
|
||||
cpumask_var_t tmp;
|
||||
struct pci_bus *pbus;
|
||||
struct pci_dev *pdev;
|
||||
unsigned long flags;
|
||||
u32 var_size = 0;
|
||||
int cpu_vmbus;
|
||||
int cpu;
|
||||
int cpu, nr_bank;
|
||||
u64 res;
|
||||
|
||||
dest = irq_data_get_effective_affinity_mask(data);
|
||||
@ -955,28 +951,27 @@ static void hv_irq_unmask(struct irq_data *data)
|
||||
*/
|
||||
params->int_target.flags |=
|
||||
HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET;
|
||||
params->int_target.vp_set.valid_banks =
|
||||
(1ull << HV_VP_SET_BANK_COUNT_MAX) - 1;
|
||||
|
||||
if (!alloc_cpumask_var(&tmp, GFP_ATOMIC)) {
|
||||
res = 1;
|
||||
goto exit_unlock;
|
||||
}
|
||||
|
||||
cpumask_and(tmp, dest, cpu_online_mask);
|
||||
nr_bank = cpumask_to_vpset(¶ms->int_target.vp_set, tmp);
|
||||
free_cpumask_var(tmp);
|
||||
|
||||
if (nr_bank <= 0) {
|
||||
res = 1;
|
||||
goto exit_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* var-sized hypercall, var-size starts after vp_mask (thus
|
||||
* vp_set.format does not count, but vp_set.valid_banks does).
|
||||
* vp_set.format does not count, but vp_set.valid_bank_mask
|
||||
* does).
|
||||
*/
|
||||
var_size = 1 + HV_VP_SET_BANK_COUNT_MAX;
|
||||
|
||||
for_each_cpu_and(cpu, dest, cpu_online_mask) {
|
||||
cpu_vmbus = hv_cpu_number_to_vp_number(cpu);
|
||||
|
||||
if (cpu_vmbus >= HV_VP_SET_BANK_COUNT_MAX * 64) {
|
||||
dev_err(&hbus->hdev->device,
|
||||
"too high CPU %d", cpu_vmbus);
|
||||
res = 1;
|
||||
goto exit_unlock;
|
||||
}
|
||||
|
||||
params->int_target.vp_set.masks[cpu_vmbus / 64] |=
|
||||
(1ULL << (cpu_vmbus & 63));
|
||||
}
|
||||
var_size = 1 + nr_bank;
|
||||
} else {
|
||||
for_each_cpu_and(cpu, dest, cpu_online_mask) {
|
||||
params->int_target.vp_mask |=
|
||||
|
@ -583,7 +583,7 @@ static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
|
||||
bridge->data = port;
|
||||
bridge->ops = &mvebu_pci_bridge_emul_ops;
|
||||
|
||||
pci_bridge_emul_init(bridge);
|
||||
pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR);
|
||||
}
|
||||
|
||||
static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
@ -37,7 +38,12 @@
|
||||
#define RP_LTSSM_MASK 0x1f
|
||||
#define LTSSM_L0 0xf
|
||||
|
||||
#define PCIE_CAP_OFFSET 0x80
|
||||
#define S10_RP_TX_CNTRL 0x2004
|
||||
#define S10_RP_RXCPL_REG 0x2008
|
||||
#define S10_RP_RXCPL_STATUS 0x200C
|
||||
#define S10_RP_CFG_ADDR(pcie, reg) \
|
||||
(((pcie)->hip_base) + (reg) + (1 << 20))
|
||||
|
||||
/* TLP configuration type 0 and 1 */
|
||||
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
|
||||
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
|
||||
@ -49,18 +55,19 @@
|
||||
#define RP_DEVFN 0
|
||||
#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
|
||||
#define TLP_CFGRD_DW0(pcie, bus) \
|
||||
((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \
|
||||
: TLP_FMTTYPE_CFGRD1) << 24) | \
|
||||
TLP_PAYLOAD_SIZE)
|
||||
((((bus == pcie->root_bus_nr) ? pcie->pcie_data->cfgrd0 \
|
||||
: pcie->pcie_data->cfgrd1) << 24) | \
|
||||
TLP_PAYLOAD_SIZE)
|
||||
#define TLP_CFGWR_DW0(pcie, bus) \
|
||||
((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGWR0 \
|
||||
: TLP_FMTTYPE_CFGWR1) << 24) | \
|
||||
TLP_PAYLOAD_SIZE)
|
||||
((((bus == pcie->root_bus_nr) ? pcie->pcie_data->cfgwr0 \
|
||||
: pcie->pcie_data->cfgwr1) << 24) | \
|
||||
TLP_PAYLOAD_SIZE)
|
||||
#define TLP_CFG_DW1(pcie, tag, be) \
|
||||
(((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be))
|
||||
(((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be))
|
||||
#define TLP_CFG_DW2(bus, devfn, offset) \
|
||||
(((bus) << 24) | ((devfn) << 16) | (offset))
|
||||
#define TLP_COMP_STATUS(s) (((s) >> 13) & 7)
|
||||
#define TLP_BYTE_COUNT(s) (((s) >> 0) & 0xfff)
|
||||
#define TLP_HDR_SIZE 3
|
||||
#define TLP_LOOP 500
|
||||
|
||||
@ -69,14 +76,47 @@
|
||||
|
||||
#define DWORD_MASK 3
|
||||
|
||||
#define S10_TLP_FMTTYPE_CFGRD0 0x05
|
||||
#define S10_TLP_FMTTYPE_CFGRD1 0x04
|
||||
#define S10_TLP_FMTTYPE_CFGWR0 0x45
|
||||
#define S10_TLP_FMTTYPE_CFGWR1 0x44
|
||||
|
||||
enum altera_pcie_version {
|
||||
ALTERA_PCIE_V1 = 0,
|
||||
ALTERA_PCIE_V2,
|
||||
};
|
||||
|
||||
struct altera_pcie {
|
||||
struct platform_device *pdev;
|
||||
void __iomem *cra_base; /* DT Cra */
|
||||
void __iomem *cra_base;
|
||||
void __iomem *hip_base;
|
||||
int irq;
|
||||
u8 root_bus_nr;
|
||||
struct irq_domain *irq_domain;
|
||||
struct resource bus_range;
|
||||
struct list_head resources;
|
||||
const struct altera_pcie_data *pcie_data;
|
||||
};
|
||||
|
||||
struct altera_pcie_ops {
|
||||
int (*tlp_read_pkt)(struct altera_pcie *pcie, u32 *value);
|
||||
void (*tlp_write_pkt)(struct altera_pcie *pcie, u32 *headers,
|
||||
u32 data, bool align);
|
||||
bool (*get_link_status)(struct altera_pcie *pcie);
|
||||
int (*rp_read_cfg)(struct altera_pcie *pcie, int where,
|
||||
int size, u32 *value);
|
||||
int (*rp_write_cfg)(struct altera_pcie *pcie, u8 busno,
|
||||
int where, int size, u32 value);
|
||||
};
|
||||
|
||||
struct altera_pcie_data {
|
||||
const struct altera_pcie_ops *ops;
|
||||
enum altera_pcie_version version;
|
||||
u32 cap_offset; /* PCIe capability structure register offset */
|
||||
u32 cfgrd0;
|
||||
u32 cfgrd1;
|
||||
u32 cfgwr0;
|
||||
u32 cfgwr1;
|
||||
};
|
||||
|
||||
struct tlp_rp_regpair_t {
|
||||
@ -101,6 +141,15 @@ static bool altera_pcie_link_up(struct altera_pcie *pcie)
|
||||
return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0);
|
||||
}
|
||||
|
||||
static bool s10_altera_pcie_link_up(struct altera_pcie *pcie)
|
||||
{
|
||||
void __iomem *addr = S10_RP_CFG_ADDR(pcie,
|
||||
pcie->pcie_data->cap_offset +
|
||||
PCI_EXP_LNKSTA);
|
||||
|
||||
return !!(readw(addr) & PCI_EXP_LNKSTA_DLLLA);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -128,12 +177,18 @@ static void tlp_write_tx(struct altera_pcie *pcie,
|
||||
cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL);
|
||||
}
|
||||
|
||||
static void s10_tlp_write_tx(struct altera_pcie *pcie, u32 reg0, u32 ctrl)
|
||||
{
|
||||
cra_writel(pcie, reg0, RP_TX_REG0);
|
||||
cra_writel(pcie, ctrl, S10_RP_TX_CNTRL);
|
||||
}
|
||||
|
||||
static bool altera_pcie_valid_device(struct altera_pcie *pcie,
|
||||
struct pci_bus *bus, int dev)
|
||||
{
|
||||
/* If there is no link, then there is no device */
|
||||
if (bus->number != pcie->root_bus_nr) {
|
||||
if (!altera_pcie_link_up(pcie))
|
||||
if (!pcie->pcie_data->ops->get_link_status(pcie))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -183,6 +238,53 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int s10_tlp_read_packet(struct altera_pcie *pcie, u32 *value)
|
||||
{
|
||||
u32 ctrl;
|
||||
u32 comp_status;
|
||||
u32 dw[4];
|
||||
u32 count;
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
|
||||
for (count = 0; count < TLP_LOOP; count++) {
|
||||
ctrl = cra_readl(pcie, S10_RP_RXCPL_STATUS);
|
||||
if (ctrl & RP_RXCPL_SOP) {
|
||||
/* Read first DW */
|
||||
dw[0] = cra_readl(pcie, S10_RP_RXCPL_REG);
|
||||
break;
|
||||
}
|
||||
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
/* SOP detection failed, return error */
|
||||
if (count == TLP_LOOP)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
count = 1;
|
||||
|
||||
/* Poll for EOP */
|
||||
while (count < ARRAY_SIZE(dw)) {
|
||||
ctrl = cra_readl(pcie, S10_RP_RXCPL_STATUS);
|
||||
dw[count++] = cra_readl(pcie, S10_RP_RXCPL_REG);
|
||||
if (ctrl & RP_RXCPL_EOP) {
|
||||
comp_status = TLP_COMP_STATUS(dw[1]);
|
||||
if (comp_status)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
if (value && TLP_BYTE_COUNT(dw[1]) == sizeof(u32) &&
|
||||
count == 4)
|
||||
*value = dw[3];
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
dev_warn(dev, "Malformed TLP packet\n");
|
||||
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
|
||||
u32 data, bool align)
|
||||
{
|
||||
@ -210,6 +312,15 @@ static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
|
||||
tlp_write_tx(pcie, &tlp_rp_regdata);
|
||||
}
|
||||
|
||||
static void s10_tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
|
||||
u32 data, bool dummy)
|
||||
{
|
||||
s10_tlp_write_tx(pcie, headers[0], RP_TX_SOP);
|
||||
s10_tlp_write_tx(pcie, headers[1], 0);
|
||||
s10_tlp_write_tx(pcie, headers[2], 0);
|
||||
s10_tlp_write_tx(pcie, data, RP_TX_EOP);
|
||||
}
|
||||
|
||||
static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
||||
int where, u8 byte_en, u32 *value)
|
||||
{
|
||||
@ -219,9 +330,9 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
||||
headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en);
|
||||
headers[2] = TLP_CFG_DW2(bus, devfn, where);
|
||||
|
||||
tlp_write_packet(pcie, headers, 0, false);
|
||||
pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, 0, false);
|
||||
|
||||
return tlp_read_packet(pcie, value);
|
||||
return pcie->pcie_data->ops->tlp_read_pkt(pcie, value);
|
||||
}
|
||||
|
||||
static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
||||
@ -236,11 +347,13 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
||||
|
||||
/* check alignment to Qword */
|
||||
if ((where & 0x7) == 0)
|
||||
tlp_write_packet(pcie, headers, value, true);
|
||||
pcie->pcie_data->ops->tlp_write_pkt(pcie, headers,
|
||||
value, true);
|
||||
else
|
||||
tlp_write_packet(pcie, headers, value, false);
|
||||
pcie->pcie_data->ops->tlp_write_pkt(pcie, headers,
|
||||
value, false);
|
||||
|
||||
ret = tlp_read_packet(pcie, NULL);
|
||||
ret = pcie->pcie_data->ops->tlp_read_pkt(pcie, NULL);
|
||||
if (ret != PCIBIOS_SUCCESSFUL)
|
||||
return ret;
|
||||
|
||||
@ -254,6 +367,53 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int s10_rp_read_cfg(struct altera_pcie *pcie, int where,
|
||||
int size, u32 *value)
|
||||
{
|
||||
void __iomem *addr = S10_RP_CFG_ADDR(pcie, where);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
*value = readb(addr);
|
||||
break;
|
||||
case 2:
|
||||
*value = readw(addr);
|
||||
break;
|
||||
default:
|
||||
*value = readl(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int s10_rp_write_cfg(struct altera_pcie *pcie, u8 busno,
|
||||
int where, int size, u32 value)
|
||||
{
|
||||
void __iomem *addr = S10_RP_CFG_ADDR(pcie, where);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
writeb(value, addr);
|
||||
break;
|
||||
case 2:
|
||||
writew(value, addr);
|
||||
break;
|
||||
default:
|
||||
writel(value, addr);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Monitor changes to PCI_PRIMARY_BUS register on root port
|
||||
* and update local copy of root bus number accordingly.
|
||||
*/
|
||||
if (busno == pcie->root_bus_nr && where == PCI_PRIMARY_BUS)
|
||||
pcie->root_bus_nr = value & 0xff;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno,
|
||||
unsigned int devfn, int where, int size,
|
||||
u32 *value)
|
||||
@ -262,6 +422,10 @@ static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno,
|
||||
u32 data;
|
||||
u8 byte_en;
|
||||
|
||||
if (busno == pcie->root_bus_nr && pcie->pcie_data->ops->rp_read_cfg)
|
||||
return pcie->pcie_data->ops->rp_read_cfg(pcie, where,
|
||||
size, value);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
byte_en = 1 << (where & 3);
|
||||
@ -302,6 +466,10 @@ static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno,
|
||||
u32 shift = 8 * (where & 3);
|
||||
u8 byte_en;
|
||||
|
||||
if (busno == pcie->root_bus_nr && pcie->pcie_data->ops->rp_write_cfg)
|
||||
return pcie->pcie_data->ops->rp_write_cfg(pcie, busno,
|
||||
where, size, value);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
data32 = (value & 0xff) << shift;
|
||||
@ -365,7 +533,8 @@ static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno,
|
||||
int ret;
|
||||
|
||||
ret = _altera_pcie_cfg_read(pcie, busno, devfn,
|
||||
PCIE_CAP_OFFSET + offset, sizeof(*value),
|
||||
pcie->pcie_data->cap_offset + offset,
|
||||
sizeof(*value),
|
||||
&data);
|
||||
*value = data;
|
||||
return ret;
|
||||
@ -375,7 +544,8 @@ 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),
|
||||
pcie->pcie_data->cap_offset + offset,
|
||||
sizeof(value),
|
||||
value);
|
||||
}
|
||||
|
||||
@ -403,7 +573,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie)
|
||||
/* Wait for link is up */
|
||||
start_jiffies = jiffies;
|
||||
for (;;) {
|
||||
if (altera_pcie_link_up(pcie))
|
||||
if (pcie->pcie_data->ops->get_link_status(pcie))
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) {
|
||||
@ -418,7 +588,7 @@ static void altera_pcie_retrain(struct altera_pcie *pcie)
|
||||
{
|
||||
u16 linkcap, linkstat, linkctl;
|
||||
|
||||
if (!altera_pcie_link_up(pcie))
|
||||
if (!pcie->pcie_data->ops->get_link_status(pcie))
|
||||
return;
|
||||
|
||||
/*
|
||||
@ -540,12 +710,20 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie)
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
struct platform_device *pdev = pcie->pdev;
|
||||
struct resource *cra;
|
||||
struct resource *hip;
|
||||
|
||||
cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
|
||||
pcie->cra_base = devm_ioremap_resource(dev, cra);
|
||||
if (IS_ERR(pcie->cra_base))
|
||||
return PTR_ERR(pcie->cra_base);
|
||||
|
||||
if (pcie->pcie_data->version == ALTERA_PCIE_V2) {
|
||||
hip = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Hip");
|
||||
pcie->hip_base = devm_ioremap_resource(&pdev->dev, hip);
|
||||
if (IS_ERR(pcie->hip_base))
|
||||
return PTR_ERR(pcie->hip_base);
|
||||
}
|
||||
|
||||
/* setup IRQ */
|
||||
pcie->irq = platform_get_irq(pdev, 0);
|
||||
if (pcie->irq < 0) {
|
||||
@ -562,6 +740,48 @@ static void altera_pcie_host_init(struct altera_pcie *pcie)
|
||||
altera_pcie_retrain(pcie);
|
||||
}
|
||||
|
||||
static const struct altera_pcie_ops altera_pcie_ops_1_0 = {
|
||||
.tlp_read_pkt = tlp_read_packet,
|
||||
.tlp_write_pkt = tlp_write_packet,
|
||||
.get_link_status = altera_pcie_link_up,
|
||||
};
|
||||
|
||||
static const struct altera_pcie_ops altera_pcie_ops_2_0 = {
|
||||
.tlp_read_pkt = s10_tlp_read_packet,
|
||||
.tlp_write_pkt = s10_tlp_write_packet,
|
||||
.get_link_status = s10_altera_pcie_link_up,
|
||||
.rp_read_cfg = s10_rp_read_cfg,
|
||||
.rp_write_cfg = s10_rp_write_cfg,
|
||||
};
|
||||
|
||||
static const struct altera_pcie_data altera_pcie_1_0_data = {
|
||||
.ops = &altera_pcie_ops_1_0,
|
||||
.cap_offset = 0x80,
|
||||
.version = ALTERA_PCIE_V1,
|
||||
.cfgrd0 = TLP_FMTTYPE_CFGRD0,
|
||||
.cfgrd1 = TLP_FMTTYPE_CFGRD1,
|
||||
.cfgwr0 = TLP_FMTTYPE_CFGWR0,
|
||||
.cfgwr1 = TLP_FMTTYPE_CFGWR1,
|
||||
};
|
||||
|
||||
static const struct altera_pcie_data altera_pcie_2_0_data = {
|
||||
.ops = &altera_pcie_ops_2_0,
|
||||
.version = ALTERA_PCIE_V2,
|
||||
.cap_offset = 0x70,
|
||||
.cfgrd0 = S10_TLP_FMTTYPE_CFGRD0,
|
||||
.cfgrd1 = S10_TLP_FMTTYPE_CFGRD1,
|
||||
.cfgwr0 = S10_TLP_FMTTYPE_CFGWR0,
|
||||
.cfgwr1 = S10_TLP_FMTTYPE_CFGWR1,
|
||||
};
|
||||
|
||||
static const struct of_device_id altera_pcie_of_match[] = {
|
||||
{.compatible = "altr,pcie-root-port-1.0",
|
||||
.data = &altera_pcie_1_0_data },
|
||||
{.compatible = "altr,pcie-root-port-2.0",
|
||||
.data = &altera_pcie_2_0_data },
|
||||
{},
|
||||
};
|
||||
|
||||
static int altera_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -570,6 +790,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
|
||||
struct pci_bus *child;
|
||||
struct pci_host_bridge *bridge;
|
||||
int ret;
|
||||
const struct of_device_id *match;
|
||||
|
||||
bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
|
||||
if (!bridge)
|
||||
@ -578,6 +799,12 @@ static int altera_pcie_probe(struct platform_device *pdev)
|
||||
pcie = pci_host_bridge_priv(bridge);
|
||||
pcie->pdev = pdev;
|
||||
|
||||
match = of_match_device(altera_pcie_of_match, &pdev->dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
pcie->pcie_data = match->data;
|
||||
|
||||
ret = altera_pcie_parse_dt(pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Parsing DT failed\n");
|
||||
@ -628,11 +855,6 @@ static int altera_pcie_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id altera_pcie_of_match[] = {
|
||||
{ .compatible = "altr,pcie-root-port-1.0", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver altera_pcie_driver = {
|
||||
.probe = altera_pcie_probe,
|
||||
.driver = {
|
||||
|
@ -396,21 +396,21 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
|
||||
cfg |= BIT(epf->func_no);
|
||||
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
|
||||
|
||||
/*
|
||||
* The PCIe links are automatically established by the controller
|
||||
* once for all at powerup: the software can neither start nor stop
|
||||
* those links later at runtime.
|
||||
*
|
||||
* Then we only have to notify the EP core that our links are already
|
||||
* established. However we don't call directly pci_epc_linkup() because
|
||||
* we've already locked the epc->lock.
|
||||
*/
|
||||
list_for_each_entry(epf, &epc->pci_epf, list)
|
||||
pci_epf_linkup(epf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features cdns_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
|
||||
{
|
||||
return &cdns_pcie_epc_features;
|
||||
}
|
||||
|
||||
static const struct pci_epc_ops cdns_pcie_epc_ops = {
|
||||
.write_header = cdns_pcie_ep_write_header,
|
||||
.set_bar = cdns_pcie_ep_set_bar,
|
||||
@ -421,6 +421,7 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
|
||||
.get_msi = cdns_pcie_ep_get_msi,
|
||||
.raise_irq = cdns_pcie_ep_raise_irq,
|
||||
.start = cdns_pcie_ep_start,
|
||||
.get_features = cdns_pcie_ep_get_features,
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_pcie_ep_of_match[] = {
|
||||
|
@ -90,6 +90,12 @@
|
||||
#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
|
||||
#define PCIE_AXI_WINDOW0 0x448
|
||||
#define WIN_ENABLE BIT(7)
|
||||
/*
|
||||
* Define PCIe to AHB window size as 2^33 to support max 8GB address space
|
||||
* translate, support least 4GB DRAM size access from EP DMA(physical DRAM
|
||||
* start from 0x40000000).
|
||||
*/
|
||||
#define PCIE2AHB_SIZE 0x21
|
||||
|
||||
/* PCIe V2 configuration transaction header */
|
||||
#define PCIE_CFG_HEADER0 0x460
|
||||
@ -654,7 +660,6 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
|
||||
struct resource *mem = &pcie->mem;
|
||||
const struct mtk_pcie_soc *soc = port->pcie->soc;
|
||||
u32 val;
|
||||
size_t size;
|
||||
int err;
|
||||
|
||||
/* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */
|
||||
@ -706,15 +711,15 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
|
||||
mtk_pcie_enable_msi(port);
|
||||
|
||||
/* Set AHB to PCIe translation windows */
|
||||
size = mem->end - mem->start;
|
||||
val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size));
|
||||
val = lower_32_bits(mem->start) |
|
||||
AHB2PCIE_SIZE(fls(resource_size(mem)));
|
||||
writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
|
||||
|
||||
val = upper_32_bits(mem->start);
|
||||
writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
|
||||
|
||||
/* Set PCIe to AXI translation memory space.*/
|
||||
val = fls(0xffffffff) | WIN_ENABLE;
|
||||
val = PCIE2AHB_SIZE | WIN_ENABLE;
|
||||
writel(val, port->base + PCIE_AXI_WINDOW0);
|
||||
|
||||
return 0;
|
||||
|
@ -499,12 +499,21 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc)
|
||||
|
||||
rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG);
|
||||
|
||||
list_for_each_entry(epf, &epc->pci_epf, list)
|
||||
pci_epf_linkup(epf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features rockchip_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
rockchip_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
|
||||
{
|
||||
return &rockchip_pcie_epc_features;
|
||||
}
|
||||
|
||||
static const struct pci_epc_ops rockchip_pcie_epc_ops = {
|
||||
.write_header = rockchip_pcie_ep_write_header,
|
||||
.set_bar = rockchip_pcie_ep_set_bar,
|
||||
@ -515,6 +524,7 @@ static const struct pci_epc_ops rockchip_pcie_epc_ops = {
|
||||
.get_msi = rockchip_pcie_ep_get_msi,
|
||||
.raise_irq = rockchip_pcie_ep_raise_irq,
|
||||
.start = rockchip_pcie_ep_start,
|
||||
.get_features = rockchip_pcie_ep_get_features,
|
||||
};
|
||||
|
||||
static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip,
|
||||
|
@ -571,6 +571,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
|
||||
LIST_HEAD(resources);
|
||||
resource_size_t offset[2] = {0};
|
||||
resource_size_t membar2_offset = 0x2000, busn_start = 0;
|
||||
struct pci_bus *child;
|
||||
|
||||
/*
|
||||
* Shadow registers may exist in certain VMD device ids which allow
|
||||
@ -698,7 +699,19 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
|
||||
vmd_attach_resources(vmd);
|
||||
vmd_setup_dma_ops(vmd);
|
||||
dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
|
||||
pci_rescan_bus(vmd->bus);
|
||||
|
||||
pci_scan_child_bus(vmd->bus);
|
||||
pci_assign_unassigned_bus_resources(vmd->bus);
|
||||
|
||||
/*
|
||||
* VMD root buses are virtual and don't return true on pci_is_pcie()
|
||||
* and will fail pcie_bus_configure_settings() early. It can instead be
|
||||
* run on each of the real root ports.
|
||||
*/
|
||||
list_for_each_entry(child, &vmd->bus->children, node)
|
||||
pcie_bus_configure_settings(child);
|
||||
|
||||
pci_bus_add_devices(vmd->bus);
|
||||
|
||||
WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
|
||||
"domain"), "Can't create symlink to domain\n");
|
||||
|
@ -47,9 +47,8 @@ struct pci_epf_test {
|
||||
void *reg[6];
|
||||
struct pci_epf *epf;
|
||||
enum pci_barno test_reg_bar;
|
||||
bool linkup_notifier;
|
||||
bool msix_available;
|
||||
struct delayed_work cmd_handler;
|
||||
const struct pci_epc_features *epc_features;
|
||||
};
|
||||
|
||||
struct pci_epf_test_reg {
|
||||
@ -71,11 +70,6 @@ static struct pci_epf_header test_header = {
|
||||
.interrupt_pin = PCI_INTERRUPT_INTA,
|
||||
};
|
||||
|
||||
struct pci_epf_test_data {
|
||||
enum pci_barno test_reg_bar;
|
||||
bool linkup_notifier;
|
||||
};
|
||||
|
||||
static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
|
||||
|
||||
static int pci_epf_test_copy(struct pci_epf_test *epf_test)
|
||||
@ -175,7 +169,7 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test)
|
||||
goto err_map_addr;
|
||||
}
|
||||
|
||||
memcpy(buf, src_addr, reg->size);
|
||||
memcpy_fromio(buf, src_addr, reg->size);
|
||||
|
||||
crc32 = crc32_le(~0, buf, reg->size);
|
||||
if (crc32 != reg->checksum)
|
||||
@ -230,7 +224,7 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test)
|
||||
get_random_bytes(buf, reg->size);
|
||||
reg->checksum = crc32_le(~0, buf, reg->size);
|
||||
|
||||
memcpy(dst_addr, buf, reg->size);
|
||||
memcpy_toio(dst_addr, buf, reg->size);
|
||||
|
||||
/*
|
||||
* wait 1ms inorder for the write to complete. Without this delay L3
|
||||
@ -402,13 +396,15 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
|
||||
struct device *dev = &epf->dev;
|
||||
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
|
||||
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
|
||||
const struct pci_epc_features *epc_features;
|
||||
|
||||
epc_features = epf_test->epc_features;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++) {
|
||||
epf_bar = &epf->bar[bar];
|
||||
|
||||
epf_bar->flags |= upper_32_bits(epf_bar->size) ?
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64 :
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_32;
|
||||
if (!!(epc_features->reserved_bar & (1 << bar)))
|
||||
continue;
|
||||
|
||||
ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
|
||||
if (ret) {
|
||||
@ -433,9 +429,13 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
|
||||
{
|
||||
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
|
||||
struct device *dev = &epf->dev;
|
||||
struct pci_epf_bar *epf_bar;
|
||||
void *base;
|
||||
int bar;
|
||||
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
|
||||
const struct pci_epc_features *epc_features;
|
||||
|
||||
epc_features = epf_test->epc_features;
|
||||
|
||||
base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg),
|
||||
test_reg_bar);
|
||||
@ -446,37 +446,69 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
|
||||
epf_test->reg[test_reg_bar] = base;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++) {
|
||||
epf_bar = &epf->bar[bar];
|
||||
if (bar == test_reg_bar)
|
||||
continue;
|
||||
|
||||
if (!!(epc_features->reserved_bar & (1 << bar)))
|
||||
continue;
|
||||
|
||||
base = pci_epf_alloc_space(epf, bar_size[bar], bar);
|
||||
if (!base)
|
||||
dev_err(dev, "Failed to allocate space for BAR%d\n",
|
||||
bar);
|
||||
epf_test->reg[bar] = base;
|
||||
if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
|
||||
bar++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_epf_configure_bar(struct pci_epf *epf,
|
||||
const struct pci_epc_features *epc_features)
|
||||
{
|
||||
struct pci_epf_bar *epf_bar;
|
||||
bool bar_fixed_64bit;
|
||||
int i;
|
||||
|
||||
for (i = BAR_0; i <= BAR_5; i++) {
|
||||
epf_bar = &epf->bar[i];
|
||||
bar_fixed_64bit = !!(epc_features->bar_fixed_64bit & (1 << i));
|
||||
if (bar_fixed_64bit)
|
||||
epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
|
||||
if (epc_features->bar_fixed_size[i])
|
||||
bar_size[i] = epc_features->bar_fixed_size[i];
|
||||
}
|
||||
}
|
||||
|
||||
static int pci_epf_test_bind(struct pci_epf *epf)
|
||||
{
|
||||
int ret;
|
||||
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
|
||||
struct pci_epf_header *header = epf->header;
|
||||
const struct pci_epc_features *epc_features;
|
||||
enum pci_barno test_reg_bar = BAR_0;
|
||||
struct pci_epc *epc = epf->epc;
|
||||
struct device *dev = &epf->dev;
|
||||
bool linkup_notifier = false;
|
||||
bool msix_capable = false;
|
||||
bool msi_capable = true;
|
||||
|
||||
if (WARN_ON_ONCE(!epc))
|
||||
return -EINVAL;
|
||||
|
||||
if (epc->features & EPC_FEATURE_NO_LINKUP_NOTIFIER)
|
||||
epf_test->linkup_notifier = false;
|
||||
else
|
||||
epf_test->linkup_notifier = true;
|
||||
epc_features = pci_epc_get_features(epc, epf->func_no);
|
||||
if (epc_features) {
|
||||
linkup_notifier = epc_features->linkup_notifier;
|
||||
msix_capable = epc_features->msix_capable;
|
||||
msi_capable = epc_features->msi_capable;
|
||||
test_reg_bar = pci_epc_get_first_free_bar(epc_features);
|
||||
pci_epf_configure_bar(epf, epc_features);
|
||||
}
|
||||
|
||||
epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE;
|
||||
|
||||
epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features);
|
||||
epf_test->test_reg_bar = test_reg_bar;
|
||||
epf_test->epc_features = epc_features;
|
||||
|
||||
ret = pci_epc_write_header(epc, epf->func_no, header);
|
||||
if (ret) {
|
||||
@ -492,13 +524,15 @@ static int pci_epf_test_bind(struct pci_epf *epf)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
|
||||
if (ret) {
|
||||
dev_err(dev, "MSI configuration failed\n");
|
||||
return ret;
|
||||
if (msi_capable) {
|
||||
ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
|
||||
if (ret) {
|
||||
dev_err(dev, "MSI configuration failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (epf_test->msix_available) {
|
||||
if (msix_capable) {
|
||||
ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
|
||||
if (ret) {
|
||||
dev_err(dev, "MSI-X configuration failed\n");
|
||||
@ -506,7 +540,7 @@ static int pci_epf_test_bind(struct pci_epf *epf)
|
||||
}
|
||||
}
|
||||
|
||||
if (!epf_test->linkup_notifier)
|
||||
if (!linkup_notifier)
|
||||
queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
|
||||
|
||||
return 0;
|
||||
@ -523,17 +557,6 @@ static int pci_epf_test_probe(struct pci_epf *epf)
|
||||
{
|
||||
struct pci_epf_test *epf_test;
|
||||
struct device *dev = &epf->dev;
|
||||
const struct pci_epf_device_id *match;
|
||||
struct pci_epf_test_data *data;
|
||||
enum pci_barno test_reg_bar = BAR_0;
|
||||
bool linkup_notifier = true;
|
||||
|
||||
match = pci_epf_match_device(pci_epf_test_ids, epf);
|
||||
data = (struct pci_epf_test_data *)match->driver_data;
|
||||
if (data) {
|
||||
test_reg_bar = data->test_reg_bar;
|
||||
linkup_notifier = data->linkup_notifier;
|
||||
}
|
||||
|
||||
epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL);
|
||||
if (!epf_test)
|
||||
@ -541,8 +564,6 @@ static int pci_epf_test_probe(struct pci_epf *epf)
|
||||
|
||||
epf->header = &test_header;
|
||||
epf_test->epf = epf;
|
||||
epf_test->test_reg_bar = test_reg_bar;
|
||||
epf_test->linkup_notifier = linkup_notifier;
|
||||
|
||||
INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
|
||||
|
||||
|
@ -83,6 +83,59 @@ err:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_epc_get);
|
||||
|
||||
/**
|
||||
* pci_epc_get_first_free_bar() - helper to get first unreserved BAR
|
||||
* @epc_features: pci_epc_features structure that holds the reserved bar bitmap
|
||||
*
|
||||
* Invoke to get the first unreserved BAR that can be used for endpoint
|
||||
* function. For any incorrect value in reserved_bar return '0'.
|
||||
*/
|
||||
unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features
|
||||
*epc_features)
|
||||
{
|
||||
int free_bar;
|
||||
|
||||
if (!epc_features)
|
||||
return 0;
|
||||
|
||||
free_bar = ffz(epc_features->reserved_bar);
|
||||
if (free_bar > 5)
|
||||
return 0;
|
||||
|
||||
return free_bar;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_epc_get_first_free_bar);
|
||||
|
||||
/**
|
||||
* pci_epc_get_features() - get the features supported by EPC
|
||||
* @epc: the features supported by *this* EPC device will be returned
|
||||
* @func_no: the features supported by the EPC device specific to the
|
||||
* endpoint function with func_no will be returned
|
||||
*
|
||||
* Invoke to get the features provided by the EPC which may be
|
||||
* specific to an endpoint function. Returns pci_epc_features on success
|
||||
* and NULL for any failures.
|
||||
*/
|
||||
const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
|
||||
u8 func_no)
|
||||
{
|
||||
const struct pci_epc_features *epc_features;
|
||||
unsigned long flags;
|
||||
|
||||
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
|
||||
return NULL;
|
||||
|
||||
if (!epc->ops->get_features)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&epc->lock, flags);
|
||||
epc_features = epc->ops->get_features(epc, func_no);
|
||||
spin_unlock_irqrestore(&epc->lock, flags);
|
||||
|
||||
return epc_features;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_epc_get_features);
|
||||
|
||||
/**
|
||||
* pci_epc_stop() - stop the PCI link
|
||||
* @epc: the link of the EPC device that has to be stopped
|
||||
|
@ -131,7 +131,9 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar)
|
||||
epf->bar[bar].phys_addr = phys_addr;
|
||||
epf->bar[bar].size = size;
|
||||
epf->bar[bar].barno = bar;
|
||||
epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY;
|
||||
epf->bar[bar].flags |= upper_32_bits(size) ?
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64 :
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_32;
|
||||
|
||||
return space;
|
||||
}
|
||||
|
@ -378,7 +378,6 @@ int ibmphp_add_pfmem_from_mem(struct resource_node *);
|
||||
struct bus_node *ibmphp_find_res_bus(u8);
|
||||
void ibmphp_print_test(void); /* for debugging purposes */
|
||||
|
||||
void ibmphp_hpc_initvars(void);
|
||||
int ibmphp_hpc_readslot(struct slot *, u8, u8 *);
|
||||
int ibmphp_hpc_writeslot(struct slot *, u8);
|
||||
void ibmphp_lock_operations(void);
|
||||
|
@ -1277,8 +1277,6 @@ static int __init ibmphp_init(void)
|
||||
|
||||
ibmphp_debug = debug;
|
||||
|
||||
ibmphp_hpc_initvars();
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
irqs[i] = 0;
|
||||
|
||||
|
@ -15,13 +15,13 @@
|
||||
|
||||
#include <linux/wait.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/kthread.h>
|
||||
#include "ibmphp.h"
|
||||
|
||||
@ -88,10 +88,10 @@ static int to_debug = 0;
|
||||
//----------------------------------------------------------------------------
|
||||
// global variables
|
||||
//----------------------------------------------------------------------------
|
||||
static struct mutex sem_hpcaccess; // lock access to HPC
|
||||
static struct semaphore semOperations; // lock all operations and
|
||||
static DEFINE_MUTEX(sem_hpcaccess); // lock access to HPC
|
||||
static DEFINE_MUTEX(operations_mutex); // lock all operations and
|
||||
// access to data structures
|
||||
static struct semaphore sem_exit; // make sure polling thread goes away
|
||||
static DECLARE_COMPLETION(exit_complete); // make sure polling thread goes away
|
||||
static struct task_struct *ibmphp_poll_thread;
|
||||
//----------------------------------------------------------------------------
|
||||
// local function prototypes
|
||||
@ -109,23 +109,6 @@ static int hpc_wait_ctlr_notworking(int, struct controller *, void __iomem *, u8
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* Name: ibmphp_hpc_initvars
|
||||
*
|
||||
* Action: initialize semaphores and variables
|
||||
*---------------------------------------------------------------------*/
|
||||
void __init ibmphp_hpc_initvars(void)
|
||||
{
|
||||
debug("%s - Entry\n", __func__);
|
||||
|
||||
mutex_init(&sem_hpcaccess);
|
||||
sema_init(&semOperations, 1);
|
||||
sema_init(&sem_exit, 0);
|
||||
to_debug = 0;
|
||||
|
||||
debug("%s - Exit\n", __func__);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* Name: i2c_ctrl_read
|
||||
*
|
||||
@ -780,7 +763,7 @@ void free_hpc_access(void)
|
||||
*---------------------------------------------------------------------*/
|
||||
void ibmphp_lock_operations(void)
|
||||
{
|
||||
down(&semOperations);
|
||||
mutex_lock(&operations_mutex);
|
||||
to_debug = 1;
|
||||
}
|
||||
|
||||
@ -790,7 +773,7 @@ void ibmphp_lock_operations(void)
|
||||
void ibmphp_unlock_operations(void)
|
||||
{
|
||||
debug("%s - Entry\n", __func__);
|
||||
up(&semOperations);
|
||||
mutex_unlock(&operations_mutex);
|
||||
to_debug = 0;
|
||||
debug("%s - Exit\n", __func__);
|
||||
}
|
||||
@ -816,7 +799,7 @@ static int poll_hpc(void *data)
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
/* try to get the lock to do some kind of hardware access */
|
||||
down(&semOperations);
|
||||
mutex_lock(&operations_mutex);
|
||||
|
||||
switch (poll_state) {
|
||||
case POLL_LATCH_REGISTER:
|
||||
@ -871,13 +854,13 @@ static int poll_hpc(void *data)
|
||||
break;
|
||||
case POLL_SLEEP:
|
||||
/* don't sleep with a lock on the hardware */
|
||||
up(&semOperations);
|
||||
mutex_unlock(&operations_mutex);
|
||||
msleep(POLL_INTERVAL_SEC * 1000);
|
||||
|
||||
if (kthread_should_stop())
|
||||
goto out_sleep;
|
||||
|
||||
down(&semOperations);
|
||||
mutex_lock(&operations_mutex);
|
||||
|
||||
if (poll_count >= POLL_LATCH_CNT) {
|
||||
poll_count = 0;
|
||||
@ -887,12 +870,12 @@ static int poll_hpc(void *data)
|
||||
break;
|
||||
}
|
||||
/* give up the hardware semaphore */
|
||||
up(&semOperations);
|
||||
mutex_unlock(&operations_mutex);
|
||||
/* sleep for a short time just for good measure */
|
||||
out_sleep:
|
||||
msleep(100);
|
||||
}
|
||||
up(&sem_exit);
|
||||
complete(&exit_complete);
|
||||
debug("%s - Exit\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
@ -1060,9 +1043,9 @@ void __exit ibmphp_hpc_stop_poll_thread(void)
|
||||
debug("after locking operations\n");
|
||||
|
||||
// wait for poll thread to exit
|
||||
debug("before sem_exit down\n");
|
||||
down(&sem_exit);
|
||||
debug("after sem_exit down\n");
|
||||
debug("before exit_complete down\n");
|
||||
wait_for_completion(&exit_complete);
|
||||
debug("after exit_completion down\n");
|
||||
|
||||
// cleanup
|
||||
debug("before free_hpc_access\n");
|
||||
@ -1070,8 +1053,6 @@ void __exit ibmphp_hpc_stop_poll_thread(void)
|
||||
debug("after free_hpc_access\n");
|
||||
ibmphp_unlock_operations();
|
||||
debug("after unlock operations\n");
|
||||
up(&sem_exit);
|
||||
debug("after sem exit up\n");
|
||||
|
||||
debug("%s - Exit\n", __func__);
|
||||
}
|
||||
|
@ -156,9 +156,9 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
|
||||
slot_ctrl |= (cmd & mask);
|
||||
ctrl->cmd_busy = 1;
|
||||
smp_mb();
|
||||
ctrl->slot_ctrl = slot_ctrl;
|
||||
pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl);
|
||||
ctrl->cmd_started = jiffies;
|
||||
ctrl->slot_ctrl = slot_ctrl;
|
||||
|
||||
/*
|
||||
* Controllers with the Intel CF118 and similar errata advertise
|
||||
@ -736,12 +736,25 @@ void pcie_clear_hotplug_events(struct controller *ctrl)
|
||||
|
||||
void pcie_enable_interrupt(struct controller *ctrl)
|
||||
{
|
||||
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE);
|
||||
u16 mask;
|
||||
|
||||
mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
|
||||
pcie_write_cmd(ctrl, mask, mask);
|
||||
}
|
||||
|
||||
void pcie_disable_interrupt(struct controller *ctrl)
|
||||
{
|
||||
pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE);
|
||||
u16 mask;
|
||||
|
||||
/*
|
||||
* Mask hot-plug interrupt to prevent it triggering immediately
|
||||
* when the link goes inactive (we still get PME when any of the
|
||||
* enabled events is detected). Same goes with Link Layer State
|
||||
* changed event which generates PME immediately when the link goes
|
||||
* inactive so mask it as well.
|
||||
*/
|
||||
mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
|
||||
pcie_write_cmd(ctrl, 0, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -920,3 +933,5 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0400,
|
||||
PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0401,
|
||||
PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_HXT, 0x0401,
|
||||
PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl);
|
||||
|
@ -113,7 +113,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent,
|
||||
* a fake root for all functions of a multi-function
|
||||
* device we go down them as well.
|
||||
*/
|
||||
if (!strcmp(node->name, "multifunc-device")) {
|
||||
if (of_node_name_eq(node, "multifunc-device")) {
|
||||
for_each_child_of_node(node, node2) {
|
||||
if (__of_pci_pci_compare(node2, devfn)) {
|
||||
of_node_put(node);
|
||||
|
@ -24,29 +24,6 @@
|
||||
#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END
|
||||
#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2)
|
||||
|
||||
/*
|
||||
* Initialize a pci_bridge_emul structure to represent a fake PCI
|
||||
* bridge configuration space. The caller needs to have initialized
|
||||
* the PCI configuration space with whatever values make sense
|
||||
* (typically at least vendor, device, revision), the ->ops pointer,
|
||||
* and optionally ->data and ->has_pcie.
|
||||
*/
|
||||
void pci_bridge_emul_init(struct pci_bridge_emul *bridge)
|
||||
{
|
||||
bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16;
|
||||
bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
|
||||
bridge->conf.cache_line_size = 0x10;
|
||||
bridge->conf.status = PCI_STATUS_CAP_LIST;
|
||||
|
||||
if (bridge->has_pcie) {
|
||||
bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START;
|
||||
bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP;
|
||||
/* Set PCIe v2, root port, slot support */
|
||||
bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
|
||||
PCI_EXP_FLAGS_SLOT;
|
||||
}
|
||||
}
|
||||
|
||||
struct pci_bridge_reg_behavior {
|
||||
/* Read-only bits */
|
||||
u32 ro;
|
||||
@ -283,6 +260,61 @@ const static struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = {
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize a pci_bridge_emul structure to represent a fake PCI
|
||||
* bridge configuration space. The caller needs to have initialized
|
||||
* the PCI configuration space with whatever values make sense
|
||||
* (typically at least vendor, device, revision), the ->ops pointer,
|
||||
* and optionally ->data and ->has_pcie.
|
||||
*/
|
||||
int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
|
||||
unsigned int flags)
|
||||
{
|
||||
bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16;
|
||||
bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
|
||||
bridge->conf.cache_line_size = 0x10;
|
||||
bridge->conf.status = PCI_STATUS_CAP_LIST;
|
||||
bridge->pci_regs_behavior = kmemdup(pci_regs_behavior,
|
||||
sizeof(pci_regs_behavior),
|
||||
GFP_KERNEL);
|
||||
if (!bridge->pci_regs_behavior)
|
||||
return -ENOMEM;
|
||||
|
||||
if (bridge->has_pcie) {
|
||||
bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START;
|
||||
bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP;
|
||||
/* Set PCIe v2, root port, slot support */
|
||||
bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
|
||||
PCI_EXP_FLAGS_SLOT;
|
||||
bridge->pcie_cap_regs_behavior =
|
||||
kmemdup(pcie_cap_regs_behavior,
|
||||
sizeof(pcie_cap_regs_behavior),
|
||||
GFP_KERNEL);
|
||||
if (!bridge->pcie_cap_regs_behavior) {
|
||||
kfree(bridge->pci_regs_behavior);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR) {
|
||||
bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].ro = ~0;
|
||||
bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup a pci_bridge_emul structure that was previously initilized
|
||||
* using pci_bridge_emul_init().
|
||||
*/
|
||||
void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge)
|
||||
{
|
||||
if (bridge->has_pcie)
|
||||
kfree(bridge->pcie_cap_regs_behavior);
|
||||
kfree(bridge->pci_regs_behavior);
|
||||
}
|
||||
|
||||
/*
|
||||
* Should be called by the PCI controller driver when reading the PCI
|
||||
* configuration space of the fake bridge. It will call back the
|
||||
@ -312,11 +344,11 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
|
||||
reg -= PCI_CAP_PCIE_START;
|
||||
read_op = bridge->ops->read_pcie;
|
||||
cfgspace = (u32 *) &bridge->pcie_conf;
|
||||
behavior = pcie_cap_regs_behavior;
|
||||
behavior = bridge->pcie_cap_regs_behavior;
|
||||
} else {
|
||||
read_op = bridge->ops->read_base;
|
||||
cfgspace = (u32 *) &bridge->conf;
|
||||
behavior = pci_regs_behavior;
|
||||
behavior = bridge->pci_regs_behavior;
|
||||
}
|
||||
|
||||
if (read_op)
|
||||
@ -383,11 +415,11 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
|
||||
reg -= PCI_CAP_PCIE_START;
|
||||
write_op = bridge->ops->write_pcie;
|
||||
cfgspace = (u32 *) &bridge->pcie_conf;
|
||||
behavior = pcie_cap_regs_behavior;
|
||||
behavior = bridge->pcie_cap_regs_behavior;
|
||||
} else {
|
||||
write_op = bridge->ops->write_base;
|
||||
cfgspace = (u32 *) &bridge->conf;
|
||||
behavior = pci_regs_behavior;
|
||||
behavior = bridge->pci_regs_behavior;
|
||||
}
|
||||
|
||||
/* Keep all bits, except the RW bits */
|
||||
|
@ -107,15 +107,26 @@ struct pci_bridge_emul_ops {
|
||||
u32 old, u32 new, u32 mask);
|
||||
};
|
||||
|
||||
struct pci_bridge_reg_behavior;
|
||||
|
||||
struct pci_bridge_emul {
|
||||
struct pci_bridge_emul_conf conf;
|
||||
struct pci_bridge_emul_pcie_conf pcie_conf;
|
||||
struct pci_bridge_emul_ops *ops;
|
||||
struct pci_bridge_reg_behavior *pci_regs_behavior;
|
||||
struct pci_bridge_reg_behavior *pcie_cap_regs_behavior;
|
||||
void *data;
|
||||
bool has_pcie;
|
||||
};
|
||||
|
||||
void pci_bridge_emul_init(struct pci_bridge_emul *bridge);
|
||||
enum {
|
||||
PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR = BIT(0),
|
||||
};
|
||||
|
||||
int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
|
||||
unsigned int flags);
|
||||
void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge);
|
||||
|
||||
int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
|
||||
int size, u32 *value);
|
||||
int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
|
||||
|
@ -100,7 +100,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,
|
||||
{
|
||||
struct pci_driver *pdrv = to_pci_driver(driver);
|
||||
const struct pci_device_id *ids = pdrv->id_table;
|
||||
__u32 vendor, device, subvendor = PCI_ANY_ID,
|
||||
u32 vendor, device, subvendor = PCI_ANY_ID,
|
||||
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
|
||||
unsigned long driver_data = 0;
|
||||
int fields = 0;
|
||||
@ -168,7 +168,7 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
|
||||
{
|
||||
struct pci_dynid *dynid, *n;
|
||||
struct pci_driver *pdrv = to_pci_driver(driver);
|
||||
__u32 vendor, device, subvendor = PCI_ANY_ID,
|
||||
u32 vendor, device, subvendor = PCI_ANY_ID,
|
||||
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
|
||||
int fields = 0;
|
||||
size_t retval = -ENODEV;
|
||||
|
@ -861,7 +861,7 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||
if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
|
||||
&& !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
|
||||
need_restore = true;
|
||||
/* Fall-through: force to D0 */
|
||||
/* Fall-through - force to D0 */
|
||||
default:
|
||||
pmcsr = 0;
|
||||
break;
|
||||
@ -1233,7 +1233,6 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
|
||||
pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
|
||||
}
|
||||
|
||||
|
||||
static int pci_save_pcix_state(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
@ -1270,6 +1269,45 @@ static void pci_restore_pcix_state(struct pci_dev *dev)
|
||||
pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]);
|
||||
}
|
||||
|
||||
static void pci_save_ltr_state(struct pci_dev *dev)
|
||||
{
|
||||
int ltr;
|
||||
struct pci_cap_saved_state *save_state;
|
||||
u16 *cap;
|
||||
|
||||
if (!pci_is_pcie(dev))
|
||||
return;
|
||||
|
||||
ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
|
||||
if (!ltr)
|
||||
return;
|
||||
|
||||
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR);
|
||||
if (!save_state) {
|
||||
pci_err(dev, "no suspend buffer for LTR; ASPM issues possible after resume\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cap = (u16 *)&save_state->cap.data[0];
|
||||
pci_read_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, cap++);
|
||||
pci_read_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, cap++);
|
||||
}
|
||||
|
||||
static void pci_restore_ltr_state(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_cap_saved_state *save_state;
|
||||
int ltr;
|
||||
u16 *cap;
|
||||
|
||||
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR);
|
||||
ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
|
||||
if (!save_state || !ltr)
|
||||
return;
|
||||
|
||||
cap = (u16 *)&save_state->cap.data[0];
|
||||
pci_write_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap++);
|
||||
pci_write_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, *cap++);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_save_state - save the PCI configuration space of a device before suspending
|
||||
@ -1291,6 +1329,7 @@ int pci_save_state(struct pci_dev *dev)
|
||||
if (i != 0)
|
||||
return i;
|
||||
|
||||
pci_save_ltr_state(dev);
|
||||
pci_save_dpc_state(dev);
|
||||
return pci_save_vc_state(dev);
|
||||
}
|
||||
@ -1390,7 +1429,12 @@ void pci_restore_state(struct pci_dev *dev)
|
||||
if (!dev->state_saved)
|
||||
return;
|
||||
|
||||
/* PCI Express register must be restored first */
|
||||
/*
|
||||
* Restore max latencies (in the LTR capability) before enabling
|
||||
* LTR itself (in the PCIe capability).
|
||||
*/
|
||||
pci_restore_ltr_state(dev);
|
||||
|
||||
pci_restore_pcie_state(dev);
|
||||
pci_restore_pasid_state(dev);
|
||||
pci_restore_pri_state(dev);
|
||||
@ -2260,7 +2304,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup)
|
||||
case PCI_D2:
|
||||
if (pci_no_d1d2(dev))
|
||||
break;
|
||||
/* else: fall through */
|
||||
/* else, fall through */
|
||||
default:
|
||||
target_state = state;
|
||||
}
|
||||
@ -2501,6 +2545,25 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
|
||||
pm_runtime_put_sync(parent);
|
||||
}
|
||||
|
||||
static const struct dmi_system_id bridge_d3_blacklist[] = {
|
||||
#ifdef CONFIG_X86
|
||||
{
|
||||
/*
|
||||
* Gigabyte X299 root port is not marked as hotplug capable
|
||||
* which allows Linux to power manage it. However, this
|
||||
* confuses the BIOS SMI handler so don't power manage root
|
||||
* ports on that system.
|
||||
*/
|
||||
.ident = "X299 DESIGNARE EX-CF",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "X299 DESIGNARE EX-CF"),
|
||||
},
|
||||
},
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* pci_bridge_d3_possible - Is it possible to put the bridge into D3
|
||||
* @bridge: Bridge to check
|
||||
@ -2546,6 +2609,9 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
|
||||
if (bridge->is_hotplug_bridge)
|
||||
return false;
|
||||
|
||||
if (dmi_check_system(bridge_d3_blacklist))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* It should be safe to put PCIe ports from 2015 or newer
|
||||
* to D3.
|
||||
@ -2998,6 +3064,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
|
||||
if (error)
|
||||
pci_err(dev, "unable to preallocate PCI-X save buffer\n");
|
||||
|
||||
error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_LTR,
|
||||
2 * sizeof(u16));
|
||||
if (error)
|
||||
pci_err(dev, "unable to allocate suspend buffer for LTR\n");
|
||||
|
||||
pci_allocate_vc_save_buffers(dev);
|
||||
}
|
||||
|
||||
@ -5058,39 +5129,42 @@ unlock:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save and disable devices from the top of the tree down */
|
||||
static void pci_bus_save_and_disable(struct pci_bus *bus)
|
||||
/*
|
||||
* Save and disable devices from the top of the tree down while holding
|
||||
* the @dev mutex lock for the entire tree.
|
||||
*/
|
||||
static void pci_bus_save_and_disable_locked(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
pci_dev_lock(dev);
|
||||
pci_dev_save_and_disable(dev);
|
||||
pci_dev_unlock(dev);
|
||||
if (dev->subordinate)
|
||||
pci_bus_save_and_disable(dev->subordinate);
|
||||
pci_bus_save_and_disable_locked(dev->subordinate);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore devices from top of the tree down - parent bridges need to be
|
||||
* restored before we can get to subordinate devices.
|
||||
* Restore devices from top of the tree down while holding @dev mutex lock
|
||||
* for the entire tree. Parent bridges need to be restored before we can
|
||||
* get to subordinate devices.
|
||||
*/
|
||||
static void pci_bus_restore(struct pci_bus *bus)
|
||||
static void pci_bus_restore_locked(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
pci_dev_lock(dev);
|
||||
pci_dev_restore(dev);
|
||||
pci_dev_unlock(dev);
|
||||
if (dev->subordinate)
|
||||
pci_bus_restore(dev->subordinate);
|
||||
pci_bus_restore_locked(dev->subordinate);
|
||||
}
|
||||
}
|
||||
|
||||
/* Save and disable devices from the top of the tree down */
|
||||
static void pci_slot_save_and_disable(struct pci_slot *slot)
|
||||
/*
|
||||
* Save and disable devices from the top of the tree down while holding
|
||||
* the @dev mutex lock for the entire tree.
|
||||
*/
|
||||
static void pci_slot_save_and_disable_locked(struct pci_slot *slot)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
|
||||
@ -5099,26 +5173,25 @@ static void pci_slot_save_and_disable(struct pci_slot *slot)
|
||||
continue;
|
||||
pci_dev_save_and_disable(dev);
|
||||
if (dev->subordinate)
|
||||
pci_bus_save_and_disable(dev->subordinate);
|
||||
pci_bus_save_and_disable_locked(dev->subordinate);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore devices from top of the tree down - parent bridges need to be
|
||||
* restored before we can get to subordinate devices.
|
||||
* Restore devices from top of the tree down while holding @dev mutex lock
|
||||
* for the entire tree. Parent bridges need to be restored before we can
|
||||
* get to subordinate devices.
|
||||
*/
|
||||
static void pci_slot_restore(struct pci_slot *slot)
|
||||
static void pci_slot_restore_locked(struct pci_slot *slot)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
|
||||
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
|
||||
if (!dev->slot || dev->slot != slot)
|
||||
continue;
|
||||
pci_dev_lock(dev);
|
||||
pci_dev_restore(dev);
|
||||
pci_dev_unlock(dev);
|
||||
if (dev->subordinate)
|
||||
pci_bus_restore(dev->subordinate);
|
||||
pci_bus_restore_locked(dev->subordinate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5177,17 +5250,15 @@ static int __pci_reset_slot(struct pci_slot *slot)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pci_slot_save_and_disable(slot);
|
||||
|
||||
if (pci_slot_trylock(slot)) {
|
||||
pci_slot_save_and_disable_locked(slot);
|
||||
might_sleep();
|
||||
rc = pci_reset_hotplug_slot(slot->hotplug, 0);
|
||||
pci_slot_restore_locked(slot);
|
||||
pci_slot_unlock(slot);
|
||||
} else
|
||||
rc = -EAGAIN;
|
||||
|
||||
pci_slot_restore(slot);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -5273,17 +5344,15 @@ static int __pci_reset_bus(struct pci_bus *bus)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pci_bus_save_and_disable(bus);
|
||||
|
||||
if (pci_bus_trylock(bus)) {
|
||||
pci_bus_save_and_disable_locked(bus);
|
||||
might_sleep();
|
||||
rc = pci_bridge_secondary_bus_reset(bus->self);
|
||||
pci_bus_restore_locked(bus);
|
||||
pci_bus_unlock(bus);
|
||||
} else
|
||||
rc = -EAGAIN;
|
||||
|
||||
pci_bus_restore(bus);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -6000,8 +6069,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
|
||||
* to enable the kernel to reassign new resource
|
||||
* window later on.
|
||||
*/
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
|
||||
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
||||
for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
|
||||
r = &dev->resource[i];
|
||||
if (!(r->flags & IORESOURCE_MEM))
|
||||
|
@ -6,10 +6,9 @@ config PCIEPORTBUS
|
||||
bool "PCI Express Port Bus support"
|
||||
depends on PCI
|
||||
help
|
||||
This automatically enables PCI Express Port Bus support. Users can
|
||||
choose Native Hot-Plug support, Advanced Error Reporting support,
|
||||
Power Management Event support and Virtual Channel support to run
|
||||
on PCI Express Ports (Root or Switch).
|
||||
This enables PCI Express Port Bus support. Users can then enable
|
||||
support for Native Hot-Plug, Advanced Error Reporting, Power
|
||||
Management Events, and Downstream Port Containment.
|
||||
|
||||
#
|
||||
# Include service Kconfig here
|
||||
|
@ -3,6 +3,7 @@
|
||||
# Makefile for PCI Express features and port driver
|
||||
|
||||
pcieportdrv-y := portdrv_core.o portdrv_pci.o err.o
|
||||
pcieportdrv-y += bw_notification.o
|
||||
|
||||
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
|
||||
|
||||
|
@ -117,7 +117,7 @@ bool pci_aer_available(void)
|
||||
|
||||
static int ecrc_policy = ECRC_POLICY_DEFAULT;
|
||||
|
||||
static const char *ecrc_policy_str[] = {
|
||||
static const char * const ecrc_policy_str[] = {
|
||||
[ECRC_POLICY_DEFAULT] = "bios",
|
||||
[ECRC_POLICY_OFF] = "off",
|
||||
[ECRC_POLICY_ON] = "on"
|
||||
@ -203,11 +203,8 @@ void pcie_ecrc_get_policy(char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++)
|
||||
if (!strncmp(str, ecrc_policy_str[i],
|
||||
strlen(ecrc_policy_str[i])))
|
||||
break;
|
||||
if (i >= ARRAY_SIZE(ecrc_policy_str))
|
||||
i = match_string(ecrc_policy_str, ARRAY_SIZE(ecrc_policy_str), str);
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
ecrc_policy = i;
|
||||
|
110
drivers/pci/pcie/bw_notification.c
Normal file
110
drivers/pci/pcie/bw_notification.c
Normal file
@ -0,0 +1,110 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* PCI Express Link Bandwidth Notification services driver
|
||||
* Author: Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2019, Dell Inc
|
||||
*
|
||||
* The PCIe Link Bandwidth Notification provides a way to notify the
|
||||
* operating system when the link width or data rate changes. This
|
||||
* capability is required for all root ports and downstream ports
|
||||
* supporting links wider than x1 and/or multiple link speeds.
|
||||
*
|
||||
* This service port driver hooks into the bandwidth notification interrupt
|
||||
* and warns when links become degraded in operation.
|
||||
*/
|
||||
|
||||
#include "../pci.h"
|
||||
#include "portdrv.h"
|
||||
|
||||
static bool pcie_link_bandwidth_notification_supported(struct pci_dev *dev)
|
||||
{
|
||||
int ret;
|
||||
u32 lnk_cap;
|
||||
|
||||
ret = pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnk_cap);
|
||||
return (ret == PCIBIOS_SUCCESSFUL) && (lnk_cap & PCI_EXP_LNKCAP_LBNC);
|
||||
}
|
||||
|
||||
static void pcie_enable_link_bandwidth_notification(struct pci_dev *dev)
|
||||
{
|
||||
u16 lnk_ctl;
|
||||
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
|
||||
lnk_ctl |= PCI_EXP_LNKCTL_LBMIE;
|
||||
pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
|
||||
}
|
||||
|
||||
static void pcie_disable_link_bandwidth_notification(struct pci_dev *dev)
|
||||
{
|
||||
u16 lnk_ctl;
|
||||
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
|
||||
lnk_ctl &= ~PCI_EXP_LNKCTL_LBMIE;
|
||||
pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
|
||||
}
|
||||
|
||||
static irqreturn_t pcie_bw_notification_handler(int irq, void *context)
|
||||
{
|
||||
struct pcie_device *srv = context;
|
||||
struct pci_dev *port = srv->port;
|
||||
struct pci_dev *dev;
|
||||
u16 link_status, events;
|
||||
int ret;
|
||||
|
||||
ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
|
||||
events = link_status & PCI_EXP_LNKSTA_LBMS;
|
||||
|
||||
if (ret != PCIBIOS_SUCCESSFUL || !events)
|
||||
return IRQ_NONE;
|
||||
|
||||
/*
|
||||
* Print status from downstream devices, not this root port or
|
||||
* downstream switch port.
|
||||
*/
|
||||
down_read(&pci_bus_sem);
|
||||
list_for_each_entry(dev, &port->subordinate->devices, bus_list)
|
||||
__pcie_print_link_status(dev, false);
|
||||
up_read(&pci_bus_sem);
|
||||
|
||||
pcie_update_link_speed(port->subordinate, link_status);
|
||||
pcie_capability_write_word(port, PCI_EXP_LNKSTA, events);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pcie_bandwidth_notification_probe(struct pcie_device *srv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Single-width or single-speed ports do not have to support this. */
|
||||
if (!pcie_link_bandwidth_notification_supported(srv->port))
|
||||
return -ENODEV;
|
||||
|
||||
ret = request_threaded_irq(srv->irq, NULL, pcie_bw_notification_handler,
|
||||
IRQF_SHARED, "PCIe BW notif", srv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pcie_enable_link_bandwidth_notification(srv->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcie_bandwidth_notification_remove(struct pcie_device *srv)
|
||||
{
|
||||
pcie_disable_link_bandwidth_notification(srv->port);
|
||||
free_irq(srv->irq, srv);
|
||||
}
|
||||
|
||||
static struct pcie_port_service_driver pcie_bandwidth_notification_driver = {
|
||||
.name = "pcie_bw_notification",
|
||||
.port_type = PCIE_ANY_PORT,
|
||||
.service = PCIE_PORT_SERVICE_BWNOTIF,
|
||||
.probe = pcie_bandwidth_notification_probe,
|
||||
.remove = pcie_bandwidth_notification_remove,
|
||||
};
|
||||
|
||||
int __init pcie_bandwidth_notification_init(void)
|
||||
{
|
||||
return pcie_port_service_register(&pcie_bandwidth_notification_driver);
|
||||
}
|
@ -202,6 +202,28 @@ static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
|
||||
pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, status);
|
||||
}
|
||||
|
||||
static int dpc_get_aer_uncorrect_severity(struct pci_dev *dev,
|
||||
struct aer_err_info *info)
|
||||
{
|
||||
int pos = dev->aer_cap;
|
||||
u32 status, mask, sev;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask);
|
||||
status &= ~mask;
|
||||
if (!status)
|
||||
return 0;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &sev);
|
||||
status &= sev;
|
||||
if (status)
|
||||
info->severity = AER_FATAL;
|
||||
else
|
||||
info->severity = AER_NONFATAL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static irqreturn_t dpc_handler(int irq, void *context)
|
||||
{
|
||||
struct aer_err_info info;
|
||||
@ -229,9 +251,12 @@ static irqreturn_t dpc_handler(int irq, void *context)
|
||||
/* show RP PIO error detail information */
|
||||
if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
|
||||
dpc_process_rp_pio_error(dpc);
|
||||
else if (reason == 0 && aer_get_device_error_info(pdev, &info)) {
|
||||
else if (reason == 0 &&
|
||||
dpc_get_aer_uncorrect_severity(pdev, &info) &&
|
||||
aer_get_device_error_info(pdev, &info)) {
|
||||
aer_print_error(pdev, &info);
|
||||
pci_cleanup_aer_uncorrect_error_status(pdev);
|
||||
pci_aer_clear_fatal_status(pdev);
|
||||
}
|
||||
|
||||
/* We configure DPC so it only triggers on ERR_FATAL */
|
||||
|
@ -363,6 +363,16 @@ static bool pcie_pme_check_wakeup(struct pci_bus *bus)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pcie_pme_disable_interrupt(struct pci_dev *port,
|
||||
struct pcie_pme_service_data *data)
|
||||
{
|
||||
spin_lock_irq(&data->lock);
|
||||
pcie_pme_interrupt_enable(port, false);
|
||||
pcie_clear_root_pme_status(port);
|
||||
data->noirq = true;
|
||||
spin_unlock_irq(&data->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_pme_suspend - Suspend PCIe PME service device.
|
||||
* @srv: PCIe service device to suspend.
|
||||
@ -387,11 +397,7 @@ static int pcie_pme_suspend(struct pcie_device *srv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irq(&data->lock);
|
||||
pcie_pme_interrupt_enable(port, false);
|
||||
pcie_clear_root_pme_status(port);
|
||||
data->noirq = true;
|
||||
spin_unlock_irq(&data->lock);
|
||||
pcie_pme_disable_interrupt(port, data);
|
||||
|
||||
synchronize_irq(srv->irq);
|
||||
|
||||
@ -427,34 +433,12 @@ static int pcie_pme_resume(struct pcie_device *srv)
|
||||
*/
|
||||
static void pcie_pme_remove(struct pcie_device *srv)
|
||||
{
|
||||
pcie_pme_suspend(srv);
|
||||
struct pcie_pme_service_data *data = get_service_data(srv);
|
||||
|
||||
pcie_pme_disable_interrupt(srv->port, data);
|
||||
free_irq(srv->irq, srv);
|
||||
kfree(get_service_data(srv));
|
||||
}
|
||||
|
||||
static int pcie_pme_runtime_suspend(struct pcie_device *srv)
|
||||
{
|
||||
struct pcie_pme_service_data *data = get_service_data(srv);
|
||||
|
||||
spin_lock_irq(&data->lock);
|
||||
pcie_pme_interrupt_enable(srv->port, false);
|
||||
pcie_clear_root_pme_status(srv->port);
|
||||
data->noirq = true;
|
||||
spin_unlock_irq(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcie_pme_runtime_resume(struct pcie_device *srv)
|
||||
{
|
||||
struct pcie_pme_service_data *data = get_service_data(srv);
|
||||
|
||||
spin_lock_irq(&data->lock);
|
||||
pcie_pme_interrupt_enable(srv->port, true);
|
||||
data->noirq = false;
|
||||
spin_unlock_irq(&data->lock);
|
||||
|
||||
return 0;
|
||||
cancel_work_sync(&data->work);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static struct pcie_port_service_driver pcie_pme_driver = {
|
||||
@ -464,8 +448,6 @@ static struct pcie_port_service_driver pcie_pme_driver = {
|
||||
|
||||
.probe = pcie_pme_probe,
|
||||
.suspend = pcie_pme_suspend,
|
||||
.runtime_suspend = pcie_pme_runtime_suspend,
|
||||
.runtime_resume = pcie_pme_runtime_resume,
|
||||
.resume = pcie_pme_resume,
|
||||
.remove = pcie_pme_remove,
|
||||
};
|
||||
|
@ -20,8 +20,10 @@
|
||||
#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
|
||||
#define PCIE_PORT_SERVICE_DPC_SHIFT 3 /* Downstream Port Containment */
|
||||
#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
|
||||
#define PCIE_PORT_SERVICE_BWNOTIF_SHIFT 4 /* Bandwidth notification */
|
||||
#define PCIE_PORT_SERVICE_BWNOTIF (1 << PCIE_PORT_SERVICE_BWNOTIF_SHIFT)
|
||||
|
||||
#define PCIE_PORT_DEVICE_MAXSERVICES 4
|
||||
#define PCIE_PORT_DEVICE_MAXSERVICES 5
|
||||
|
||||
#ifdef CONFIG_PCIEAER
|
||||
int pcie_aer_init(void);
|
||||
@ -47,6 +49,8 @@ int pcie_dpc_init(void);
|
||||
static inline int pcie_dpc_init(void) { return 0; }
|
||||
#endif
|
||||
|
||||
int pcie_bandwidth_notification_init(void);
|
||||
|
||||
/* Port Type */
|
||||
#define PCIE_ANY_PORT (~0)
|
||||
|
||||
|
@ -99,7 +99,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
|
||||
*/
|
||||
static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
|
||||
{
|
||||
int nr_entries, nvec;
|
||||
int nr_entries, nvec, pcie_irq;
|
||||
u32 pme = 0, aer = 0, dpc = 0;
|
||||
|
||||
/* Allocate the maximum possible number of MSI/MSI-X vectors */
|
||||
@ -135,10 +135,13 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
|
||||
return nr_entries;
|
||||
}
|
||||
|
||||
/* PME and hotplug share an MSI/MSI-X vector */
|
||||
if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
|
||||
irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme);
|
||||
irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme);
|
||||
/* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */
|
||||
if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP |
|
||||
PCIE_PORT_SERVICE_BWNOTIF)) {
|
||||
pcie_irq = pci_irq_vector(dev, pme);
|
||||
irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq;
|
||||
irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq;
|
||||
irqs[PCIE_PORT_SERVICE_BWNOTIF_SHIFT] = pcie_irq;
|
||||
}
|
||||
|
||||
if (mask & PCIE_PORT_SERVICE_AER)
|
||||
@ -250,6 +253,10 @@ static int get_port_device_capability(struct pci_dev *dev)
|
||||
pci_aer_available() && services & PCIE_PORT_SERVICE_AER)
|
||||
services |= PCIE_PORT_SERVICE_DPC;
|
||||
|
||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
|
||||
pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
|
||||
services |= PCIE_PORT_SERVICE_BWNOTIF;
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
@ -182,10 +182,12 @@ static void pcie_portdrv_err_resume(struct pci_dev *dev)
|
||||
/*
|
||||
* LINUX Device Driver Model
|
||||
*/
|
||||
static const struct pci_device_id port_pci_ids[] = { {
|
||||
static const struct pci_device_id port_pci_ids[] = {
|
||||
/* handle any PCI-Express port */
|
||||
PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0),
|
||||
}, { /* end: all zeroes */ }
|
||||
{ PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0) },
|
||||
/* subtractive decode PCI-to-PCI bridge, class type is 060401h */
|
||||
{ PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x01), ~0) },
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct pci_error_handlers pcie_portdrv_err_handler = {
|
||||
@ -238,6 +240,7 @@ static void __init pcie_init_services(void)
|
||||
pcie_pme_init();
|
||||
pcie_dpc_init();
|
||||
pcie_hp_init();
|
||||
pcie_bandwidth_notification_init();
|
||||
}
|
||||
|
||||
static int __init pcie_portdrv_init(void)
|
||||
|
@ -121,13 +121,13 @@ static u64 pci_size(u64 base, u64 maxbase, u64 mask)
|
||||
* Get the lowest of them to find the decode size, and from that
|
||||
* the extent.
|
||||
*/
|
||||
size = (size & ~(size-1)) - 1;
|
||||
size = size & ~(size-1);
|
||||
|
||||
/*
|
||||
* base == maxbase can be valid only if the BAR has already been
|
||||
* programmed with all 1s.
|
||||
*/
|
||||
if (base == maxbase && ((base | size) & mask) != mask)
|
||||
if (base == maxbase && ((base | (size - 1)) & mask) != mask)
|
||||
return 0;
|
||||
|
||||
return size;
|
||||
@ -278,7 +278,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
||||
/* Above 32-bit boundary; try to reallocate */
|
||||
res->flags |= IORESOURCE_UNSET;
|
||||
res->start = 0;
|
||||
res->end = sz64;
|
||||
res->end = sz64 - 1;
|
||||
pci_info(dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n",
|
||||
pos, (unsigned long long)l64);
|
||||
goto out;
|
||||
@ -286,7 +286,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
||||
}
|
||||
|
||||
region.start = l64;
|
||||
region.end = l64 + sz64;
|
||||
region.end = l64 + sz64 - 1;
|
||||
|
||||
pcibios_bus_to_resource(dev->bus, res, ®ion);
|
||||
pcibios_resource_to_bus(dev->bus, &inverted_region, res);
|
||||
@ -348,6 +348,57 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_read_bridge_windows(struct pci_dev *bridge)
|
||||
{
|
||||
u16 io;
|
||||
u32 pmem, tmp;
|
||||
|
||||
pci_read_config_word(bridge, PCI_IO_BASE, &io);
|
||||
if (!io) {
|
||||
pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0);
|
||||
pci_read_config_word(bridge, PCI_IO_BASE, &io);
|
||||
pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
|
||||
}
|
||||
if (io)
|
||||
bridge->io_window = 1;
|
||||
|
||||
/*
|
||||
* DECchip 21050 pass 2 errata: the bridge may miss an address
|
||||
* disconnect boundary by one PCI data phase. Workaround: do not
|
||||
* use prefetching on this device.
|
||||
*/
|
||||
if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001)
|
||||
return;
|
||||
|
||||
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
|
||||
if (!pmem) {
|
||||
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,
|
||||
0xffe0fff0);
|
||||
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
|
||||
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
|
||||
}
|
||||
if (!pmem)
|
||||
return;
|
||||
|
||||
bridge->pref_window = 1;
|
||||
|
||||
if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
|
||||
|
||||
/*
|
||||
* Bridge claims to have a 64-bit prefetchable memory
|
||||
* window; verify that the upper bits are actually
|
||||
* writable.
|
||||
*/
|
||||
pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &pmem);
|
||||
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
|
||||
0xffffffff);
|
||||
pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
|
||||
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, pmem);
|
||||
if (tmp)
|
||||
bridge->pref_64_window = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_read_bridge_io(struct pci_bus *child)
|
||||
{
|
||||
struct pci_dev *dev = child->self;
|
||||
@ -1728,9 +1779,6 @@ int pci_setup_device(struct pci_dev *dev)
|
||||
break;
|
||||
|
||||
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
|
||||
if (class != PCI_CLASS_BRIDGE_PCI)
|
||||
goto bad;
|
||||
|
||||
/*
|
||||
* The PCI-to-PCI bridge spec requires that subtractive
|
||||
* decoding (i.e. transparent) bridge must have programming
|
||||
@ -1739,6 +1787,7 @@ int pci_setup_device(struct pci_dev *dev)
|
||||
pci_read_irq(dev);
|
||||
dev->transparent = ((dev->class & 0xff) == 1);
|
||||
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
|
||||
pci_read_bridge_windows(dev);
|
||||
set_pcie_hotplug_bridge(dev);
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
|
||||
if (pos) {
|
||||
@ -1856,8 +1905,6 @@ static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
|
||||
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
|
||||
hpp->latency_timer);
|
||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
|
||||
if (hpp->enable_serr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_SERR;
|
||||
if (hpp->enable_perr)
|
||||
pci_bctl |= PCI_BRIDGE_CTL_PARITY;
|
||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
|
||||
@ -2071,11 +2118,8 @@ static void pci_configure_ltr(struct pci_dev *dev)
|
||||
{
|
||||
#ifdef CONFIG_PCIEASPM
|
||||
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
|
||||
u32 cap;
|
||||
struct pci_dev *bridge;
|
||||
|
||||
if (!host->native_ltr)
|
||||
return;
|
||||
u32 cap, ctl;
|
||||
|
||||
if (!pci_is_pcie(dev))
|
||||
return;
|
||||
@ -2084,22 +2128,35 @@ static void pci_configure_ltr(struct pci_dev *dev)
|
||||
if (!(cap & PCI_EXP_DEVCAP2_LTR))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Software must not enable LTR in an Endpoint unless the Root
|
||||
* Complex and all intermediate Switches indicate support for LTR.
|
||||
* PCIe r3.1, sec 6.18.
|
||||
*/
|
||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
|
||||
dev->ltr_path = 1;
|
||||
else {
|
||||
pcie_capability_read_dword(dev, PCI_EXP_DEVCTL2, &ctl);
|
||||
if (ctl & PCI_EXP_DEVCTL2_LTR_EN) {
|
||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
|
||||
dev->ltr_path = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
bridge = pci_upstream_bridge(dev);
|
||||
if (bridge && bridge->ltr_path)
|
||||
dev->ltr_path = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->ltr_path)
|
||||
if (!host->native_ltr)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Software must not enable LTR in an Endpoint unless the Root
|
||||
* Complex and all intermediate Switches indicate support for LTR.
|
||||
* PCIe r4.0, sec 6.18.
|
||||
*/
|
||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||
((bridge = pci_upstream_bridge(dev)) &&
|
||||
bridge->ltr_path)) {
|
||||
pcie_capability_set_word(dev, PCI_EXP_DEVCTL2,
|
||||
PCI_EXP_DEVCTL2_LTR_EN);
|
||||
dev->ltr_path = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -2129,6 +2186,24 @@ static void pci_configure_eetlp_prefix(struct pci_dev *dev)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void pci_configure_serr(struct pci_dev *dev)
|
||||
{
|
||||
u16 control;
|
||||
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
||||
|
||||
/*
|
||||
* A bridge will not forward ERR_ messages coming from an
|
||||
* endpoint unless SERR# forwarding is enabled.
|
||||
*/
|
||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &control);
|
||||
if (!(control & PCI_BRIDGE_CTL_SERR)) {
|
||||
control |= PCI_BRIDGE_CTL_SERR;
|
||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, control);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_configure_device(struct pci_dev *dev)
|
||||
{
|
||||
struct hotplug_params hpp;
|
||||
@ -2139,6 +2214,7 @@ static void pci_configure_device(struct pci_dev *dev)
|
||||
pci_configure_relaxed_ordering(dev);
|
||||
pci_configure_ltr(dev);
|
||||
pci_configure_eetlp_prefix(dev);
|
||||
pci_configure_serr(dev);
|
||||
|
||||
memset(&hpp, 0, sizeof(hpp));
|
||||
ret = pci_get_hp_params(dev, &hpp);
|
||||
|
@ -2139,7 +2139,7 @@ static void quirk_netmos(struct pci_dev *dev)
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
|
||||
dev->subsystem_device == 0x0299)
|
||||
return;
|
||||
/* else: fall through */
|
||||
/* else, fall through */
|
||||
case PCI_DEVICE_ID_NETMOS_9735:
|
||||
case PCI_DEVICE_ID_NETMOS_9745:
|
||||
case PCI_DEVICE_ID_NETMOS_9845:
|
||||
@ -4520,6 +4520,8 @@ static const struct pci_dev_acs_enabled {
|
||||
/* QCOM QDF2xxx root ports */
|
||||
{ PCI_VENDOR_ID_QCOM, 0x0400, pci_quirk_qcom_rp_acs },
|
||||
{ PCI_VENDOR_ID_QCOM, 0x0401, pci_quirk_qcom_rp_acs },
|
||||
/* HXT SD4800 root ports. The ACS design is same as QCOM QDF2xxx */
|
||||
{ PCI_VENDOR_ID_HXT, 0x0401, pci_quirk_qcom_rp_acs },
|
||||
/* Intel PCH root ports */
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs },
|
||||
|
@ -735,58 +735,21 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i)
|
||||
base/limit registers must be read-only and read as 0. */
|
||||
static void pci_bridge_check_ranges(struct pci_bus *bus)
|
||||
{
|
||||
u16 io;
|
||||
u32 pmem;
|
||||
struct pci_dev *bridge = bus->self;
|
||||
struct resource *b_res;
|
||||
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
|
||||
|
||||
b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
|
||||
b_res[1].flags |= IORESOURCE_MEM;
|
||||
|
||||
pci_read_config_word(bridge, PCI_IO_BASE, &io);
|
||||
if (!io) {
|
||||
pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0);
|
||||
pci_read_config_word(bridge, PCI_IO_BASE, &io);
|
||||
pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
|
||||
}
|
||||
if (io)
|
||||
if (bridge->io_window)
|
||||
b_res[0].flags |= IORESOURCE_IO;
|
||||
|
||||
/* DECchip 21050 pass 2 errata: the bridge may miss an address
|
||||
disconnect boundary by one PCI data phase.
|
||||
Workaround: do not use prefetching on this device. */
|
||||
if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001)
|
||||
return;
|
||||
|
||||
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
|
||||
if (!pmem) {
|
||||
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,
|
||||
0xffe0fff0);
|
||||
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
|
||||
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
|
||||
}
|
||||
if (pmem) {
|
||||
if (bridge->pref_window) {
|
||||
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
|
||||
if ((pmem & PCI_PREF_RANGE_TYPE_MASK) ==
|
||||
PCI_PREF_RANGE_TYPE_64) {
|
||||
if (bridge->pref_64_window) {
|
||||
b_res[2].flags |= IORESOURCE_MEM_64;
|
||||
b_res[2].flags |= PCI_PREF_RANGE_TYPE_64;
|
||||
}
|
||||
}
|
||||
|
||||
/* double check if bridge does support 64 bit pref */
|
||||
if (b_res[2].flags & IORESOURCE_MEM_64) {
|
||||
u32 mem_base_hi, tmp;
|
||||
pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32,
|
||||
&mem_base_hi);
|
||||
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
|
||||
0xffffffff);
|
||||
pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
|
||||
if (!tmp)
|
||||
b_res[2].flags &= ~IORESOURCE_MEM_64;
|
||||
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
|
||||
mem_base_hi);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for sizing routines: find first available
|
||||
@ -1223,12 +1186,12 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
|
||||
if (!b)
|
||||
continue;
|
||||
|
||||
switch (dev->class >> 8) {
|
||||
case PCI_CLASS_BRIDGE_CARDBUS:
|
||||
switch (dev->hdr_type) {
|
||||
case PCI_HEADER_TYPE_CARDBUS:
|
||||
pci_bus_size_cardbus(b, realloc_head);
|
||||
break;
|
||||
|
||||
case PCI_CLASS_BRIDGE_PCI:
|
||||
case PCI_HEADER_TYPE_BRIDGE:
|
||||
default:
|
||||
__pci_bus_size_bridges(b, realloc_head);
|
||||
break;
|
||||
@ -1239,12 +1202,12 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
|
||||
if (pci_is_root_bus(bus))
|
||||
return;
|
||||
|
||||
switch (bus->self->class >> 8) {
|
||||
case PCI_CLASS_BRIDGE_CARDBUS:
|
||||
switch (bus->self->hdr_type) {
|
||||
case PCI_HEADER_TYPE_CARDBUS:
|
||||
/* don't size cardbuses yet. */
|
||||
break;
|
||||
|
||||
case PCI_CLASS_BRIDGE_PCI:
|
||||
case PCI_HEADER_TYPE_BRIDGE:
|
||||
pci_bridge_check_ranges(bus);
|
||||
if (bus->self->is_hotplug_bridge) {
|
||||
additional_io_size = pci_hotplug_io_size;
|
||||
@ -1393,13 +1356,13 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
|
||||
|
||||
__pci_bus_assign_resources(b, realloc_head, fail_head);
|
||||
|
||||
switch (dev->class >> 8) {
|
||||
case PCI_CLASS_BRIDGE_PCI:
|
||||
switch (dev->hdr_type) {
|
||||
case PCI_HEADER_TYPE_BRIDGE:
|
||||
if (!pci_is_enabled(dev))
|
||||
pci_setup_bridge(b);
|
||||
break;
|
||||
|
||||
case PCI_CLASS_BRIDGE_CARDBUS:
|
||||
case PCI_HEADER_TYPE_CARDBUS:
|
||||
pci_setup_cardbus(b);
|
||||
break;
|
||||
|
||||
|
@ -92,7 +92,6 @@
|
||||
#define PCH_MAX_SPBR 1023
|
||||
|
||||
/* Definition for ML7213/ML7223/ML7831 by LAPIS Semiconductor */
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
#define PCI_DEVICE_ID_ML7213_SPI 0x802c
|
||||
#define PCI_DEVICE_ID_ML7223_SPI 0x800F
|
||||
#define PCI_DEVICE_ID_ML7831_SPI 0x8816
|
||||
|
@ -192,8 +192,6 @@ enum {
|
||||
#define PCH_UART_HAL_LOOP (PCH_UART_MCR_LOOP)
|
||||
#define PCH_UART_HAL_AFE (PCH_UART_MCR_AFE)
|
||||
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
|
||||
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
|
||||
|
||||
#define DEFAULT_UARTCLK 1843200 /* 1.8432 MHz */
|
||||
|
@ -368,7 +368,6 @@ struct pch_udc_dev {
|
||||
#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939
|
||||
#define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808
|
||||
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
#define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D
|
||||
#define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808
|
||||
|
||||
|
@ -83,12 +83,12 @@ struct msi_desc {
|
||||
struct {
|
||||
u32 masked;
|
||||
struct {
|
||||
__u8 is_msix : 1;
|
||||
__u8 multiple : 3;
|
||||
__u8 multi_cap : 3;
|
||||
__u8 maskbit : 1;
|
||||
__u8 is_64 : 1;
|
||||
__u16 entry_nr;
|
||||
u8 is_msix : 1;
|
||||
u8 multiple : 3;
|
||||
u8 multi_cap : 3;
|
||||
u8 maskbit : 1;
|
||||
u8 is_64 : 1;
|
||||
u16 entry_nr;
|
||||
unsigned default_irq;
|
||||
} msi_attrib;
|
||||
union {
|
||||
|
@ -59,6 +59,8 @@ struct pci_epc_ops {
|
||||
enum pci_epc_irq_type type, u16 interrupt_num);
|
||||
int (*start)(struct pci_epc *epc);
|
||||
void (*stop)(struct pci_epc *epc);
|
||||
const struct pci_epc_features* (*get_features)(struct pci_epc *epc,
|
||||
u8 func_no);
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
@ -97,16 +99,25 @@ struct pci_epc {
|
||||
struct config_group *group;
|
||||
/* spinlock to protect against concurrent access of EP controller */
|
||||
spinlock_t lock;
|
||||
unsigned int features;
|
||||
};
|
||||
|
||||
#define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0)
|
||||
#define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3))
|
||||
#define EPC_FEATURE_MSIX_AVAILABLE BIT(4)
|
||||
#define EPC_FEATURE_SET_BAR(features, bar) \
|
||||
(features |= (EPC_FEATURE_BAR_MASK & (bar << 1)))
|
||||
#define EPC_FEATURE_GET_BAR(features) \
|
||||
((features & EPC_FEATURE_BAR_MASK) >> 1)
|
||||
/**
|
||||
* struct pci_epc_features - features supported by a EPC device per function
|
||||
* @linkup_notifier: indicate if the EPC device can notify EPF driver on link up
|
||||
* @msi_capable: indicate if the endpoint function has MSI capability
|
||||
* @msix_capable: indicate if the endpoint function has MSI-X capability
|
||||
* @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver
|
||||
* @bar_fixed_64bit: bitmap to indicate fixed 64bit BARs
|
||||
* @bar_fixed_size: Array specifying the size supported by each BAR
|
||||
*/
|
||||
struct pci_epc_features {
|
||||
unsigned int linkup_notifier : 1;
|
||||
unsigned int msi_capable : 1;
|
||||
unsigned int msix_capable : 1;
|
||||
u8 reserved_bar;
|
||||
u8 bar_fixed_64bit;
|
||||
u64 bar_fixed_size[BAR_5 + 1];
|
||||
};
|
||||
|
||||
#define to_pci_epc(device) container_of((device), struct pci_epc, dev)
|
||||
|
||||
@ -158,6 +169,10 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
|
||||
enum pci_epc_irq_type type, u16 interrupt_num);
|
||||
int pci_epc_start(struct pci_epc *epc);
|
||||
void pci_epc_stop(struct pci_epc *epc);
|
||||
const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
|
||||
u8 func_no);
|
||||
unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features
|
||||
*epc_features);
|
||||
struct pci_epc *pci_epc_get(const char *epc_name);
|
||||
void pci_epc_put(struct pci_epc *epc);
|
||||
|
||||
|
@ -373,6 +373,9 @@ struct pci_dev {
|
||||
bool match_driver; /* Skip attaching driver */
|
||||
|
||||
unsigned int transparent:1; /* Subtractive decode bridge */
|
||||
unsigned int io_window:1; /* Bridge has I/O window */
|
||||
unsigned int pref_window:1; /* Bridge has pref mem window */
|
||||
unsigned int pref_64_window:1; /* Pref mem window is 64-bit */
|
||||
unsigned int multifunction:1; /* Multi-function device */
|
||||
|
||||
unsigned int is_busmaster:1; /* Is busmaster */
|
||||
|
@ -1140,6 +1140,8 @@
|
||||
#define PCI_VENDOR_ID_TCONRAD 0x10da
|
||||
#define PCI_DEVICE_ID_TCONRAD_TOKENRING 0x0508
|
||||
|
||||
#define PCI_VENDOR_ID_ROHM 0x10db
|
||||
|
||||
#define PCI_VENDOR_ID_NVIDIA 0x10de
|
||||
#define PCI_DEVICE_ID_NVIDIA_TNT 0x0020
|
||||
#define PCI_DEVICE_ID_NVIDIA_TNT2 0x0028
|
||||
@ -2573,6 +2575,8 @@
|
||||
|
||||
#define PCI_VENDOR_ID_HYGON 0x1d94
|
||||
|
||||
#define PCI_VENDOR_ID_HXT 0x1dbf
|
||||
|
||||
#define PCI_VENDOR_ID_TEKRAM 0x1de1
|
||||
#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user