diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-05-05 13:24:11 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-05-05 13:24:11 -0700 | 
| commit | 57151b502cbc0fa6ff9074a76883fa9d9eda322e (patch) | |
| tree | b36196203c00a8a9dba9fcaeeddd8d863fca54d7 /drivers/pci | |
| parent | 7b9df264f0ab6595eabe367b04c81824a06d9227 (diff) | |
| parent | 882862aaacefcb9f723b0f7817ddafc154465d8f (diff) | |
Merge tag 'pci-v5.13-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull pci updates from Bjorn Helgaas:
 "Enumeration:
   - Release OF node when pci_scan_device() fails (Dmitry Baryshkov)
   - Add pci_disable_parity() (Bjorn Helgaas)
   - Disable Mellanox Tavor parity reporting (Heiner Kallweit)
   - Disable N2100 r8169 parity reporting (Heiner Kallweit)
   - Fix RCiEP device to RCEC association (Qiuxu Zhuo)
   - Convert sysfs "config", "rom", "reset", "label", "index",
     "acpi_index" to static attributes to help fix races in device
     enumeration (Krzysztof Wilczyński)
   - Convert sysfs "vpd" to static attribute (Heiner Kallweit, Krzysztof
     Wilczyński)
   - Use sysfs_emit() in "show" functions (Krzysztof Wilczyński)
   - Remove unused alloc_pci_root_info() return value (Krzysztof
     Wilczyński)
  PCI device hotplug:
   - Fix acpiphp reference count leak (Feilong Lin)
  Power management:
   - Fix acpi_pci_set_power_state() debug message (Rafael J. Wysocki)
   - Fix runtime PM imbalance (Dinghao Liu)
  Virtualization:
   - Increase delay after FLR to work around Intel DC P4510 NVMe erratum
     (Raphael Norwitz)
  MSI:
   - Convert rcar, tegra, xilinx to MSI domains (Marc Zyngier)
   - For rcar, xilinx, use controller address as MSI doorbell (Marc
     Zyngier)
   - Remove unused hv msi_controller struct (Marc Zyngier)
   - Remove unused PCI core msi_controller support (Marc Zyngier)
   - Remove struct msi_controller altogether (Marc Zyngier)
   - Remove unused default_teardown_msi_irqs() (Marc Zyngier)
   - Let host bridges declare their reliance on MSI domains (Marc
     Zyngier)
   - Make pci_host_common_probe() declare its reliance on MSI domains
     (Marc Zyngier)
   - Advertise mediatek lack of built-in MSI handling (Thomas Gleixner)
   - Document ways of ending up with NO_MSI (Marc Zyngier)
   - Refactor HT advertising of NO_MSI flag (Marc Zyngier)
  VPD:
   - Remove obsolete Broadcom NIC VPD length-limiting quirk (Heiner
     Kallweit)
   - Remove sysfs VPD size checking dead code (Heiner Kallweit)
   - Convert VPF sysfs file to static attribute (Heiner Kallweit)
   - Remove unnecessary pci_set_vpd_size() (Heiner Kallweit)
   - Tone down "missing VPD" message (Heiner Kallweit)
  Endpoint framework:
   - Fix NULL pointer dereference when epc_features not implemented
     (Shradha Todi)
   - Add missing destroy_workqueue() in endpoint test (Yang Yingliang)
  Amazon Annapurna Labs PCIe controller driver:
   - Fix compile testing without CONFIG_PCI_ECAM (Arnd Bergmann)
   - Fix "no symbols" warnings when compile testing with
     CONFIG_TRIM_UNUSED_KSYMS (Arnd Bergmann)
  APM X-Gene PCIe controller driver:
   - Fix cfg resource mapping regression (Dejin Zheng)
  Broadcom iProc PCIe controller driver:
   - Return zero for success of iproc_msi_irq_domain_alloc() (Pali
     Rohár)
  Broadcom STB PCIe controller driver:
   - Add reset_control_rearm() stub for !CONFIG_RESET_CONTROLLER (Jim
     Quinlan)
   - Fix use of BCM7216 reset controller (Jim Quinlan)
   - Use reset/rearm for Broadcom STB pulse reset instead of
     deassert/assert (Jim Quinlan)
   - Fix brcm_pcie_probe() error return for unsupported revision (Wei
     Yongjun)
  Cavium ThunderX PCIe controller driver:
   - Fix compile testing (Arnd Bergmann)
   - Fix "no symbols" warnings when compile testing with
     CONFIG_TRIM_UNUSED_KSYMS (Arnd Bergmann)
  Freescale Layerscape PCIe controller driver:
   - Fix ls_pcie_ep_probe() syntax error (comma for semicolon)
     (Krzysztof Wilczyński)
   - Remove layerscape-gen4 dependencies on OF and ARM64, add dependency
     on ARCH_LAYERSCAPE (Geert Uytterhoeven)
  HiSilicon HIP PCIe controller driver:
   - Remove obsolete HiSilicon PCIe DT description (Dongdong Liu)
  Intel Gateway PCIe controller driver:
   - Remove unused pcie_app_rd() (Jiapeng Chong)
  Intel VMD host bridge driver:
   - Program IRTE with Requester ID of VMD endpoint, not child device
     (Jon Derrick)
   - Disable VMD MSI-X remapping when possible so children can use more
     MSI-X vectors (Jon Derrick)
  MediaTek PCIe controller driver:
   - Configure FC and FTS for functions other than 0 (Ryder Lee)
   - Add YAML schema for MediaTek (Jianjun Wang)
   - Export pci_pio_to_address() for module use (Jianjun Wang)
   - Add MediaTek MT8192 PCIe controller driver (Jianjun Wang)
   - Add MediaTek MT8192 INTx support (Jianjun Wang)
   - Add MediaTek MT8192 MSI support (Jianjun Wang)
   - Add MediaTek MT8192 system power management support (Jianjun Wang)
   - Add missing MODULE_DEVICE_TABLE (Qiheng Lin)
  Microchip PolarFlare PCIe controller driver:
   - Make several symbols static (Wei Yongjun)
  NVIDIA Tegra PCIe controller driver:
   - Add MCFG quirks for Tegra194 ECAM errata (Vidya Sagar)
   - Make several symbols const (Rikard Falkeborn)
   - Fix Kconfig host/endpoint typo (Wesley Sheng)
  SiFive FU740 PCIe controller driver:
   - Add pcie_aux clock to prci driver (Greentime Hu)
   - Use reset-simple in prci driver for PCIe (Greentime Hu)
   - Add SiFive FU740 PCIe host controller driver and DT binding (Paul
     Walmsley, Greentime Hu)
  Synopsys DesignWare PCIe controller driver:
   - Move MSI Receiver init to dw_pcie_host_init() so it is
     re-initialized along with the RC in resume (Jisheng Zhang)
   - Move iATU detection earlier to fix regression (Hou Zhiqiang)
  TI J721E PCIe driver:
   - Add DT binding and TI j721e support for refclk to PCIe connector
     (Kishon Vijay Abraham I)
   - Add host mode and endpoint mode DT bindings for TI AM64 SoC (Kishon
     Vijay Abraham I)
  TI Keystone PCIe controller driver:
   - Use generic config accessors for TI AM65x (K3) to fix regression
     (Kishon Vijay Abraham I)
  Xilinx NWL PCIe controller driver:
   - Add support for coherent PCIe DMA traffic using CCI (Bharat Kumar
     Gogada)
   - Add optional "dma-coherent" DT property (Bharat Kumar Gogada)
  Miscellaneous:
   - Fix kernel-doc warnings (Krzysztof Wilczyński)
   - Remove unused MicroGate SyncLink device IDs (Jiri Slaby)
   - Remove redundant dev_err() for devm_ioremap_resource() failure
     (Chen Hui)
   - Remove redundant initialization (Colin Ian King)
   - Drop redundant dev_err() for platform_get_irq() errors (Krzysztof
     Wilczyński)"
* tag 'pci-v5.13-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (98 commits)
  riscv: dts: Add PCIe support for the SiFive FU740-C000 SoC
  PCI: fu740: Add SiFive FU740 PCIe host controller driver
  dt-bindings: PCI: Add SiFive FU740 PCIe host controller
  MAINTAINERS: Add maintainers for SiFive FU740 PCIe driver
  clk: sifive: Use reset-simple in prci driver for PCIe driver
  clk: sifive: Add pcie_aux clock in prci driver for PCIe driver
  PCI: brcmstb: Use reset/rearm instead of deassert/assert
  ata: ahci_brcm: Fix use of BCM7216 reset controller
  reset: add missing empty function reset_control_rearm()
  PCI: Allow VPD access for QLogic ISP2722
  PCI/VPD: Add helper pci_get_func0_dev()
  PCI/VPD: Remove pci_vpd_find_tag() SRDT handling
  PCI/VPD: Remove pci_vpd_find_tag() 'offset' argument
  PCI/VPD: Change pci_vpd_init() return type to void
  PCI/VPD: Make missing VPD message less alarming
  PCI/VPD: Remove pci_set_vpd_size()
  x86/PCI: Remove unused alloc_pci_root_info() return value
  MAINTAINERS: Add Jianjun Wang as MediaTek PCI co-maintainer
  PCI: mediatek-gen3: Add system PM support
  PCI: mediatek-gen3: Add MSI support
  ...
Diffstat (limited to 'drivers/pci')
55 files changed, 2434 insertions, 1138 deletions
| diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 0d3719407b8b..6d7d64939f82 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -480,7 +480,7 @@ EXPORT_SYMBOL_GPL(pci_pasid_features);  #define PASID_NUMBER_SHIFT	8  #define PASID_NUMBER_MASK	(0x1f << PASID_NUMBER_SHIFT)  /** - * pci_max_pasid - Get maximum number of PASIDs supported by device + * pci_max_pasids - Get maximum number of PASIDs supported by device   * @pdev: PCI device structure   *   * Returns negative value when PASID capability is not present. diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 5aa8977d7b0f..2f2c8a1729f9 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -41,7 +41,6 @@ config PCI_TEGRA  	bool "NVIDIA Tegra PCIe controller"  	depends on ARCH_TEGRA || COMPILE_TEST  	depends on PCI_MSI_IRQ_DOMAIN -	select PCI_MSI_ARCH_FALLBACKS  	help  	  Say Y here if you want support for the PCIe host controller found  	  on NVIDIA Tegra SoCs. @@ -59,7 +58,6 @@ config PCIE_RCAR_HOST  	bool "Renesas R-Car PCIe host controller"  	depends on ARCH_RENESAS || COMPILE_TEST  	depends on PCI_MSI_IRQ_DOMAIN -	select PCI_MSI_ARCH_FALLBACKS  	help  	  Say Y here if you want PCIe controller support on R-Car SoCs in host  	  mode. @@ -88,7 +86,7 @@ config PCI_HOST_GENERIC  config PCIE_XILINX  	bool "Xilinx AXI PCIe host bridge support"  	depends on OF || COMPILE_TEST -	select PCI_MSI_ARCH_FALLBACKS +	depends on PCI_MSI_IRQ_DOMAIN  	help  	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe  	  Host Bridge driver. @@ -233,6 +231,19 @@ config PCIE_MEDIATEK  	  Say Y here if you want to enable PCIe controller support on  	  MediaTek SoCs. +config PCIE_MEDIATEK_GEN3 +	tristate "MediaTek Gen3 PCIe controller" +	depends on ARCH_MEDIATEK || COMPILE_TEST +	depends on PCI_MSI_IRQ_DOMAIN +	help +	  Adds support for PCIe Gen3 MAC controller for MediaTek SoCs. +	  This PCIe controller is compatible with Gen3, Gen2 and Gen1 speed, +	  and support up to 256 MSI interrupt numbers for +	  multi-function devices. + +	  Say Y here if you want to enable Gen3 PCIe controller support on +	  MediaTek SoCs. +  config VMD  	depends on PCI_MSI && X86_64 && SRCU  	tristate "Intel Volume Management Device Driver" diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index e4559f2182f2..63e3880a3e87 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -11,10 +11,13 @@ obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar.o pcie-rcar-host.o  obj-$(CONFIG_PCIE_RCAR_EP) += pcie-rcar.o pcie-rcar-ep.o  obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o  obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o +obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o +obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o  obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o  obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o  obj-$(CONFIG_PCIE_XILINX_CPM) += pcie-xilinx-cpm.o  obj-$(CONFIG_PCI_V3_SEMI) += pci-v3-semi.o +obj-$(CONFIG_PCI_XGENE) += pci-xgene.o  obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o  obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o  obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o @@ -27,6 +30,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o  obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o  obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o  obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o +obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie-mediatek-gen3.o  obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o  obj-$(CONFIG_VMD) += vmd.o  obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o @@ -47,8 +51,10 @@ obj-y				+= mobiveil/  # ARM64 and use internal ifdefs to only build the pieces we need  # depending on whether ACPI, the DT driver, or both are enabled. -ifdef CONFIG_PCI +ifdef CONFIG_ACPI +ifdef CONFIG_PCI_QUIRKS  obj-$(CONFIG_ARM64) += pci-thunder-ecam.o  obj-$(CONFIG_ARM64) += pci-thunder-pem.o  obj-$(CONFIG_ARM64) += pci-xgene.o  endif +endif diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index 849f1e416ea5..35e61048e133 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -1,11 +1,12 @@  // SPDX-License-Identifier: GPL-2.0 -/** +/*   * pci-j721e - PCIe controller driver for TI's J721E SoCs   *   * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com   * Author: Kishon Vijay Abraham I <kishon@ti.com>   */ +#include <linux/clk.h>  #include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/io.h> @@ -50,6 +51,7 @@ enum link_status {  struct j721e_pcie {  	struct device		*dev; +	struct clk		*refclk;  	u32			mode;  	u32			num_lanes;  	struct cdns_pcie	*cdns_pcie; @@ -312,6 +314,7 @@ static int j721e_pcie_probe(struct platform_device *pdev)  	struct cdns_pcie_ep *ep;  	struct gpio_desc *gpiod;  	void __iomem *base; +	struct clk *clk;  	u32 num_lanes;  	u32 mode;  	int ret; @@ -411,6 +414,20 @@ static int j721e_pcie_probe(struct platform_device *pdev)  			goto err_get_sync;  		} +		clk = devm_clk_get_optional(dev, "pcie_refclk"); +		if (IS_ERR(clk)) { +			ret = PTR_ERR(clk); +			dev_err(dev, "failed to get pcie_refclk\n"); +			goto err_pcie_setup; +		} + +		ret = clk_prepare_enable(clk); +		if (ret) { +			dev_err(dev, "failed to enable pcie_refclk\n"); +			goto err_get_sync; +		} +		pcie->refclk = clk; +  		/*  		 * "Power Sequencing and Reset Signal Timings" table in  		 * PCI EXPRESS CARD ELECTROMECHANICAL SPECIFICATION, REV. 3.0 @@ -425,8 +442,10 @@ static int j721e_pcie_probe(struct platform_device *pdev)  		}  		ret = cdns_pcie_host_setup(rc); -		if (ret < 0) +		if (ret < 0) { +			clk_disable_unprepare(pcie->refclk);  			goto err_pcie_setup; +		}  		break;  	case PCI_MODE_EP: @@ -479,6 +498,7 @@ static int j721e_pcie_remove(struct platform_device *pdev)  	struct cdns_pcie *cdns_pcie = pcie->cdns_pcie;  	struct device *dev = &pdev->dev; +	clk_disable_unprepare(pcie->refclk);  	cdns_pcie_disable_phy(cdns_pcie);  	pm_runtime_put(dev);  	pm_runtime_disable(dev); diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 22c5529e9a65..423d35872ce4 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -280,7 +280,7 @@ config PCIE_TEGRA194_EP  	select PCIE_TEGRA194  	help  	  Enables support for the PCIe controller in the NVIDIA Tegra194 SoC to -	  work in host mode. There are two instances of PCIe controllers in +	  work in endpoint mode. There are two instances of PCIe controllers in  	  Tegra194. This controller can work either as EP or RC. In order to  	  enable host-specific features PCIE_TEGRA194_HOST must be selected and  	  in order to enable device-specific features PCIE_TEGRA194_EP must be @@ -311,6 +311,7 @@ config PCIE_AL  	depends on OF && (ARM64 || COMPILE_TEST)  	depends on PCI_MSI_IRQ_DOMAIN  	select PCIE_DW_HOST +	select PCI_ECAM  	help  	  Say Y here to enable support of the Amazon's Annapurna Labs PCIe  	  controller IP on Amazon SoCs. The PCIe controller uses the DesignWare @@ -318,4 +319,13 @@ config PCIE_AL  	  required only for DT-based platforms. ACPI platforms with the  	  Annapurna Labs PCIe controller don't need to enable this. +config PCIE_FU740 +	bool "SiFive FU740 PCIe host controller" +	depends on PCI_MSI_IRQ_DOMAIN +	depends on SOC_SIFIVE || COMPILE_TEST +	select PCIE_DW_HOST +	help +	  Say Y here if you want PCIe controller support for the SiFive +	  FU740. +  endmenu diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index a751553fa0db..eca805c1a023 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o  obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o  obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o  obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o +obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o  obj-$(CONFIG_PCI_IMX6) += pci-imx6.o  obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o  obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o @@ -17,7 +18,6 @@ obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o  obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o  obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o  obj-$(CONFIG_PCI_MESON) += pci-meson.o -obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o  obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o  obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o @@ -31,7 +31,13 @@ obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o  # ARM64 and use internal ifdefs to only build the pieces we need  # depending on whether ACPI, the DT driver, or both are enabled. -ifdef CONFIG_PCI +obj-$(CONFIG_PCIE_AL) += pcie-al.o +obj-$(CONFIG_PCI_HISI) += pcie-hisi.o + +ifdef CONFIG_ACPI +ifdef CONFIG_PCI_QUIRKS  obj-$(CONFIG_ARM64) += pcie-al.o  obj-$(CONFIG_ARM64) += pcie-hisi.o +obj-$(CONFIG_ARM64) += pcie-tegra194.o +endif  endif diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 53aa35cb3a49..bde3b2824e89 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -346,8 +346,9 @@ static const struct irq_domain_ops ks_pcie_legacy_irq_domain_ops = {  };  /** - * ks_pcie_set_dbi_mode() - Set DBI mode to access overlaid BAR mask - * registers + * ks_pcie_set_dbi_mode() - Set DBI mode to access overlaid BAR mask registers + * @ks_pcie: A pointer to the keystone_pcie structure which holds the KeyStone + *	     PCIe host controller driver information.   *   * Since modification of dbi_cs2 involves different clock domain, read the   * status back to ensure the transition is complete. @@ -367,6 +368,8 @@ static void ks_pcie_set_dbi_mode(struct keystone_pcie *ks_pcie)  /**   * ks_pcie_clear_dbi_mode() - Disable DBI mode + * @ks_pcie: A pointer to the keystone_pcie structure which holds the KeyStone + *	     PCIe host controller driver information.   *   * Since modification of dbi_cs2 involves different clock domain, read the   * status back to ensure the transition is complete. @@ -449,6 +452,7 @@ static struct pci_ops ks_child_pcie_ops = {  /**   * ks_pcie_v3_65_add_bus() - keystone add_bus post initialization + * @bus: A pointer to the PCI bus structure.   *   * This sets BAR0 to enable inbound access for MSI_IRQ register   */ @@ -488,6 +492,8 @@ static struct pci_ops ks_pcie_ops = {  /**   * ks_pcie_link_up() - Check if link up + * @pci: A pointer to the dw_pcie structure which holds the DesignWare PCIe host + *	 controller driver information.   */  static int ks_pcie_link_up(struct dw_pcie *pci)  { @@ -605,7 +611,6 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc)  /**   * ks_pcie_legacy_irq_handler() - Handle legacy interrupt - * @irq: IRQ line for legacy interrupts   * @desc: Pointer to irq descriptor   *   * Traverse through pending legacy interrupts and invoke handler for each. Also @@ -798,7 +803,8 @@ static int __init ks_pcie_host_init(struct pcie_port *pp)  	int ret;  	pp->bridge->ops = &ks_pcie_ops; -	pp->bridge->child_ops = &ks_child_pcie_ops; +	if (!ks_pcie->is_am6) +		pp->bridge->child_ops = &ks_child_pcie_ops;  	ret = ks_pcie_config_legacy_irq(ks_pcie);  	if (ret) diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index 39fe2ed5a6a2..39f4664bd84c 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -154,7 +154,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)  	pci->dev = dev;  	pci->ops = pcie->drvdata->dw_pcie_ops; -	ls_epc->bar_fixed_64bit = (1 << BAR_2) | (1 << BAR_4), +	ls_epc->bar_fixed_64bit = (1 << BAR_2) | (1 << BAR_4);  	pcie->pci = pci;  	pcie->ls_epc = ls_epc; diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 1c25d8337151..8d028a88b375 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -705,6 +705,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)  		}  	} +	dw_pcie_iatu_detect(pci); +  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");  	if (!res)  		return -EINVAL; diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 7e55b2b66182..a608ae1fad57 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -398,9 +398,9 @@ int dw_pcie_host_init(struct pcie_port *pp)  		if (ret)  			goto err_free_msi;  	} +	dw_pcie_iatu_detect(pci);  	dw_pcie_setup_rc(pp); -	dw_pcie_msi_init(pp);  	if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {  		ret = pci->ops->start_link(pci); @@ -551,6 +551,8 @@ void dw_pcie_setup_rc(struct pcie_port *pp)  		}  	} +	dw_pcie_msi_init(pp); +  	/* Setup RC BARs */  	dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);  	dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 004cb860e266..a945f0c0e73d 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -660,11 +660,9 @@ static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)  	pci->num_ob_windows = ob;  } -void dw_pcie_setup(struct dw_pcie *pci) +void dw_pcie_iatu_detect(struct dw_pcie *pci)  { -	u32 val;  	struct device *dev = pci->dev; -	struct device_node *np = dev->of_node;  	struct platform_device *pdev = to_platform_device(dev);  	if (pci->version >= 0x480A || (!pci->version && @@ -693,6 +691,13 @@ void dw_pcie_setup(struct dw_pcie *pci)  	dev_info(pci->dev, "Detected iATU regions: %u outbound, %u inbound",  		 pci->num_ob_windows, pci->num_ib_windows); +} + +void dw_pcie_setup(struct dw_pcie *pci) +{ +	u32 val; +	struct device *dev = pci->dev; +	struct device_node *np = dev->of_node;  	if (pci->link_gen > 0)  		dw_pcie_link_set_max_speed(pci, pci->link_gen); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 7247c8b01f04..7d6e9b7576be 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -306,6 +306,7 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,  void dw_pcie_disable_atu(struct dw_pcie *pci, int index,  			 enum dw_pcie_region_type type);  void dw_pcie_setup(struct dw_pcie *pci); +void dw_pcie_iatu_detect(struct dw_pcie *pci);  static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)  { diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c new file mode 100644 index 000000000000..00cde9a248b5 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-fu740.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FU740 DesignWare PCIe Controller integration + * Copyright (C) 2019-2021 SiFive, Inc. + * Paul Walmsley + * Greentime Hu + * + * Based in part on the i.MX6 PCIe host controller shim which is: + * + * Copyright (C) 2013 Kosagi + *		https://www.kosagi.com + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/resource.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/reset.h> + +#include "pcie-designware.h" + +#define to_fu740_pcie(x)	dev_get_drvdata((x)->dev) + +struct fu740_pcie { +	struct dw_pcie pci; +	void __iomem *mgmt_base; +	struct gpio_desc *reset; +	struct gpio_desc *pwren; +	struct clk *pcie_aux; +	struct reset_control *rst; +}; + +#define SIFIVE_DEVICESRESETREG		0x28 + +#define PCIEX8MGMT_PERST_N		0x0 +#define PCIEX8MGMT_APP_LTSSM_ENABLE	0x10 +#define PCIEX8MGMT_APP_HOLD_PHY_RST	0x18 +#define PCIEX8MGMT_DEVICE_TYPE		0x708 +#define PCIEX8MGMT_PHY0_CR_PARA_ADDR	0x860 +#define PCIEX8MGMT_PHY0_CR_PARA_RD_EN	0x870 +#define PCIEX8MGMT_PHY0_CR_PARA_RD_DATA	0x878 +#define PCIEX8MGMT_PHY0_CR_PARA_SEL	0x880 +#define PCIEX8MGMT_PHY0_CR_PARA_WR_DATA	0x888 +#define PCIEX8MGMT_PHY0_CR_PARA_WR_EN	0x890 +#define PCIEX8MGMT_PHY0_CR_PARA_ACK	0x898 +#define PCIEX8MGMT_PHY1_CR_PARA_ADDR	0x8a0 +#define PCIEX8MGMT_PHY1_CR_PARA_RD_EN	0x8b0 +#define PCIEX8MGMT_PHY1_CR_PARA_RD_DATA	0x8b8 +#define PCIEX8MGMT_PHY1_CR_PARA_SEL	0x8c0 +#define PCIEX8MGMT_PHY1_CR_PARA_WR_DATA	0x8c8 +#define PCIEX8MGMT_PHY1_CR_PARA_WR_EN	0x8d0 +#define PCIEX8MGMT_PHY1_CR_PARA_ACK	0x8d8 + +#define PCIEX8MGMT_PHY_CDR_TRACK_EN	BIT(0) +#define PCIEX8MGMT_PHY_LOS_THRSHLD	BIT(5) +#define PCIEX8MGMT_PHY_TERM_EN		BIT(9) +#define PCIEX8MGMT_PHY_TERM_ACDC	BIT(10) +#define PCIEX8MGMT_PHY_EN		BIT(11) +#define PCIEX8MGMT_PHY_INIT_VAL		(PCIEX8MGMT_PHY_CDR_TRACK_EN|\ +					 PCIEX8MGMT_PHY_LOS_THRSHLD|\ +					 PCIEX8MGMT_PHY_TERM_EN|\ +					 PCIEX8MGMT_PHY_TERM_ACDC|\ +					 PCIEX8MGMT_PHY_EN) + +#define PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3	0x1008 +#define PCIEX8MGMT_PHY_LANE_OFF		0x100 +#define PCIEX8MGMT_PHY_LANE0_BASE	(PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 0) +#define PCIEX8MGMT_PHY_LANE1_BASE	(PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 1) +#define PCIEX8MGMT_PHY_LANE2_BASE	(PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 2) +#define PCIEX8MGMT_PHY_LANE3_BASE	(PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 3) + +static void fu740_pcie_assert_reset(struct fu740_pcie *afp) +{ +	/* Assert PERST_N GPIO */ +	gpiod_set_value_cansleep(afp->reset, 0); +	/* Assert controller PERST_N */ +	writel_relaxed(0x0, afp->mgmt_base + PCIEX8MGMT_PERST_N); +} + +static void fu740_pcie_deassert_reset(struct fu740_pcie *afp) +{ +	/* Deassert controller PERST_N */ +	writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PERST_N); +	/* Deassert PERST_N GPIO */ +	gpiod_set_value_cansleep(afp->reset, 1); +} + +static void fu740_pcie_power_on(struct fu740_pcie *afp) +{ +	gpiod_set_value_cansleep(afp->pwren, 1); +	/* +	 * Ensure that PERST has been asserted for at least 100 ms. +	 * Section 2.2 of PCI Express Card Electromechanical Specification +	 * Revision 3.0 +	 */ +	msleep(100); +} + +static void fu740_pcie_drive_reset(struct fu740_pcie *afp) +{ +	fu740_pcie_assert_reset(afp); +	fu740_pcie_power_on(afp); +	fu740_pcie_deassert_reset(afp); +} + +static void fu740_phyregwrite(const uint8_t phy, const uint16_t addr, +			      const uint16_t wrdata, struct fu740_pcie *afp) +{ +	struct device *dev = afp->pci.dev; +	void __iomem *phy_cr_para_addr; +	void __iomem *phy_cr_para_wr_data; +	void __iomem *phy_cr_para_wr_en; +	void __iomem *phy_cr_para_ack; +	int ret, val; + +	/* Setup */ +	if (phy) { +		phy_cr_para_addr = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_ADDR; +		phy_cr_para_wr_data = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_WR_DATA; +		phy_cr_para_wr_en = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_WR_EN; +		phy_cr_para_ack = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_ACK; +	} else { +		phy_cr_para_addr = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_ADDR; +		phy_cr_para_wr_data = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_WR_DATA; +		phy_cr_para_wr_en = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_WR_EN; +		phy_cr_para_ack = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_ACK; +	} + +	writel_relaxed(addr, phy_cr_para_addr); +	writel_relaxed(wrdata, phy_cr_para_wr_data); +	writel_relaxed(1, phy_cr_para_wr_en); + +	/* Wait for wait_idle */ +	ret = readl_poll_timeout(phy_cr_para_ack, val, val, 10, 5000); +	if (ret) +		dev_warn(dev, "Wait for wait_idle state failed!\n"); + +	/* Clear */ +	writel_relaxed(0, phy_cr_para_wr_en); + +	/* Wait for ~wait_idle */ +	ret = readl_poll_timeout(phy_cr_para_ack, val, !val, 10, 5000); +	if (ret) +		dev_warn(dev, "Wait for !wait_idle state failed!\n"); +} + +static void fu740_pcie_init_phy(struct fu740_pcie *afp) +{ +	/* Enable phy cr_para_sel interfaces */ +	writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_SEL); +	writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_SEL); + +	/* +	 * Wait 10 cr_para cycles to guarantee that the registers are ready +	 * to be edited. +	 */ +	ndelay(10); + +	/* Set PHY AC termination mode */ +	fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE0_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); +	fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE1_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); +	fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE2_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); +	fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE3_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); +	fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE0_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); +	fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE1_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); +	fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE2_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); +	fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE3_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); +} + +static int fu740_pcie_start_link(struct dw_pcie *pci) +{ +	struct device *dev = pci->dev; +	struct fu740_pcie *afp = dev_get_drvdata(dev); + +	/* Enable LTSSM */ +	writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_APP_LTSSM_ENABLE); +	return 0; +} + +static int fu740_pcie_host_init(struct pcie_port *pp) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct fu740_pcie *afp = to_fu740_pcie(pci); +	struct device *dev = pci->dev; +	int ret; + +	/* Power on reset */ +	fu740_pcie_drive_reset(afp); + +	/* Enable pcieauxclk */ +	ret = clk_prepare_enable(afp->pcie_aux); +	if (ret) { +		dev_err(dev, "unable to enable pcie_aux clock\n"); +		return ret; +	} + +	/* +	 * Assert hold_phy_rst (hold the controller LTSSM in reset after +	 * power_up_rst_n for register programming with cr_para) +	 */ +	writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_APP_HOLD_PHY_RST); + +	/* Deassert power_up_rst_n */ +	ret = reset_control_deassert(afp->rst); +	if (ret) { +		dev_err(dev, "unable to deassert pcie_power_up_rst_n\n"); +		return ret; +	} + +	fu740_pcie_init_phy(afp); + +	/* Disable pcieauxclk */ +	clk_disable_unprepare(afp->pcie_aux); +	/* Clear hold_phy_rst */ +	writel_relaxed(0x0, afp->mgmt_base + PCIEX8MGMT_APP_HOLD_PHY_RST); +	/* Enable pcieauxclk */ +	ret = clk_prepare_enable(afp->pcie_aux); +	/* Set RC mode */ +	writel_relaxed(0x4, afp->mgmt_base + PCIEX8MGMT_DEVICE_TYPE); + +	return 0; +} + +static const struct dw_pcie_host_ops fu740_pcie_host_ops = { +	.host_init = fu740_pcie_host_init, +}; + +static const struct dw_pcie_ops dw_pcie_ops = { +	.start_link = fu740_pcie_start_link, +}; + +static int fu740_pcie_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct dw_pcie *pci; +	struct fu740_pcie *afp; + +	afp = devm_kzalloc(dev, sizeof(*afp), GFP_KERNEL); +	if (!afp) +		return -ENOMEM; +	pci = &afp->pci; +	pci->dev = dev; +	pci->ops = &dw_pcie_ops; +	pci->pp.ops = &fu740_pcie_host_ops; + +	/* SiFive specific region: mgmt */ +	afp->mgmt_base = devm_platform_ioremap_resource_byname(pdev, "mgmt"); +	if (IS_ERR(afp->mgmt_base)) +		return PTR_ERR(afp->mgmt_base); + +	/* Fetch GPIOs */ +	afp->reset = devm_gpiod_get_optional(dev, "reset-gpios", GPIOD_OUT_LOW); +	if (IS_ERR(afp->reset)) +		return dev_err_probe(dev, PTR_ERR(afp->reset), "unable to get reset-gpios\n"); + +	afp->pwren = devm_gpiod_get_optional(dev, "pwren-gpios", GPIOD_OUT_LOW); +	if (IS_ERR(afp->pwren)) +		return dev_err_probe(dev, PTR_ERR(afp->pwren), "unable to get pwren-gpios\n"); + +	/* Fetch clocks */ +	afp->pcie_aux = devm_clk_get(dev, "pcie_aux"); +	if (IS_ERR(afp->pcie_aux)) +		return dev_err_probe(dev, PTR_ERR(afp->pcie_aux), +					     "pcie_aux clock source missing or invalid\n"); + +	/* Fetch reset */ +	afp->rst = devm_reset_control_get_exclusive(dev, NULL); +	if (IS_ERR(afp->rst)) +		return dev_err_probe(dev, PTR_ERR(afp->rst), "unable to get reset\n"); + +	platform_set_drvdata(pdev, afp); + +	return dw_pcie_host_init(&pci->pp); +} + +static void fu740_pcie_shutdown(struct platform_device *pdev) +{ +	struct fu740_pcie *afp = platform_get_drvdata(pdev); + +	/* Bring down link, so bootloader gets clean state in case of reboot */ +	fu740_pcie_assert_reset(afp); +} + +static const struct of_device_id fu740_pcie_of_match[] = { +	{ .compatible = "sifive,fu740-pcie", }, +	{}, +}; + +static struct platform_driver fu740_pcie_driver = { +	.driver = { +		   .name = "fu740-pcie", +		   .of_match_table = fu740_pcie_of_match, +		   .suppress_bind_attrs = true, +	}, +	.probe = fu740_pcie_probe, +	.shutdown = fu740_pcie_shutdown, +}; + +builtin_platform_driver(fu740_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 0cedd1f95f37..f89a7d24ba28 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -81,11 +81,6 @@ static void pcie_update_bits(void __iomem *base, u32 ofs, u32 mask, u32 val)  		writel(val, base + ofs);  } -static inline u32 pcie_app_rd(struct intel_pcie_port *lpp, u32 ofs) -{ -	return readl(lpp->app_base + ofs); -} -  static inline void pcie_app_wr(struct intel_pcie_port *lpp, u32 ofs, u32 val)  {  	writel(val, lpp->app_base + ofs); diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 6fa216e52d14..bafd2c6ab3c2 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -22,6 +22,8 @@  #include <linux/of_irq.h>  #include <linux/of_pci.h>  #include <linux/pci.h> +#include <linux/pci-acpi.h> +#include <linux/pci-ecam.h>  #include <linux/phy/phy.h>  #include <linux/pinctrl/consumer.h>  #include <linux/platform_device.h> @@ -311,6 +313,104 @@ struct tegra_pcie_dw_of_data {  	enum dw_pcie_device_mode mode;  }; +#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) +struct tegra194_pcie_ecam  { +	void __iomem *config_base; +	void __iomem *iatu_base; +	void __iomem *dbi_base; +}; + +static int tegra194_acpi_init(struct pci_config_window *cfg) +{ +	struct device *dev = cfg->parent; +	struct tegra194_pcie_ecam *pcie_ecam; + +	pcie_ecam = devm_kzalloc(dev, sizeof(*pcie_ecam), GFP_KERNEL); +	if (!pcie_ecam) +		return -ENOMEM; + +	pcie_ecam->config_base = cfg->win; +	pcie_ecam->iatu_base = cfg->win + SZ_256K; +	pcie_ecam->dbi_base = cfg->win + SZ_512K; +	cfg->priv = pcie_ecam; + +	return 0; +} + +static void atu_reg_write(struct tegra194_pcie_ecam *pcie_ecam, int index, +			  u32 val, u32 reg) +{ +	u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + +	writel(val, pcie_ecam->iatu_base + offset + reg); +} + +static void program_outbound_atu(struct tegra194_pcie_ecam *pcie_ecam, +				 int index, int type, u64 cpu_addr, +				 u64 pci_addr, u64 size) +{ +	atu_reg_write(pcie_ecam, index, lower_32_bits(cpu_addr), +		      PCIE_ATU_LOWER_BASE); +	atu_reg_write(pcie_ecam, index, upper_32_bits(cpu_addr), +		      PCIE_ATU_UPPER_BASE); +	atu_reg_write(pcie_ecam, index, lower_32_bits(pci_addr), +		      PCIE_ATU_LOWER_TARGET); +	atu_reg_write(pcie_ecam, index, lower_32_bits(cpu_addr + size - 1), +		      PCIE_ATU_LIMIT); +	atu_reg_write(pcie_ecam, index, upper_32_bits(pci_addr), +		      PCIE_ATU_UPPER_TARGET); +	atu_reg_write(pcie_ecam, index, type, PCIE_ATU_CR1); +	atu_reg_write(pcie_ecam, index, PCIE_ATU_ENABLE, PCIE_ATU_CR2); +} + +static void __iomem *tegra194_map_bus(struct pci_bus *bus, +				      unsigned int devfn, int where) +{ +	struct pci_config_window *cfg = bus->sysdata; +	struct tegra194_pcie_ecam *pcie_ecam = cfg->priv; +	u32 busdev; +	int type; + +	if (bus->number < cfg->busr.start || bus->number > cfg->busr.end) +		return NULL; + +	if (bus->number == cfg->busr.start) { +		if (PCI_SLOT(devfn) == 0) +			return pcie_ecam->dbi_base + where; +		else +			return NULL; +	} + +	busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | +		 PCIE_ATU_FUNC(PCI_FUNC(devfn)); + +	if (bus->parent->number == cfg->busr.start) { +		if (PCI_SLOT(devfn) == 0) +			type = PCIE_ATU_TYPE_CFG0; +		else +			return NULL; +	} else { +		type = PCIE_ATU_TYPE_CFG1; +	} + +	program_outbound_atu(pcie_ecam, 0, type, cfg->res.start, busdev, +			     SZ_256K); + +	return pcie_ecam->config_base + where; +} + +const struct pci_ecam_ops tegra194_pcie_ops = { +	.init		= tegra194_acpi_init, +	.pci_ops	= { +		.map_bus	= tegra194_map_bus, +		.read		= pci_generic_config_read, +		.write		= pci_generic_config_write, +	} +}; +#endif /* defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) */ + +#ifdef CONFIG_PCIE_TEGRA194 +  static inline struct tegra_pcie_dw *to_tegra_pcie(struct dw_pcie *pci)  {  	return container_of(pci, struct tegra_pcie_dw, pci); @@ -1019,7 +1119,7 @@ static const struct dw_pcie_ops tegra_dw_pcie_ops = {  	.stop_link = tegra_pcie_dw_stop_link,  }; -static struct dw_pcie_host_ops tegra_pcie_dw_host_ops = { +static const struct dw_pcie_host_ops tegra_pcie_dw_host_ops = {  	.host_init = tegra_pcie_dw_host_init,  }; @@ -1645,7 +1745,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)  	if (pcie->ep_state == EP_STATE_ENABLED)  		return; -	ret = pm_runtime_get_sync(dev); +	ret = pm_runtime_resume_and_get(dev);  	if (ret < 0) {  		dev_err(dev, "Failed to get runtime sync for PCIe dev: %d\n",  			ret); @@ -1881,7 +1981,7 @@ tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)  	return &tegra_pcie_epc_features;  } -static struct dw_pcie_ep_ops pcie_ep_ops = { +static const struct dw_pcie_ep_ops pcie_ep_ops = {  	.raise_irq = tegra_pcie_ep_raise_irq,  	.get_features = tegra_pcie_ep_get_features,  }; @@ -2311,3 +2411,5 @@ MODULE_DEVICE_TABLE(of, tegra_pcie_dw_of_match);  MODULE_AUTHOR("Vidya Sagar <vidyas@nvidia.com>");  MODULE_DESCRIPTION("NVIDIA PCIe host controller driver");  MODULE_LICENSE("GPL v2"); + +#endif /* CONFIG_PCIE_TEGRA194 */ diff --git a/drivers/pci/controller/mobiveil/Kconfig b/drivers/pci/controller/mobiveil/Kconfig index a62d247018cf..e4643fb94e78 100644 --- a/drivers/pci/controller/mobiveil/Kconfig +++ b/drivers/pci/controller/mobiveil/Kconfig @@ -24,8 +24,7 @@ config PCIE_MOBIVEIL_PLAT  config PCIE_LAYERSCAPE_GEN4  	bool "Freescale Layerscape PCIe Gen4 controller" -	depends on PCI -	depends on OF && (ARM64 || ARCH_LAYERSCAPE) +	depends on ARCH_LAYERSCAPE || COMPILE_TEST  	depends on PCI_MSI_IRQ_DOMAIN  	select PCIE_MOBIVEIL_HOST  	help diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index 6ab694f8d283..d3924a44db02 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -79,6 +79,7 @@ int pci_host_common_probe(struct platform_device *pdev)  	bridge->sysdata = cfg;  	bridge->ops = (struct pci_ops *)&ops->pci_ops; +	bridge->msi_domain = true;  	return pci_host_probe(bridge);  } diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 1ff4ce24f4b3..6511648271b2 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -473,7 +473,6 @@ struct hv_pcibus_device {  	struct list_head dr_list;  	struct msi_domain_info msi_info; -	struct msi_controller msi_chip;  	struct irq_domain *irq_domain;  	spinlock_t retarget_msi_interrupt_lock; @@ -1866,9 +1865,6 @@ static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus)  	if (!hbus->pci_bus)  		return -ENODEV; -	hbus->pci_bus->msi = &hbus->msi_chip; -	hbus->pci_bus->msi->dev = &hbus->hdev->device; -  	pci_lock_rescan_remove();  	pci_scan_child_bus(hbus->pci_bus);  	hv_pci_assign_numa_node(hbus); diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 8fcabed7c6a6..8069bd9232d4 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -21,6 +21,7 @@  #include <linux/interrupt.h>  #include <linux/iopoll.h>  #include <linux/irq.h> +#include <linux/irqchip/chained_irq.h>  #include <linux/irqdomain.h>  #include <linux/kernel.h>  #include <linux/init.h> @@ -78,23 +79,8 @@  #define AFI_MSI_FPCI_BAR_ST	0x64  #define AFI_MSI_AXI_BAR_ST	0x68 -#define AFI_MSI_VEC0		0x6c -#define AFI_MSI_VEC1		0x70 -#define AFI_MSI_VEC2		0x74 -#define AFI_MSI_VEC3		0x78 -#define AFI_MSI_VEC4		0x7c -#define AFI_MSI_VEC5		0x80 -#define AFI_MSI_VEC6		0x84 -#define AFI_MSI_VEC7		0x88 - -#define AFI_MSI_EN_VEC0		0x8c -#define AFI_MSI_EN_VEC1		0x90 -#define AFI_MSI_EN_VEC2		0x94 -#define AFI_MSI_EN_VEC3		0x98 -#define AFI_MSI_EN_VEC4		0x9c -#define AFI_MSI_EN_VEC5		0xa0 -#define AFI_MSI_EN_VEC6		0xa4 -#define AFI_MSI_EN_VEC7		0xa8 +#define AFI_MSI_VEC(x)		(0x6c + ((x) * 4)) +#define AFI_MSI_EN_VEC(x)	(0x8c + ((x) * 4))  #define AFI_CONFIGURATION		0xac  #define  AFI_CONFIGURATION_EN_FPCI		(1 << 0) @@ -280,10 +266,10 @@  #define LINK_RETRAIN_TIMEOUT 100000 /* in usec */  struct tegra_msi { -	struct msi_controller chip;  	DECLARE_BITMAP(used, INT_PCI_MSI_NR);  	struct irq_domain *domain; -	struct mutex lock; +	struct mutex map_lock; +	spinlock_t mask_lock;  	void *virt;  	dma_addr_t phys;  	int irq; @@ -333,11 +319,6 @@ struct tegra_pcie_soc {  	} ectl;  }; -static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip) -{ -	return container_of(chip, struct tegra_msi, chip); -} -  struct tegra_pcie {  	struct device *dev; @@ -372,6 +353,11 @@ struct tegra_pcie {  	struct dentry *debugfs;  }; +static inline struct tegra_pcie *msi_to_pcie(struct tegra_msi *msi) +{ +	return container_of(msi, struct tegra_pcie, msi); +} +  struct tegra_pcie_port {  	struct tegra_pcie *pcie;  	struct device_node *np; @@ -1432,7 +1418,6 @@ static void tegra_pcie_phys_put(struct tegra_pcie *pcie)  	}  } -  static int tegra_pcie_get_resources(struct tegra_pcie *pcie)  {  	struct device *dev = pcie->dev; @@ -1509,6 +1494,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)  phys_put:  	if (soc->program_uphy)  		tegra_pcie_phys_put(pcie); +  	return err;  } @@ -1551,161 +1537,227 @@ static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port)  	afi_writel(pcie, val, AFI_PCIE_PME);  } -static int tegra_msi_alloc(struct tegra_msi *chip) -{ -	int msi; - -	mutex_lock(&chip->lock); - -	msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR); -	if (msi < INT_PCI_MSI_NR) -		set_bit(msi, chip->used); -	else -		msi = -ENOSPC; - -	mutex_unlock(&chip->lock); - -	return msi; -} - -static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq) +static void tegra_pcie_msi_irq(struct irq_desc *desc)  { -	struct device *dev = chip->chip.dev; - -	mutex_lock(&chip->lock); - -	if (!test_bit(irq, chip->used)) -		dev_err(dev, "trying to free unused MSI#%lu\n", irq); -	else -		clear_bit(irq, chip->used); - -	mutex_unlock(&chip->lock); -} - -static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) -{ -	struct tegra_pcie *pcie = data; -	struct device *dev = pcie->dev; +	struct tegra_pcie *pcie = irq_desc_get_handler_data(desc); +	struct irq_chip *chip = irq_desc_get_chip(desc);  	struct tegra_msi *msi = &pcie->msi; -	unsigned int i, processed = 0; +	struct device *dev = pcie->dev; +	unsigned int i; + +	chained_irq_enter(chip, desc);  	for (i = 0; i < 8; i++) { -		unsigned long reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); +		unsigned long reg = afi_readl(pcie, AFI_MSI_VEC(i));  		while (reg) {  			unsigned int offset = find_first_bit(®, 32);  			unsigned int index = i * 32 + offset;  			unsigned int irq; -			/* clear the interrupt */ -			afi_writel(pcie, 1 << offset, AFI_MSI_VEC0 + i * 4); - -			irq = irq_find_mapping(msi->domain, index); +			irq = irq_find_mapping(msi->domain->parent, index);  			if (irq) { -				if (test_bit(index, msi->used)) -					generic_handle_irq(irq); -				else -					dev_info(dev, "unhandled MSI\n"); +				generic_handle_irq(irq);  			} else {  				/*  				 * that's weird who triggered this?  				 * just clear it  				 */  				dev_info(dev, "unexpected MSI\n"); +				afi_writel(pcie, BIT(index % 32), AFI_MSI_VEC(index));  			}  			/* see if there's any more pending in this vector */ -			reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); - -			processed++; +			reg = afi_readl(pcie, AFI_MSI_VEC(i));  		}  	} -	return processed > 0 ? IRQ_HANDLED : IRQ_NONE; +	chained_irq_exit(chip, desc);  } -static int tegra_msi_setup_irq(struct msi_controller *chip, -			       struct pci_dev *pdev, struct msi_desc *desc) +static void tegra_msi_top_irq_ack(struct irq_data *d)  { -	struct tegra_msi *msi = to_tegra_msi(chip); -	struct msi_msg msg; -	unsigned int irq; -	int hwirq; +	irq_chip_ack_parent(d); +} -	hwirq = tegra_msi_alloc(msi); -	if (hwirq < 0) -		return hwirq; +static void tegra_msi_top_irq_mask(struct irq_data *d) +{ +	pci_msi_mask_irq(d); +	irq_chip_mask_parent(d); +} -	irq = irq_create_mapping(msi->domain, hwirq); -	if (!irq) { -		tegra_msi_free(msi, hwirq); -		return -EINVAL; -	} +static void tegra_msi_top_irq_unmask(struct irq_data *d) +{ +	pci_msi_unmask_irq(d); +	irq_chip_unmask_parent(d); +} + +static struct irq_chip tegra_msi_top_chip = { +	.name		= "Tegra PCIe MSI", +	.irq_ack	= tegra_msi_top_irq_ack, +	.irq_mask	= tegra_msi_top_irq_mask, +	.irq_unmask	= tegra_msi_top_irq_unmask, +}; -	irq_set_msi_desc(irq, desc); +static void tegra_msi_irq_ack(struct irq_data *d) +{ +	struct tegra_msi *msi = irq_data_get_irq_chip_data(d); +	struct tegra_pcie *pcie = msi_to_pcie(msi); +	unsigned int index = d->hwirq / 32; -	msg.address_lo = lower_32_bits(msi->phys); -	msg.address_hi = upper_32_bits(msi->phys); -	msg.data = hwirq; +	/* clear the interrupt */ +	afi_writel(pcie, BIT(d->hwirq % 32), AFI_MSI_VEC(index)); +} -	pci_write_msi_msg(irq, &msg); +static void tegra_msi_irq_mask(struct irq_data *d) +{ +	struct tegra_msi *msi = irq_data_get_irq_chip_data(d); +	struct tegra_pcie *pcie = msi_to_pcie(msi); +	unsigned int index = d->hwirq / 32; +	unsigned long flags; +	u32 value; -	return 0; +	spin_lock_irqsave(&msi->mask_lock, flags); +	value = afi_readl(pcie, AFI_MSI_EN_VEC(index)); +	value &= ~BIT(d->hwirq % 32); +	afi_writel(pcie, value, AFI_MSI_EN_VEC(index)); +	spin_unlock_irqrestore(&msi->mask_lock, flags);  } -static void tegra_msi_teardown_irq(struct msi_controller *chip, -				   unsigned int irq) +static void tegra_msi_irq_unmask(struct irq_data *d)  { -	struct tegra_msi *msi = to_tegra_msi(chip); -	struct irq_data *d = irq_get_irq_data(irq); -	irq_hw_number_t hwirq = irqd_to_hwirq(d); +	struct tegra_msi *msi = irq_data_get_irq_chip_data(d); +	struct tegra_pcie *pcie = msi_to_pcie(msi); +	unsigned int index = d->hwirq / 32; +	unsigned long flags; +	u32 value; -	irq_dispose_mapping(irq); -	tegra_msi_free(msi, hwirq); +	spin_lock_irqsave(&msi->mask_lock, flags); +	value = afi_readl(pcie, AFI_MSI_EN_VEC(index)); +	value |= BIT(d->hwirq % 32); +	afi_writel(pcie, value, AFI_MSI_EN_VEC(index)); +	spin_unlock_irqrestore(&msi->mask_lock, flags);  } -static struct irq_chip tegra_msi_irq_chip = { -	.name = "Tegra PCIe MSI", -	.irq_enable = pci_msi_unmask_irq, -	.irq_disable = pci_msi_mask_irq, -	.irq_mask = pci_msi_mask_irq, -	.irq_unmask = pci_msi_unmask_irq, +static int tegra_msi_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force) +{ +	return -EINVAL; +} + +static void tegra_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ +	struct tegra_msi *msi = irq_data_get_irq_chip_data(data); + +	msg->address_lo = lower_32_bits(msi->phys); +	msg->address_hi = upper_32_bits(msi->phys); +	msg->data = data->hwirq; +} + +static struct irq_chip tegra_msi_bottom_chip = { +	.name			= "Tegra MSI", +	.irq_ack		= tegra_msi_irq_ack, +	.irq_mask		= tegra_msi_irq_mask, +	.irq_unmask		= tegra_msi_irq_unmask, +	.irq_set_affinity 	= tegra_msi_set_affinity, +	.irq_compose_msi_msg	= tegra_compose_msi_msg,  }; -static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, -			 irq_hw_number_t hwirq) +static int tegra_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, +				  unsigned int nr_irqs, void *args)  { -	irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq); -	irq_set_chip_data(irq, domain->host_data); +	struct tegra_msi *msi = domain->host_data; +	unsigned int i; +	int hwirq; + +	mutex_lock(&msi->map_lock); + +	hwirq = bitmap_find_free_region(msi->used, INT_PCI_MSI_NR, order_base_2(nr_irqs)); + +	mutex_unlock(&msi->map_lock); + +	if (hwirq < 0) +		return -ENOSPC; + +	for (i = 0; i < nr_irqs; i++) +		irq_domain_set_info(domain, virq + i, hwirq + i, +				    &tegra_msi_bottom_chip, domain->host_data, +				    handle_edge_irq, NULL, NULL);  	tegra_cpuidle_pcie_irqs_in_use();  	return 0;  } -static const struct irq_domain_ops msi_domain_ops = { -	.map = tegra_msi_map, +static void tegra_msi_domain_free(struct irq_domain *domain, unsigned int virq, +				  unsigned int nr_irqs) +{ +	struct irq_data *d = irq_domain_get_irq_data(domain, virq); +	struct tegra_msi *msi = domain->host_data; + +	mutex_lock(&msi->map_lock); + +	bitmap_release_region(msi->used, d->hwirq, order_base_2(nr_irqs)); + +	mutex_unlock(&msi->map_lock); +} + +static const struct irq_domain_ops tegra_msi_domain_ops = { +	.alloc = tegra_msi_domain_alloc, +	.free = tegra_msi_domain_free, +}; + +static struct msi_domain_info tegra_msi_info = { +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | +		   MSI_FLAG_PCI_MSIX), +	.chip	= &tegra_msi_top_chip,  }; +static int tegra_allocate_domains(struct tegra_msi *msi) +{ +	struct tegra_pcie *pcie = msi_to_pcie(msi); +	struct fwnode_handle *fwnode = dev_fwnode(pcie->dev); +	struct irq_domain *parent; + +	parent = irq_domain_create_linear(fwnode, INT_PCI_MSI_NR, +					  &tegra_msi_domain_ops, msi); +	if (!parent) { +		dev_err(pcie->dev, "failed to create IRQ domain\n"); +		return -ENOMEM; +	} +	irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS); + +	msi->domain = pci_msi_create_irq_domain(fwnode, &tegra_msi_info, parent); +	if (!msi->domain) { +		dev_err(pcie->dev, "failed to create MSI domain\n"); +		irq_domain_remove(parent); +		return -ENOMEM; +	} + +	return 0; +} + +static void tegra_free_domains(struct tegra_msi *msi) +{ +	struct irq_domain *parent = msi->domain->parent; + +	irq_domain_remove(msi->domain); +	irq_domain_remove(parent); +} +  static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)  { -	struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);  	struct platform_device *pdev = to_platform_device(pcie->dev);  	struct tegra_msi *msi = &pcie->msi;  	struct device *dev = pcie->dev;  	int err; -	mutex_init(&msi->lock); - -	msi->chip.dev = dev; -	msi->chip.setup_irq = tegra_msi_setup_irq; -	msi->chip.teardown_irq = tegra_msi_teardown_irq; +	mutex_init(&msi->map_lock); +	spin_lock_init(&msi->mask_lock); -	msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR, -					    &msi_domain_ops, &msi->chip); -	if (!msi->domain) { -		dev_err(dev, "failed to create IRQ domain\n"); -		return -ENOMEM; +	if (IS_ENABLED(CONFIG_PCI_MSI)) { +		err = tegra_allocate_domains(msi); +		if (err) +			return err;  	}  	err = platform_get_irq_byname(pdev, "msi"); @@ -1714,12 +1766,7 @@ static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)  	msi->irq = err; -	err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD, -			  tegra_msi_irq_chip.name, pcie); -	if (err < 0) { -		dev_err(dev, "failed to request IRQ: %d\n", err); -		goto free_irq_domain; -	} +	irq_set_chained_handler_and_data(msi->irq, tegra_pcie_msi_irq, pcie);  	/* Though the PCIe controller can address >32-bit address space, to  	 * facilitate endpoints that support only 32-bit MSI target address, @@ -1740,14 +1787,14 @@ static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)  		goto free_irq;  	} -	host->msi = &msi->chip; -  	return 0;  free_irq: -	free_irq(msi->irq, pcie); +	irq_set_chained_handler_and_data(msi->irq, NULL, NULL);  free_irq_domain: -	irq_domain_remove(msi->domain); +	if (IS_ENABLED(CONFIG_PCI_MSI)) +		tegra_free_domains(msi); +  	return err;  } @@ -1755,22 +1802,18 @@ static void tegra_pcie_enable_msi(struct tegra_pcie *pcie)  {  	const struct tegra_pcie_soc *soc = pcie->soc;  	struct tegra_msi *msi = &pcie->msi; -	u32 reg; +	u32 reg, msi_state[INT_PCI_MSI_NR / 32]; +	int i;  	afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST);  	afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST);  	/* this register is in 4K increments */  	afi_writel(pcie, 1, AFI_MSI_BAR_SZ); -	/* enable all MSI vectors */ -	afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC0); -	afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC1); -	afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC2); -	afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC3); -	afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC4); -	afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC5); -	afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC6); -	afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC7); +	/* Restore the MSI allocation state */ +	bitmap_to_arr32(msi_state, msi->used, INT_PCI_MSI_NR); +	for (i = 0; i < ARRAY_SIZE(msi_state); i++) +		afi_writel(pcie, msi_state[i], AFI_MSI_EN_VEC(i));  	/* and unmask the MSI interrupt */  	reg = afi_readl(pcie, AFI_INTR_MASK); @@ -1786,16 +1829,16 @@ static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie)  	dma_free_attrs(pcie->dev, PAGE_SIZE, msi->virt, msi->phys,  		       DMA_ATTR_NO_KERNEL_MAPPING); -	if (msi->irq > 0) -		free_irq(msi->irq, pcie); -  	for (i = 0; i < INT_PCI_MSI_NR; i++) {  		irq = irq_find_mapping(msi->domain, i);  		if (irq > 0) -			irq_dispose_mapping(irq); +			irq_domain_free_irqs(irq, 1);  	} -	irq_domain_remove(msi->domain); +	irq_set_chained_handler_and_data(msi->irq, NULL, NULL); + +	if (IS_ENABLED(CONFIG_PCI_MSI)) +		tegra_free_domains(msi);  }  static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) @@ -1807,16 +1850,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)  	value &= ~AFI_INTR_MASK_MSI_MASK;  	afi_writel(pcie, value, AFI_INTR_MASK); -	/* disable all MSI vectors */ -	afi_writel(pcie, 0, AFI_MSI_EN_VEC0); -	afi_writel(pcie, 0, AFI_MSI_EN_VEC1); -	afi_writel(pcie, 0, AFI_MSI_EN_VEC2); -	afi_writel(pcie, 0, AFI_MSI_EN_VEC3); -	afi_writel(pcie, 0, AFI_MSI_EN_VEC4); -	afi_writel(pcie, 0, AFI_MSI_EN_VEC5); -	afi_writel(pcie, 0, AFI_MSI_EN_VEC6); -	afi_writel(pcie, 0, AFI_MSI_EN_VEC7); -  	return 0;  } diff --git a/drivers/pci/controller/pci-thunder-ecam.c b/drivers/pci/controller/pci-thunder-ecam.c index f964fd26f7e0..ffd84656544f 100644 --- a/drivers/pci/controller/pci-thunder-ecam.c +++ b/drivers/pci/controller/pci-thunder-ecam.c @@ -116,7 +116,7 @@ static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,  	 * the config space access window.  Since we are working with  	 * the high-order 32 bits, shift everything down by 32 bits.  	 */ -	node_bits = (cfg->res.start >> 32) & (1 << 12); +	node_bits = upper_32_bits(cfg->res.start) & (1 << 12);  	v |= node_bits;  	set_val(v, where, size, val); diff --git a/drivers/pci/controller/pci-thunder-pem.c b/drivers/pci/controller/pci-thunder-pem.c index 1a3f70ac61fc..0660b9da204f 100644 --- a/drivers/pci/controller/pci-thunder-pem.c +++ b/drivers/pci/controller/pci-thunder-pem.c @@ -12,6 +12,7 @@  #include <linux/pci-acpi.h>  #include <linux/pci-ecam.h>  #include <linux/platform_device.h> +#include <linux/io-64-nonatomic-lo-hi.h>  #include "../pci.h"  #if defined(CONFIG_PCI_HOST_THUNDER_PEM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) @@ -324,9 +325,9 @@ static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg,  	 * structure here for the BAR.  	 */  	bar4_start = res_pem->start + 0xf00000; -	pem_pci->ea_entry[0] = (u32)bar4_start | 2; -	pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u; -	pem_pci->ea_entry[2] = (u32)(bar4_start >> 32); +	pem_pci->ea_entry[0] = lower_32_bits(bar4_start) | 2; +	pem_pci->ea_entry[1] = lower_32_bits(res_pem->end - bar4_start) & ~3u; +	pem_pci->ea_entry[2] = upper_32_bits(bar4_start);  	cfg->priv = pem_pci;  	return 0; @@ -334,9 +335,9 @@ static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg,  #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) -#define PEM_RES_BASE		0x87e0c0000000UL -#define PEM_NODE_MASK		GENMASK(45, 44) -#define PEM_INDX_MASK		GENMASK(26, 24) +#define PEM_RES_BASE		0x87e0c0000000ULL +#define PEM_NODE_MASK		GENMASK_ULL(45, 44) +#define PEM_INDX_MASK		GENMASK_ULL(26, 24)  #define PEM_MIN_DOM_IN_NODE	4  #define PEM_MAX_DOM_IN_NODE	10 diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index 2afdc865253e..7f503dd4ff81 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -354,7 +354,8 @@ static int xgene_pcie_map_reg(struct xgene_pcie_port *port,  	if (IS_ERR(port->csr_base))  		return PTR_ERR(port->csr_base); -	port->cfg_base = devm_platform_ioremap_resource_byname(pdev, "cfg"); +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); +	port->cfg_base = devm_ioremap_resource(dev, res);  	if (IS_ERR(port->cfg_base))  		return PTR_ERR(port->cfg_base);  	port->cfg_addr = res->start; diff --git a/drivers/pci/controller/pcie-altera-msi.c b/drivers/pci/controller/pcie-altera-msi.c index 42691dd8ebef..98aa1dccc6e6 100644 --- a/drivers/pci/controller/pcie-altera-msi.c +++ b/drivers/pci/controller/pcie-altera-msi.c @@ -236,10 +236,8 @@ static int altera_msi_probe(struct platform_device *pdev)  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,  					   "vector_slave");  	msi->vector_base = devm_ioremap_resource(&pdev->dev, res); -	if (IS_ERR(msi->vector_base)) { -		dev_err(&pdev->dev, "failed to map vector_slave memory\n"); +	if (IS_ERR(msi->vector_base))  		return PTR_ERR(msi->vector_base); -	}  	msi->vector_phy = res->start; diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index e330e6811f0b..08bc788d9422 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1148,6 +1148,7 @@ static int brcm_pcie_suspend(struct device *dev)  	brcm_pcie_turn_off(pcie);  	ret = brcm_phy_stop(pcie); +	reset_control_rearm(pcie->rescal);  	clk_disable_unprepare(pcie->clk);  	return ret; @@ -1163,9 +1164,13 @@ static int brcm_pcie_resume(struct device *dev)  	base = pcie->base;  	clk_prepare_enable(pcie->clk); +	ret = reset_control_reset(pcie->rescal); +	if (ret) +		goto err_disable_clk; +  	ret = brcm_phy_start(pcie);  	if (ret) -		goto err; +		goto err_reset;  	/* Take bridge out of reset so we can access the SERDES reg */  	pcie->bridge_sw_init_set(pcie, 0); @@ -1180,14 +1185,16 @@ static int brcm_pcie_resume(struct device *dev)  	ret = brcm_pcie_setup(pcie);  	if (ret) -		goto err; +		goto err_reset;  	if (pcie->msi)  		brcm_msi_set_regs(pcie->msi);  	return 0; -err: +err_reset: +	reset_control_rearm(pcie->rescal); +err_disable_clk:  	clk_disable_unprepare(pcie->clk);  	return ret;  } @@ -1197,7 +1204,7 @@ static void __brcm_pcie_remove(struct brcm_pcie *pcie)  	brcm_msi_remove(pcie);  	brcm_pcie_turn_off(pcie);  	brcm_phy_stop(pcie); -	reset_control_assert(pcie->rescal); +	reset_control_rearm(pcie->rescal);  	clk_disable_unprepare(pcie->clk);  } @@ -1278,13 +1285,13 @@ static int brcm_pcie_probe(struct platform_device *pdev)  		return PTR_ERR(pcie->perst_reset);  	} -	ret = reset_control_deassert(pcie->rescal); +	ret = reset_control_reset(pcie->rescal);  	if (ret)  		dev_err(&pdev->dev, "failed to deassert 'rescal'\n");  	ret = brcm_phy_start(pcie);  	if (ret) { -		reset_control_assert(pcie->rescal); +		reset_control_rearm(pcie->rescal);  		clk_disable_unprepare(pcie->clk);  		return ret;  	} @@ -1296,6 +1303,7 @@ static int brcm_pcie_probe(struct platform_device *pdev)  	pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION);  	if (pcie->type == BCM4908 && pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) {  		dev_err(pcie->dev, "hardware revision with unsupported PERST# setup\n"); +		ret = -ENODEV;  		goto fail;  	} diff --git a/drivers/pci/controller/pcie-iproc-msi.c b/drivers/pci/controller/pcie-iproc-msi.c index 908475d27e0e..eede4e8f3f75 100644 --- a/drivers/pci/controller/pcie-iproc-msi.c +++ b/drivers/pci/controller/pcie-iproc-msi.c @@ -271,7 +271,7 @@ static int iproc_msi_irq_domain_alloc(struct irq_domain *domain,  				    NULL, NULL);  	} -	return hwirq; +	return 0;  }  static void iproc_msi_irq_domain_free(struct irq_domain *domain, diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c new file mode 100644 index 000000000000..3c5b97716d40 --- /dev/null +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -0,0 +1,1027 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek PCIe host controller driver. + * + * Copyright (c) 2020 MediaTek Inc. + * Author: Jianjun Wang <jianjun.wang@mediatek.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/pci.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include "../pci.h" + +#define PCIE_SETTING_REG		0x80 +#define PCIE_PCI_IDS_1			0x9c +#define PCI_CLASS(class)		(class << 8) +#define PCIE_RC_MODE			BIT(0) + +#define PCIE_CFGNUM_REG			0x140 +#define PCIE_CFG_DEVFN(devfn)		((devfn) & GENMASK(7, 0)) +#define PCIE_CFG_BUS(bus)		(((bus) << 8) & GENMASK(15, 8)) +#define PCIE_CFG_BYTE_EN(bytes)		(((bytes) << 16) & GENMASK(19, 16)) +#define PCIE_CFG_FORCE_BYTE_EN		BIT(20) +#define PCIE_CFG_OFFSET_ADDR		0x1000 +#define PCIE_CFG_HEADER(bus, devfn) \ +	(PCIE_CFG_BUS(bus) | PCIE_CFG_DEVFN(devfn)) + +#define PCIE_RST_CTRL_REG		0x148 +#define PCIE_MAC_RSTB			BIT(0) +#define PCIE_PHY_RSTB			BIT(1) +#define PCIE_BRG_RSTB			BIT(2) +#define PCIE_PE_RSTB			BIT(3) + +#define PCIE_LTSSM_STATUS_REG		0x150 +#define PCIE_LTSSM_STATE_MASK		GENMASK(28, 24) +#define PCIE_LTSSM_STATE(val)		((val & PCIE_LTSSM_STATE_MASK) >> 24) +#define PCIE_LTSSM_STATE_L2_IDLE	0x14 + +#define PCIE_LINK_STATUS_REG		0x154 +#define PCIE_PORT_LINKUP		BIT(8) + +#define PCIE_MSI_SET_NUM		8 +#define PCIE_MSI_IRQS_PER_SET		32 +#define PCIE_MSI_IRQS_NUM \ +	(PCIE_MSI_IRQS_PER_SET * PCIE_MSI_SET_NUM) + +#define PCIE_INT_ENABLE_REG		0x180 +#define PCIE_MSI_ENABLE			GENMASK(PCIE_MSI_SET_NUM + 8 - 1, 8) +#define PCIE_MSI_SHIFT			8 +#define PCIE_INTX_SHIFT			24 +#define PCIE_INTX_ENABLE \ +	GENMASK(PCIE_INTX_SHIFT + PCI_NUM_INTX - 1, PCIE_INTX_SHIFT) + +#define PCIE_INT_STATUS_REG		0x184 +#define PCIE_MSI_SET_ENABLE_REG		0x190 +#define PCIE_MSI_SET_ENABLE		GENMASK(PCIE_MSI_SET_NUM - 1, 0) + +#define PCIE_MSI_SET_BASE_REG		0xc00 +#define PCIE_MSI_SET_OFFSET		0x10 +#define PCIE_MSI_SET_STATUS_OFFSET	0x04 +#define PCIE_MSI_SET_ENABLE_OFFSET	0x08 + +#define PCIE_MSI_SET_ADDR_HI_BASE	0xc80 +#define PCIE_MSI_SET_ADDR_HI_OFFSET	0x04 + +#define PCIE_ICMD_PM_REG		0x198 +#define PCIE_TURN_OFF_LINK		BIT(4) + +#define PCIE_TRANS_TABLE_BASE_REG	0x800 +#define PCIE_ATR_SRC_ADDR_MSB_OFFSET	0x4 +#define PCIE_ATR_TRSL_ADDR_LSB_OFFSET	0x8 +#define PCIE_ATR_TRSL_ADDR_MSB_OFFSET	0xc +#define PCIE_ATR_TRSL_PARAM_OFFSET	0x10 +#define PCIE_ATR_TLB_SET_OFFSET		0x20 + +#define PCIE_MAX_TRANS_TABLES		8 +#define PCIE_ATR_EN			BIT(0) +#define PCIE_ATR_SIZE(size) \ +	(((((size) - 1) << 1) & GENMASK(6, 1)) | PCIE_ATR_EN) +#define PCIE_ATR_ID(id)			((id) & GENMASK(3, 0)) +#define PCIE_ATR_TYPE_MEM		PCIE_ATR_ID(0) +#define PCIE_ATR_TYPE_IO		PCIE_ATR_ID(1) +#define PCIE_ATR_TLP_TYPE(type)		(((type) << 16) & GENMASK(18, 16)) +#define PCIE_ATR_TLP_TYPE_MEM		PCIE_ATR_TLP_TYPE(0) +#define PCIE_ATR_TLP_TYPE_IO		PCIE_ATR_TLP_TYPE(2) + +/** + * struct mtk_msi_set - MSI information for each set + * @base: IO mapped register base + * @msg_addr: MSI message address + * @saved_irq_state: IRQ enable state saved at suspend time + */ +struct mtk_msi_set { +	void __iomem *base; +	phys_addr_t msg_addr; +	u32 saved_irq_state; +}; + +/** + * struct mtk_pcie_port - PCIe port information + * @dev: pointer to PCIe device + * @base: IO mapped register base + * @reg_base: physical register base + * @mac_reset: MAC reset control + * @phy_reset: PHY reset control + * @phy: PHY controller block + * @clks: PCIe clocks + * @num_clks: PCIe clocks count for this port + * @irq: PCIe controller interrupt number + * @saved_irq_state: IRQ enable state saved at suspend time + * @irq_lock: lock protecting IRQ register access + * @intx_domain: legacy INTx IRQ domain + * @msi_domain: MSI IRQ domain + * @msi_bottom_domain: MSI IRQ bottom domain + * @msi_sets: MSI sets information + * @lock: lock protecting IRQ bit map + * @msi_irq_in_use: bit map for assigned MSI IRQ + */ +struct mtk_pcie_port { +	struct device *dev; +	void __iomem *base; +	phys_addr_t reg_base; +	struct reset_control *mac_reset; +	struct reset_control *phy_reset; +	struct phy *phy; +	struct clk_bulk_data *clks; +	int num_clks; + +	int irq; +	u32 saved_irq_state; +	raw_spinlock_t irq_lock; +	struct irq_domain *intx_domain; +	struct irq_domain *msi_domain; +	struct irq_domain *msi_bottom_domain; +	struct mtk_msi_set msi_sets[PCIE_MSI_SET_NUM]; +	struct mutex lock; +	DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_IRQS_NUM); +}; + +/** + * mtk_pcie_config_tlp_header() - Configure a configuration TLP header + * @bus: PCI bus to query + * @devfn: device/function number + * @where: offset in config space + * @size: data size in TLP header + * + * Set byte enable field and device information in configuration TLP header. + */ +static void mtk_pcie_config_tlp_header(struct pci_bus *bus, unsigned int devfn, +					int where, int size) +{ +	struct mtk_pcie_port *port = bus->sysdata; +	int bytes; +	u32 val; + +	bytes = (GENMASK(size - 1, 0) & 0xf) << (where & 0x3); + +	val = PCIE_CFG_FORCE_BYTE_EN | PCIE_CFG_BYTE_EN(bytes) | +	      PCIE_CFG_HEADER(bus->number, devfn); + +	writel_relaxed(val, port->base + PCIE_CFGNUM_REG); +} + +static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, +				      int where) +{ +	struct mtk_pcie_port *port = bus->sysdata; + +	return port->base + PCIE_CFG_OFFSET_ADDR + where; +} + +static int mtk_pcie_config_read(struct pci_bus *bus, unsigned int devfn, +				int where, int size, u32 *val) +{ +	mtk_pcie_config_tlp_header(bus, devfn, where, size); + +	return pci_generic_config_read32(bus, devfn, where, size, val); +} + +static int mtk_pcie_config_write(struct pci_bus *bus, unsigned int devfn, +				 int where, int size, u32 val) +{ +	mtk_pcie_config_tlp_header(bus, devfn, where, size); + +	if (size <= 2) +		val <<= (where & 0x3) * 8; + +	return pci_generic_config_write32(bus, devfn, where, 4, val); +} + +static struct pci_ops mtk_pcie_ops = { +	.map_bus = mtk_pcie_map_bus, +	.read  = mtk_pcie_config_read, +	.write = mtk_pcie_config_write, +}; + +static int mtk_pcie_set_trans_table(struct mtk_pcie_port *port, +				    resource_size_t cpu_addr, +				    resource_size_t pci_addr, +				    resource_size_t size, +				    unsigned long type, int num) +{ +	void __iomem *table; +	u32 val; + +	if (num >= PCIE_MAX_TRANS_TABLES) { +		dev_err(port->dev, "not enough translate table for addr: %#llx, limited to [%d]\n", +			(unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES); +		return -ENODEV; +	} + +	table = port->base + PCIE_TRANS_TABLE_BASE_REG + +		num * PCIE_ATR_TLB_SET_OFFSET; + +	writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(size) - 1), +		       table); +	writel_relaxed(upper_32_bits(cpu_addr), +		       table + PCIE_ATR_SRC_ADDR_MSB_OFFSET); +	writel_relaxed(lower_32_bits(pci_addr), +		       table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET); +	writel_relaxed(upper_32_bits(pci_addr), +		       table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET); + +	if (type == IORESOURCE_IO) +		val = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO; +	else +		val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM; + +	writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET); + +	return 0; +} + +static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) +{ +	int i; +	u32 val; + +	for (i = 0; i < PCIE_MSI_SET_NUM; i++) { +		struct mtk_msi_set *msi_set = &port->msi_sets[i]; + +		msi_set->base = port->base + PCIE_MSI_SET_BASE_REG + +				i * PCIE_MSI_SET_OFFSET; +		msi_set->msg_addr = port->reg_base + PCIE_MSI_SET_BASE_REG + +				    i * PCIE_MSI_SET_OFFSET; + +		/* Configure the MSI capture address */ +		writel_relaxed(lower_32_bits(msi_set->msg_addr), msi_set->base); +		writel_relaxed(upper_32_bits(msi_set->msg_addr), +			       port->base + PCIE_MSI_SET_ADDR_HI_BASE + +			       i * PCIE_MSI_SET_ADDR_HI_OFFSET); +	} + +	val = readl_relaxed(port->base + PCIE_MSI_SET_ENABLE_REG); +	val |= PCIE_MSI_SET_ENABLE; +	writel_relaxed(val, port->base + PCIE_MSI_SET_ENABLE_REG); + +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); +	val |= PCIE_MSI_ENABLE; +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG); +} + +static int mtk_pcie_startup_port(struct mtk_pcie_port *port) +{ +	struct resource_entry *entry; +	struct pci_host_bridge *host = pci_host_bridge_from_priv(port); +	unsigned int table_index = 0; +	int err; +	u32 val; + +	/* Set as RC mode */ +	val = readl_relaxed(port->base + PCIE_SETTING_REG); +	val |= PCIE_RC_MODE; +	writel_relaxed(val, port->base + PCIE_SETTING_REG); + +	/* Set class code */ +	val = readl_relaxed(port->base + PCIE_PCI_IDS_1); +	val &= ~GENMASK(31, 8); +	val |= PCI_CLASS(PCI_CLASS_BRIDGE_PCI << 8); +	writel_relaxed(val, port->base + PCIE_PCI_IDS_1); + +	/* Mask all INTx interrupts */ +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); +	val &= ~PCIE_INTX_ENABLE; +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG); + +	/* Assert all reset signals */ +	val = readl_relaxed(port->base + PCIE_RST_CTRL_REG); +	val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | PCIE_PE_RSTB; +	writel_relaxed(val, port->base + PCIE_RST_CTRL_REG); + +	/* +	 * Described in PCIe CEM specification setctions 2.2 (PERST# Signal) +	 * and 2.2.1 (Initial Power-Up (G3 to S0)). +	 * The deassertion of PERST# should be delayed 100ms (TPVPERL) +	 * for the power and clock to become stable. +	 */ +	msleep(100); + +	/* De-assert reset signals */ +	val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | PCIE_PE_RSTB); +	writel_relaxed(val, port->base + PCIE_RST_CTRL_REG); + +	/* Check if the link is up or not */ +	err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_REG, val, +				 !!(val & PCIE_PORT_LINKUP), 20, +				 PCI_PM_D3COLD_WAIT * USEC_PER_MSEC); +	if (err) { +		val = readl_relaxed(port->base + PCIE_LTSSM_STATUS_REG); +		dev_err(port->dev, "PCIe link down, ltssm reg val: %#x\n", val); +		return err; +	} + +	mtk_pcie_enable_msi(port); + +	/* Set PCIe translation windows */ +	resource_list_for_each_entry(entry, &host->windows) { +		struct resource *res = entry->res; +		unsigned long type = resource_type(res); +		resource_size_t cpu_addr; +		resource_size_t pci_addr; +		resource_size_t size; +		const char *range_type; + +		if (type == IORESOURCE_IO) { +			cpu_addr = pci_pio_to_address(res->start); +			range_type = "IO"; +		} else if (type == IORESOURCE_MEM) { +			cpu_addr = res->start; +			range_type = "MEM"; +		} else { +			continue; +		} + +		pci_addr = res->start - entry->offset; +		size = resource_size(res); +		err = mtk_pcie_set_trans_table(port, cpu_addr, pci_addr, size, +					       type, table_index); +		if (err) +			return err; + +		dev_dbg(port->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n", +			range_type, table_index, (unsigned long long)cpu_addr, +			(unsigned long long)pci_addr, (unsigned long long)size); + +		table_index++; +	} + +	return 0; +} + +static int mtk_pcie_set_affinity(struct irq_data *data, +				 const struct cpumask *mask, bool force) +{ +	return -EINVAL; +} + +static void mtk_pcie_msi_irq_mask(struct irq_data *data) +{ +	pci_msi_mask_irq(data); +	irq_chip_mask_parent(data); +} + +static void mtk_pcie_msi_irq_unmask(struct irq_data *data) +{ +	pci_msi_unmask_irq(data); +	irq_chip_unmask_parent(data); +} + +static struct irq_chip mtk_msi_irq_chip = { +	.irq_ack = irq_chip_ack_parent, +	.irq_mask = mtk_pcie_msi_irq_mask, +	.irq_unmask = mtk_pcie_msi_irq_unmask, +	.name = "MSI", +}; + +static struct msi_domain_info mtk_msi_domain_info = { +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | +		   MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), +	.chip	= &mtk_msi_irq_chip, +}; + +static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ +	struct mtk_msi_set *msi_set = irq_data_get_irq_chip_data(data); +	struct mtk_pcie_port *port = data->domain->host_data; +	unsigned long hwirq; + +	hwirq =	data->hwirq % PCIE_MSI_IRQS_PER_SET; + +	msg->address_hi = upper_32_bits(msi_set->msg_addr); +	msg->address_lo = lower_32_bits(msi_set->msg_addr); +	msg->data = hwirq; +	dev_dbg(port->dev, "msi#%#lx address_hi %#x address_lo %#x data %d\n", +		hwirq, msg->address_hi, msg->address_lo, msg->data); +} + +static void mtk_msi_bottom_irq_ack(struct irq_data *data) +{ +	struct mtk_msi_set *msi_set = irq_data_get_irq_chip_data(data); +	unsigned long hwirq; + +	hwirq =	data->hwirq % PCIE_MSI_IRQS_PER_SET; + +	writel_relaxed(BIT(hwirq), msi_set->base + PCIE_MSI_SET_STATUS_OFFSET); +} + +static void mtk_msi_bottom_irq_mask(struct irq_data *data) +{ +	struct mtk_msi_set *msi_set = irq_data_get_irq_chip_data(data); +	struct mtk_pcie_port *port = data->domain->host_data; +	unsigned long hwirq, flags; +	u32 val; + +	hwirq =	data->hwirq % PCIE_MSI_IRQS_PER_SET; + +	raw_spin_lock_irqsave(&port->irq_lock, flags); +	val = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); +	val &= ~BIT(hwirq); +	writel_relaxed(val, msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); +	raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +static void mtk_msi_bottom_irq_unmask(struct irq_data *data) +{ +	struct mtk_msi_set *msi_set = irq_data_get_irq_chip_data(data); +	struct mtk_pcie_port *port = data->domain->host_data; +	unsigned long hwirq, flags; +	u32 val; + +	hwirq =	data->hwirq % PCIE_MSI_IRQS_PER_SET; + +	raw_spin_lock_irqsave(&port->irq_lock, flags); +	val = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); +	val |= BIT(hwirq); +	writel_relaxed(val, msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); +	raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +static struct irq_chip mtk_msi_bottom_irq_chip = { +	.irq_ack		= mtk_msi_bottom_irq_ack, +	.irq_mask		= mtk_msi_bottom_irq_mask, +	.irq_unmask		= mtk_msi_bottom_irq_unmask, +	.irq_compose_msi_msg	= mtk_compose_msi_msg, +	.irq_set_affinity	= mtk_pcie_set_affinity, +	.name			= "MSI", +}; + +static int mtk_msi_bottom_domain_alloc(struct irq_domain *domain, +				       unsigned int virq, unsigned int nr_irqs, +				       void *arg) +{ +	struct mtk_pcie_port *port = domain->host_data; +	struct mtk_msi_set *msi_set; +	int i, hwirq, set_idx; + +	mutex_lock(&port->lock); + +	hwirq = bitmap_find_free_region(port->msi_irq_in_use, PCIE_MSI_IRQS_NUM, +					order_base_2(nr_irqs)); + +	mutex_unlock(&port->lock); + +	if (hwirq < 0) +		return -ENOSPC; + +	set_idx = hwirq / PCIE_MSI_IRQS_PER_SET; +	msi_set = &port->msi_sets[set_idx]; + +	for (i = 0; i < nr_irqs; i++) +		irq_domain_set_info(domain, virq + i, hwirq + i, +				    &mtk_msi_bottom_irq_chip, msi_set, +				    handle_edge_irq, NULL, NULL); + +	return 0; +} + +static void mtk_msi_bottom_domain_free(struct irq_domain *domain, +				       unsigned int virq, unsigned int nr_irqs) +{ +	struct mtk_pcie_port *port = domain->host_data; +	struct irq_data *data = irq_domain_get_irq_data(domain, virq); + +	mutex_lock(&port->lock); + +	bitmap_release_region(port->msi_irq_in_use, data->hwirq, +			      order_base_2(nr_irqs)); + +	mutex_unlock(&port->lock); + +	irq_domain_free_irqs_common(domain, virq, nr_irqs); +} + +static const struct irq_domain_ops mtk_msi_bottom_domain_ops = { +	.alloc = mtk_msi_bottom_domain_alloc, +	.free = mtk_msi_bottom_domain_free, +}; + +static void mtk_intx_mask(struct irq_data *data) +{ +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&port->irq_lock, flags); +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); +	val &= ~BIT(data->hwirq + PCIE_INTX_SHIFT); +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG); +	raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +static void mtk_intx_unmask(struct irq_data *data) +{ +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&port->irq_lock, flags); +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); +	val |= BIT(data->hwirq + PCIE_INTX_SHIFT); +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG); +	raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +/** + * mtk_intx_eoi() - Clear INTx IRQ status at the end of interrupt + * @data: pointer to chip specific data + * + * As an emulated level IRQ, its interrupt status will remain + * until the corresponding de-assert message is received; hence that + * the status can only be cleared when the interrupt has been serviced. + */ +static void mtk_intx_eoi(struct irq_data *data) +{ +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); +	unsigned long hwirq; + +	hwirq = data->hwirq + PCIE_INTX_SHIFT; +	writel_relaxed(BIT(hwirq), port->base + PCIE_INT_STATUS_REG); +} + +static struct irq_chip mtk_intx_irq_chip = { +	.irq_mask		= mtk_intx_mask, +	.irq_unmask		= mtk_intx_unmask, +	.irq_eoi		= mtk_intx_eoi, +	.irq_set_affinity	= mtk_pcie_set_affinity, +	.name			= "INTx", +}; + +static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +			     irq_hw_number_t hwirq) +{ +	irq_set_chip_data(irq, domain->host_data); +	irq_set_chip_and_handler_name(irq, &mtk_intx_irq_chip, +				      handle_fasteoi_irq, "INTx"); +	return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { +	.map = mtk_pcie_intx_map, +}; + +static int mtk_pcie_init_irq_domains(struct mtk_pcie_port *port) +{ +	struct device *dev = port->dev; +	struct device_node *intc_node, *node = dev->of_node; +	int ret; + +	raw_spin_lock_init(&port->irq_lock); + +	/* Setup INTx */ +	intc_node = of_get_child_by_name(node, "interrupt-controller"); +	if (!intc_node) { +		dev_err(dev, "missing interrupt-controller node\n"); +		return -ENODEV; +	} + +	port->intx_domain = irq_domain_add_linear(intc_node, PCI_NUM_INTX, +						  &intx_domain_ops, port); +	if (!port->intx_domain) { +		dev_err(dev, "failed to create INTx IRQ domain\n"); +		return -ENODEV; +	} + +	/* Setup MSI */ +	mutex_init(&port->lock); + +	port->msi_bottom_domain = irq_domain_add_linear(node, PCIE_MSI_IRQS_NUM, +				  &mtk_msi_bottom_domain_ops, port); +	if (!port->msi_bottom_domain) { +		dev_err(dev, "failed to create MSI bottom domain\n"); +		ret = -ENODEV; +		goto err_msi_bottom_domain; +	} + +	port->msi_domain = pci_msi_create_irq_domain(dev->fwnode, +						     &mtk_msi_domain_info, +						     port->msi_bottom_domain); +	if (!port->msi_domain) { +		dev_err(dev, "failed to create MSI domain\n"); +		ret = -ENODEV; +		goto err_msi_domain; +	} + +	return 0; + +err_msi_domain: +	irq_domain_remove(port->msi_bottom_domain); +err_msi_bottom_domain: +	irq_domain_remove(port->intx_domain); + +	return ret; +} + +static void mtk_pcie_irq_teardown(struct mtk_pcie_port *port) +{ +	irq_set_chained_handler_and_data(port->irq, NULL, NULL); + +	if (port->intx_domain) +		irq_domain_remove(port->intx_domain); + +	if (port->msi_domain) +		irq_domain_remove(port->msi_domain); + +	if (port->msi_bottom_domain) +		irq_domain_remove(port->msi_bottom_domain); + +	irq_dispose_mapping(port->irq); +} + +static void mtk_pcie_msi_handler(struct mtk_pcie_port *port, int set_idx) +{ +	struct mtk_msi_set *msi_set = &port->msi_sets[set_idx]; +	unsigned long msi_enable, msi_status; +	unsigned int virq; +	irq_hw_number_t bit, hwirq; + +	msi_enable = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); + +	do { +		msi_status = readl_relaxed(msi_set->base + +					   PCIE_MSI_SET_STATUS_OFFSET); +		msi_status &= msi_enable; +		if (!msi_status) +			break; + +		for_each_set_bit(bit, &msi_status, PCIE_MSI_IRQS_PER_SET) { +			hwirq = bit + set_idx * PCIE_MSI_IRQS_PER_SET; +			virq = irq_find_mapping(port->msi_bottom_domain, hwirq); +			generic_handle_irq(virq); +		} +	} while (true); +} + +static void mtk_pcie_irq_handler(struct irq_desc *desc) +{ +	struct mtk_pcie_port *port = irq_desc_get_handler_data(desc); +	struct irq_chip *irqchip = irq_desc_get_chip(desc); +	unsigned long status; +	unsigned int virq; +	irq_hw_number_t irq_bit = PCIE_INTX_SHIFT; + +	chained_irq_enter(irqchip, desc); + +	status = readl_relaxed(port->base + PCIE_INT_STATUS_REG); +	for_each_set_bit_from(irq_bit, &status, PCI_NUM_INTX + +			      PCIE_INTX_SHIFT) { +		virq = irq_find_mapping(port->intx_domain, +					irq_bit - PCIE_INTX_SHIFT); +		generic_handle_irq(virq); +	} + +	irq_bit = PCIE_MSI_SHIFT; +	for_each_set_bit_from(irq_bit, &status, PCIE_MSI_SET_NUM + +			      PCIE_MSI_SHIFT) { +		mtk_pcie_msi_handler(port, irq_bit - PCIE_MSI_SHIFT); + +		writel_relaxed(BIT(irq_bit), port->base + PCIE_INT_STATUS_REG); +	} + +	chained_irq_exit(irqchip, desc); +} + +static int mtk_pcie_setup_irq(struct mtk_pcie_port *port) +{ +	struct device *dev = port->dev; +	struct platform_device *pdev = to_platform_device(dev); +	int err; + +	err = mtk_pcie_init_irq_domains(port); +	if (err) +		return err; + +	port->irq = platform_get_irq(pdev, 0); +	if (port->irq < 0) +		return port->irq; + +	irq_set_chained_handler_and_data(port->irq, mtk_pcie_irq_handler, port); + +	return 0; +} + +static int mtk_pcie_parse_port(struct mtk_pcie_port *port) +{ +	struct device *dev = port->dev; +	struct platform_device *pdev = to_platform_device(dev); +	struct resource *regs; +	int ret; + +	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcie-mac"); +	if (!regs) +		return -EINVAL; +	port->base = devm_ioremap_resource(dev, regs); +	if (IS_ERR(port->base)) { +		dev_err(dev, "failed to map register base\n"); +		return PTR_ERR(port->base); +	} + +	port->reg_base = regs->start; + +	port->phy_reset = devm_reset_control_get_optional_exclusive(dev, "phy"); +	if (IS_ERR(port->phy_reset)) { +		ret = PTR_ERR(port->phy_reset); +		if (ret != -EPROBE_DEFER) +			dev_err(dev, "failed to get PHY reset\n"); + +		return ret; +	} + +	port->mac_reset = devm_reset_control_get_optional_exclusive(dev, "mac"); +	if (IS_ERR(port->mac_reset)) { +		ret = PTR_ERR(port->mac_reset); +		if (ret != -EPROBE_DEFER) +			dev_err(dev, "failed to get MAC reset\n"); + +		return ret; +	} + +	port->phy = devm_phy_optional_get(dev, "pcie-phy"); +	if (IS_ERR(port->phy)) { +		ret = PTR_ERR(port->phy); +		if (ret != -EPROBE_DEFER) +			dev_err(dev, "failed to get PHY\n"); + +		return ret; +	} + +	port->num_clks = devm_clk_bulk_get_all(dev, &port->clks); +	if (port->num_clks < 0) { +		dev_err(dev, "failed to get clocks\n"); +		return port->num_clks; +	} + +	return 0; +} + +static int mtk_pcie_power_up(struct mtk_pcie_port *port) +{ +	struct device *dev = port->dev; +	int err; + +	/* PHY power on and enable pipe clock */ +	reset_control_deassert(port->phy_reset); + +	err = phy_init(port->phy); +	if (err) { +		dev_err(dev, "failed to initialize PHY\n"); +		goto err_phy_init; +	} + +	err = phy_power_on(port->phy); +	if (err) { +		dev_err(dev, "failed to power on PHY\n"); +		goto err_phy_on; +	} + +	/* MAC power on and enable transaction layer clocks */ +	reset_control_deassert(port->mac_reset); + +	pm_runtime_enable(dev); +	pm_runtime_get_sync(dev); + +	err = clk_bulk_prepare_enable(port->num_clks, port->clks); +	if (err) { +		dev_err(dev, "failed to enable clocks\n"); +		goto err_clk_init; +	} + +	return 0; + +err_clk_init: +	pm_runtime_put_sync(dev); +	pm_runtime_disable(dev); +	reset_control_assert(port->mac_reset); +	phy_power_off(port->phy); +err_phy_on: +	phy_exit(port->phy); +err_phy_init: +	reset_control_assert(port->phy_reset); + +	return err; +} + +static void mtk_pcie_power_down(struct mtk_pcie_port *port) +{ +	clk_bulk_disable_unprepare(port->num_clks, port->clks); + +	pm_runtime_put_sync(port->dev); +	pm_runtime_disable(port->dev); +	reset_control_assert(port->mac_reset); + +	phy_power_off(port->phy); +	phy_exit(port->phy); +	reset_control_assert(port->phy_reset); +} + +static int mtk_pcie_setup(struct mtk_pcie_port *port) +{ +	int err; + +	err = mtk_pcie_parse_port(port); +	if (err) +		return err; + +	/* Don't touch the hardware registers before power up */ +	err = mtk_pcie_power_up(port); +	if (err) +		return err; + +	/* Try link up */ +	err = mtk_pcie_startup_port(port); +	if (err) +		goto err_setup; + +	err = mtk_pcie_setup_irq(port); +	if (err) +		goto err_setup; + +	return 0; + +err_setup: +	mtk_pcie_power_down(port); + +	return err; +} + +static int mtk_pcie_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct mtk_pcie_port *port; +	struct pci_host_bridge *host; +	int err; + +	host = devm_pci_alloc_host_bridge(dev, sizeof(*port)); +	if (!host) +		return -ENOMEM; + +	port = pci_host_bridge_priv(host); + +	port->dev = dev; +	platform_set_drvdata(pdev, port); + +	err = mtk_pcie_setup(port); +	if (err) +		return err; + +	host->ops = &mtk_pcie_ops; +	host->sysdata = port; + +	err = pci_host_probe(host); +	if (err) { +		mtk_pcie_irq_teardown(port); +		mtk_pcie_power_down(port); +		return err; +	} + +	return 0; +} + +static int mtk_pcie_remove(struct platform_device *pdev) +{ +	struct mtk_pcie_port *port = platform_get_drvdata(pdev); +	struct pci_host_bridge *host = pci_host_bridge_from_priv(port); + +	pci_lock_rescan_remove(); +	pci_stop_root_bus(host->bus); +	pci_remove_root_bus(host->bus); +	pci_unlock_rescan_remove(); + +	mtk_pcie_irq_teardown(port); +	mtk_pcie_power_down(port); + +	return 0; +} + +static void __maybe_unused mtk_pcie_irq_save(struct mtk_pcie_port *port) +{ +	int i; + +	raw_spin_lock(&port->irq_lock); + +	port->saved_irq_state = readl_relaxed(port->base + PCIE_INT_ENABLE_REG); + +	for (i = 0; i < PCIE_MSI_SET_NUM; i++) { +		struct mtk_msi_set *msi_set = &port->msi_sets[i]; + +		msi_set->saved_irq_state = readl_relaxed(msi_set->base + +					   PCIE_MSI_SET_ENABLE_OFFSET); +	} + +	raw_spin_unlock(&port->irq_lock); +} + +static void __maybe_unused mtk_pcie_irq_restore(struct mtk_pcie_port *port) +{ +	int i; + +	raw_spin_lock(&port->irq_lock); + +	writel_relaxed(port->saved_irq_state, port->base + PCIE_INT_ENABLE_REG); + +	for (i = 0; i < PCIE_MSI_SET_NUM; i++) { +		struct mtk_msi_set *msi_set = &port->msi_sets[i]; + +		writel_relaxed(msi_set->saved_irq_state, +			       msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET); +	} + +	raw_spin_unlock(&port->irq_lock); +} + +static int __maybe_unused mtk_pcie_turn_off_link(struct mtk_pcie_port *port) +{ +	u32 val; + +	val = readl_relaxed(port->base + PCIE_ICMD_PM_REG); +	val |= PCIE_TURN_OFF_LINK; +	writel_relaxed(val, port->base + PCIE_ICMD_PM_REG); + +	/* Check the link is L2 */ +	return readl_poll_timeout(port->base + PCIE_LTSSM_STATUS_REG, val, +				  (PCIE_LTSSM_STATE(val) == +				   PCIE_LTSSM_STATE_L2_IDLE), 20, +				   50 * USEC_PER_MSEC); +} + +static int __maybe_unused mtk_pcie_suspend_noirq(struct device *dev) +{ +	struct mtk_pcie_port *port = dev_get_drvdata(dev); +	int err; +	u32 val; + +	/* Trigger link to L2 state */ +	err = mtk_pcie_turn_off_link(port); +	if (err) { +		dev_err(port->dev, "cannot enter L2 state\n"); +		return err; +	} + +	/* Pull down the PERST# pin */ +	val = readl_relaxed(port->base + PCIE_RST_CTRL_REG); +	val |= PCIE_PE_RSTB; +	writel_relaxed(val, port->base + PCIE_RST_CTRL_REG); + +	dev_dbg(port->dev, "entered L2 states successfully"); + +	mtk_pcie_irq_save(port); +	mtk_pcie_power_down(port); + +	return 0; +} + +static int __maybe_unused mtk_pcie_resume_noirq(struct device *dev) +{ +	struct mtk_pcie_port *port = dev_get_drvdata(dev); +	int err; + +	err = mtk_pcie_power_up(port); +	if (err) +		return err; + +	err = mtk_pcie_startup_port(port); +	if (err) { +		mtk_pcie_power_down(port); +		return err; +	} + +	mtk_pcie_irq_restore(port); + +	return 0; +} + +static const struct dev_pm_ops mtk_pcie_pm_ops = { +	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_pcie_suspend_noirq, +				      mtk_pcie_resume_noirq) +}; + +static const struct of_device_id mtk_pcie_of_match[] = { +	{ .compatible = "mediatek,mt8192-pcie" }, +	{}, +}; + +static struct platform_driver mtk_pcie_driver = { +	.probe = mtk_pcie_probe, +	.remove = mtk_pcie_remove, +	.driver = { +		.name = "mtk-pcie", +		.of_match_table = mtk_pcie_of_match, +		.pm = &mtk_pcie_pm_ops, +	}, +}; + +module_platform_driver(mtk_pcie_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 23548b517e4b..62a042e75d9a 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -143,6 +143,7 @@ struct mtk_pcie_port;   * struct mtk_pcie_soc - differentiate between host generations   * @need_fix_class_id: whether this host's class ID needed to be fixed or not   * @need_fix_device_id: whether this host's device ID needed to be fixed or not + * @no_msi: Bridge has no MSI support, and relies on an external block   * @device_id: device ID which this host need to be fixed   * @ops: pointer to configuration access functions   * @startup: pointer to controller setting functions @@ -151,6 +152,7 @@ struct mtk_pcie_port;  struct mtk_pcie_soc {  	bool need_fix_class_id;  	bool need_fix_device_id; +	bool no_msi;  	unsigned int device_id;  	struct pci_ops *ops;  	int (*startup)(struct mtk_pcie_port *port); @@ -760,7 +762,7 @@ static struct pci_ops mtk_pcie_ops = {  static int mtk_pcie_startup_port(struct mtk_pcie_port *port)  {  	struct mtk_pcie *pcie = port->pcie; -	u32 func = PCI_FUNC(port->slot << 3); +	u32 func = PCI_FUNC(port->slot);  	u32 slot = PCI_SLOT(port->slot << 3);  	u32 val;  	int err; @@ -1087,6 +1089,7 @@ static int mtk_pcie_probe(struct platform_device *pdev)  	host->ops = pcie->soc->ops;  	host->sysdata = pcie; +	host->msi_domain = pcie->soc->no_msi;  	err = pci_host_probe(host);  	if (err) @@ -1176,6 +1179,7 @@ static const struct dev_pm_ops mtk_pcie_pm_ops = {  };  static const struct mtk_pcie_soc mtk_pcie_soc_v1 = { +	.no_msi = true,  	.ops = &mtk_pcie_ops,  	.startup = mtk_pcie_startup_port,  }; @@ -1210,6 +1214,7 @@ static const struct of_device_id mtk_pcie_ids[] = {  	{ .compatible = "mediatek,mt7629-pcie", .data = &mtk_pcie_soc_mt7629 },  	{},  }; +MODULE_DEVICE_TABLE(of, mtk_pcie_ids);  static struct platform_driver mtk_pcie_driver = {  	.probe = mtk_pcie_probe, diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c index 04c19ff81aff..89c68c56d93b 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c @@ -301,27 +301,27 @@ static const struct cause event_cause[NUM_EVENTS] = {  	LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"),  }; -struct event_map pcie_event_to_event[] = { +static struct event_map pcie_event_to_event[] = {  	PCIE_EVENT_TO_EVENT_MAP(L2_EXIT),  	PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT),  	PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT),  }; -struct event_map sec_error_to_event[] = { +static struct event_map sec_error_to_event[] = {  	SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR),  	SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR),  	SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR),  	SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR),  }; -struct event_map ded_error_to_event[] = { +static struct event_map ded_error_to_event[] = {  	DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR),  	DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR),  	DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR),  	DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR),  }; -struct event_map local_status_to_event[] = { +static struct event_map local_status_to_event[] = {  	LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0),  	LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1),  	LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0), @@ -1023,10 +1023,8 @@ static int mc_platform_init(struct pci_config_window *cfg)  	}  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { -		dev_err(dev, "unable to request IRQ%d\n", irq); +	if (irq < 0)  		return -ENODEV; -	}  	for (i = 0; i < NUM_EVENTS; i++) {  		event_irq = irq_create_mapping(port->event_domain, i); diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index a728e8f9ad3c..765cf2b45e24 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -35,18 +35,12 @@  struct rcar_msi {  	DECLARE_BITMAP(used, INT_PCI_MSI_NR);  	struct irq_domain *domain; -	struct msi_controller chip; -	unsigned long pages; -	struct mutex lock; +	struct mutex map_lock; +	spinlock_t mask_lock;  	int irq1;  	int irq2;  }; -static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip) -{ -	return container_of(chip, struct rcar_msi, chip); -} -  /* Structure representing the PCIe interface */  struct rcar_pcie_host {  	struct rcar_pcie	pcie; @@ -56,6 +50,11 @@ struct rcar_pcie_host {  	int			(*phy_init_fn)(struct rcar_pcie_host *host);  }; +static struct rcar_pcie_host *msi_to_host(struct rcar_msi *msi) +{ +	return container_of(msi, struct rcar_pcie_host, msi); +} +  static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)  {  	unsigned int shift = BITS_PER_BYTE * (where & 3); @@ -292,8 +291,6 @@ static int rcar_pcie_enable(struct rcar_pcie_host *host)  	bridge->sysdata = host;  	bridge->ops = &rcar_pcie_ops; -	if (IS_ENABLED(CONFIG_PCI_MSI)) -		bridge->msi = &host->msi.chip;  	return pci_host_probe(bridge);  } @@ -473,42 +470,6 @@ static int rcar_pcie_phy_init_gen3(struct rcar_pcie_host *host)  	return err;  } -static int rcar_msi_alloc(struct rcar_msi *chip) -{ -	int msi; - -	mutex_lock(&chip->lock); - -	msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR); -	if (msi < INT_PCI_MSI_NR) -		set_bit(msi, chip->used); -	else -		msi = -ENOSPC; - -	mutex_unlock(&chip->lock); - -	return msi; -} - -static int rcar_msi_alloc_region(struct rcar_msi *chip, int no_irqs) -{ -	int msi; - -	mutex_lock(&chip->lock); -	msi = bitmap_find_free_region(chip->used, INT_PCI_MSI_NR, -				      order_base_2(no_irqs)); -	mutex_unlock(&chip->lock); - -	return msi; -} - -static void rcar_msi_free(struct rcar_msi *chip, unsigned long irq) -{ -	mutex_lock(&chip->lock); -	clear_bit(irq, chip->used); -	mutex_unlock(&chip->lock); -} -  static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)  {  	struct rcar_pcie_host *host = data; @@ -527,18 +488,13 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)  		unsigned int index = find_first_bit(®, 32);  		unsigned int msi_irq; -		/* clear the interrupt */ -		rcar_pci_write_reg(pcie, 1 << index, PCIEMSIFR); - -		msi_irq = irq_find_mapping(msi->domain, index); +		msi_irq = irq_find_mapping(msi->domain->parent, index);  		if (msi_irq) { -			if (test_bit(index, msi->used)) -				generic_handle_irq(msi_irq); -			else -				dev_info(dev, "unhandled MSI\n"); +			generic_handle_irq(msi_irq);  		} else {  			/* Unknown MSI, just clear it */  			dev_dbg(dev, "unexpected MSI\n"); +			rcar_pci_write_reg(pcie, BIT(index), PCIEMSIFR);  		}  		/* see if there's any more pending in this vector */ @@ -548,149 +504,169 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)  	return IRQ_HANDLED;  } -static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, -			      struct msi_desc *desc) +static void rcar_msi_top_irq_ack(struct irq_data *d)  { -	struct rcar_msi *msi = to_rcar_msi(chip); -	struct rcar_pcie_host *host = container_of(chip, struct rcar_pcie_host, -						   msi.chip); -	struct rcar_pcie *pcie = &host->pcie; -	struct msi_msg msg; -	unsigned int irq; -	int hwirq; +	irq_chip_ack_parent(d); +} -	hwirq = rcar_msi_alloc(msi); -	if (hwirq < 0) -		return hwirq; +static void rcar_msi_top_irq_mask(struct irq_data *d) +{ +	pci_msi_mask_irq(d); +	irq_chip_mask_parent(d); +} -	irq = irq_find_mapping(msi->domain, hwirq); -	if (!irq) { -		rcar_msi_free(msi, hwirq); -		return -EINVAL; -	} +static void rcar_msi_top_irq_unmask(struct irq_data *d) +{ +	pci_msi_unmask_irq(d); +	irq_chip_unmask_parent(d); +} -	irq_set_msi_desc(irq, desc); +static struct irq_chip rcar_msi_top_chip = { +	.name		= "PCIe MSI", +	.irq_ack	= rcar_msi_top_irq_ack, +	.irq_mask	= rcar_msi_top_irq_mask, +	.irq_unmask	= rcar_msi_top_irq_unmask, +}; -	msg.address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE; -	msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR); -	msg.data = hwirq; +static void rcar_msi_irq_ack(struct irq_data *d) +{ +	struct rcar_msi *msi = irq_data_get_irq_chip_data(d); +	struct rcar_pcie *pcie = &msi_to_host(msi)->pcie; -	pci_write_msi_msg(irq, &msg); +	/* clear the interrupt */ +	rcar_pci_write_reg(pcie, BIT(d->hwirq), PCIEMSIFR); +} -	return 0; +static void rcar_msi_irq_mask(struct irq_data *d) +{ +	struct rcar_msi *msi = irq_data_get_irq_chip_data(d); +	struct rcar_pcie *pcie = &msi_to_host(msi)->pcie; +	unsigned long flags; +	u32 value; + +	spin_lock_irqsave(&msi->mask_lock, flags); +	value = rcar_pci_read_reg(pcie, PCIEMSIIER); +	value &= ~BIT(d->hwirq); +	rcar_pci_write_reg(pcie, value, PCIEMSIIER); +	spin_unlock_irqrestore(&msi->mask_lock, flags);  } -static int rcar_msi_setup_irqs(struct msi_controller *chip, -			       struct pci_dev *pdev, int nvec, int type) +static void rcar_msi_irq_unmask(struct irq_data *d)  { -	struct rcar_msi *msi = to_rcar_msi(chip); -	struct rcar_pcie_host *host = container_of(chip, struct rcar_pcie_host, -						   msi.chip); -	struct rcar_pcie *pcie = &host->pcie; -	struct msi_desc *desc; -	struct msi_msg msg; -	unsigned int irq; -	int hwirq; -	int i; +	struct rcar_msi *msi = irq_data_get_irq_chip_data(d); +	struct rcar_pcie *pcie = &msi_to_host(msi)->pcie; +	unsigned long flags; +	u32 value; + +	spin_lock_irqsave(&msi->mask_lock, flags); +	value = rcar_pci_read_reg(pcie, PCIEMSIIER); +	value |= BIT(d->hwirq); +	rcar_pci_write_reg(pcie, value, PCIEMSIIER); +	spin_unlock_irqrestore(&msi->mask_lock, flags); +} -	/* MSI-X interrupts are not supported */ -	if (type == PCI_CAP_ID_MSIX) -		return -EINVAL; +static int rcar_msi_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force) +{ +	return -EINVAL; +} -	WARN_ON(!list_is_singular(&pdev->dev.msi_list)); -	desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list); +static void rcar_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ +	struct rcar_msi *msi = irq_data_get_irq_chip_data(data); +	struct rcar_pcie *pcie = &msi_to_host(msi)->pcie; -	hwirq = rcar_msi_alloc_region(msi, nvec); -	if (hwirq < 0) -		return -ENOSPC; +	msg->address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE; +	msg->address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR); +	msg->data = data->hwirq; +} -	irq = irq_find_mapping(msi->domain, hwirq); -	if (!irq) -		return -ENOSPC; +static struct irq_chip rcar_msi_bottom_chip = { +	.name			= "Rcar MSI", +	.irq_ack		= rcar_msi_irq_ack, +	.irq_mask		= rcar_msi_irq_mask, +	.irq_unmask		= rcar_msi_irq_unmask, +	.irq_set_affinity 	= rcar_msi_set_affinity, +	.irq_compose_msi_msg	= rcar_compose_msi_msg, +}; -	for (i = 0; i < nvec; i++) { -		/* -		 * irq_create_mapping() called from rcar_pcie_probe() pre- -		 * allocates descs,  so there is no need to allocate descs here. -		 * We can therefore assume that if irq_find_mapping() above -		 * returns non-zero, then the descs are also successfully -		 * allocated. -		 */ -		if (irq_set_msi_desc_off(irq, i, desc)) { -			/* TODO: clear */ -			return -EINVAL; -		} -	} +static int rcar_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, +				  unsigned int nr_irqs, void *args) +{ +	struct rcar_msi *msi = domain->host_data; +	unsigned int i; +	int hwirq; -	desc->nvec_used = nvec; -	desc->msi_attrib.multiple = order_base_2(nvec); +	mutex_lock(&msi->map_lock); -	msg.address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE; -	msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR); -	msg.data = hwirq; +	hwirq = bitmap_find_free_region(msi->used, INT_PCI_MSI_NR, order_base_2(nr_irqs)); -	pci_write_msi_msg(irq, &msg); +	mutex_unlock(&msi->map_lock); + +	if (hwirq < 0) +		return -ENOSPC; + +	for (i = 0; i < nr_irqs; i++) +		irq_domain_set_info(domain, virq + i, hwirq + i, +				    &rcar_msi_bottom_chip, domain->host_data, +				    handle_edge_irq, NULL, NULL);  	return 0;  } -static void rcar_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) +static void rcar_msi_domain_free(struct irq_domain *domain, unsigned int virq, +				  unsigned int nr_irqs)  { -	struct rcar_msi *msi = to_rcar_msi(chip); -	struct irq_data *d = irq_get_irq_data(irq); - -	rcar_msi_free(msi, d->hwirq); -} +	struct irq_data *d = irq_domain_get_irq_data(domain, virq); +	struct rcar_msi *msi = domain->host_data; -static struct irq_chip rcar_msi_irq_chip = { -	.name = "R-Car PCIe MSI", -	.irq_enable = pci_msi_unmask_irq, -	.irq_disable = pci_msi_mask_irq, -	.irq_mask = pci_msi_mask_irq, -	.irq_unmask = pci_msi_unmask_irq, -}; +	mutex_lock(&msi->map_lock); -static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, -			irq_hw_number_t hwirq) -{ -	irq_set_chip_and_handler(irq, &rcar_msi_irq_chip, handle_simple_irq); -	irq_set_chip_data(irq, domain->host_data); +	bitmap_release_region(msi->used, d->hwirq, order_base_2(nr_irqs)); -	return 0; +	mutex_unlock(&msi->map_lock);  } -static const struct irq_domain_ops msi_domain_ops = { -	.map = rcar_msi_map, +static const struct irq_domain_ops rcar_msi_domain_ops = { +	.alloc	= rcar_msi_domain_alloc, +	.free	= rcar_msi_domain_free, +}; + +static struct msi_domain_info rcar_msi_info = { +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | +		   MSI_FLAG_MULTI_PCI_MSI), +	.chip	= &rcar_msi_top_chip,  }; -static void rcar_pcie_unmap_msi(struct rcar_pcie_host *host) +static int rcar_allocate_domains(struct rcar_msi *msi)  { -	struct rcar_msi *msi = &host->msi; -	int i, irq; +	struct rcar_pcie *pcie = &msi_to_host(msi)->pcie; +	struct fwnode_handle *fwnode = dev_fwnode(pcie->dev); +	struct irq_domain *parent; + +	parent = irq_domain_create_linear(fwnode, INT_PCI_MSI_NR, +					  &rcar_msi_domain_ops, msi); +	if (!parent) { +		dev_err(pcie->dev, "failed to create IRQ domain\n"); +		return -ENOMEM; +	} +	irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS); -	for (i = 0; i < INT_PCI_MSI_NR; i++) { -		irq = irq_find_mapping(msi->domain, i); -		if (irq > 0) -			irq_dispose_mapping(irq); +	msi->domain = pci_msi_create_irq_domain(fwnode, &rcar_msi_info, parent); +	if (!msi->domain) { +		dev_err(pcie->dev, "failed to create MSI domain\n"); +		irq_domain_remove(parent); +		return -ENOMEM;  	} -	irq_domain_remove(msi->domain); +	return 0;  } -static void rcar_pcie_hw_enable_msi(struct rcar_pcie_host *host) +static void rcar_free_domains(struct rcar_msi *msi)  { -	struct rcar_pcie *pcie = &host->pcie; -	struct rcar_msi *msi = &host->msi; -	unsigned long base; - -	/* setup MSI data target */ -	base = virt_to_phys((void *)msi->pages); +	struct irq_domain *parent = msi->domain->parent; -	rcar_pci_write_reg(pcie, lower_32_bits(base) | MSIFE, PCIEMSIALR); -	rcar_pci_write_reg(pcie, upper_32_bits(base), PCIEMSIAUR); - -	/* enable all MSI interrupts */ -	rcar_pci_write_reg(pcie, 0xffffffff, PCIEMSIIER); +	irq_domain_remove(msi->domain); +	irq_domain_remove(parent);  }  static int rcar_pcie_enable_msi(struct rcar_pcie_host *host) @@ -698,29 +674,24 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host)  	struct rcar_pcie *pcie = &host->pcie;  	struct device *dev = pcie->dev;  	struct rcar_msi *msi = &host->msi; -	int err, i; - -	mutex_init(&msi->lock); +	struct resource res; +	int err; -	msi->chip.dev = dev; -	msi->chip.setup_irq = rcar_msi_setup_irq; -	msi->chip.setup_irqs = rcar_msi_setup_irqs; -	msi->chip.teardown_irq = rcar_msi_teardown_irq; +	mutex_init(&msi->map_lock); +	spin_lock_init(&msi->mask_lock); -	msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR, -					    &msi_domain_ops, &msi->chip); -	if (!msi->domain) { -		dev_err(dev, "failed to create IRQ domain\n"); -		return -ENOMEM; -	} +	err = of_address_to_resource(dev->of_node, 0, &res); +	if (err) +		return err; -	for (i = 0; i < INT_PCI_MSI_NR; i++) -		irq_create_mapping(msi->domain, i); +	err = rcar_allocate_domains(msi); +	if (err) +		return err;  	/* Two irqs are for MSI, but they are also used for non-MSI irqs */  	err = devm_request_irq(dev, msi->irq1, rcar_pcie_msi_irq,  			       IRQF_SHARED | IRQF_NO_THREAD, -			       rcar_msi_irq_chip.name, host); +			       rcar_msi_bottom_chip.name, host);  	if (err < 0) {  		dev_err(dev, "failed to request IRQ: %d\n", err);  		goto err; @@ -728,27 +699,32 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host)  	err = devm_request_irq(dev, msi->irq2, rcar_pcie_msi_irq,  			       IRQF_SHARED | IRQF_NO_THREAD, -			       rcar_msi_irq_chip.name, host); +			       rcar_msi_bottom_chip.name, host);  	if (err < 0) {  		dev_err(dev, "failed to request IRQ: %d\n", err);  		goto err;  	} -	/* setup MSI data target */ -	msi->pages = __get_free_pages(GFP_KERNEL | GFP_DMA32, 0); -	rcar_pcie_hw_enable_msi(host); +	/* disable all MSIs */ +	rcar_pci_write_reg(pcie, 0, PCIEMSIIER); + +	/* +	 * Setup MSI data target using RC base address address, which +	 * is guaranteed to be in the low 32bit range on any RCar HW. +	 */ +	rcar_pci_write_reg(pcie, lower_32_bits(res.start) | MSIFE, PCIEMSIALR); +	rcar_pci_write_reg(pcie, upper_32_bits(res.start), PCIEMSIAUR);  	return 0;  err: -	rcar_pcie_unmap_msi(host); +	rcar_free_domains(msi);  	return err;  }  static void rcar_pcie_teardown_msi(struct rcar_pcie_host *host)  {  	struct rcar_pcie *pcie = &host->pcie; -	struct rcar_msi *msi = &host->msi;  	/* Disable all MSI interrupts */  	rcar_pci_write_reg(pcie, 0, PCIEMSIIER); @@ -756,9 +732,7 @@ static void rcar_pcie_teardown_msi(struct rcar_pcie_host *host)  	/* Disable address decoding of the MSI interrupt, MSIFE */  	rcar_pci_write_reg(pcie, 0, PCIEMSIALR); -	free_pages(msi->pages, 0); - -	rcar_pcie_unmap_msi(host); +	rcar_free_domains(&host->msi);  }  static int rcar_pcie_get_resources(struct rcar_pcie_host *host) @@ -1011,8 +985,17 @@ static int __maybe_unused rcar_pcie_resume(struct device *dev)  	dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);  	/* Enable MSI */ -	if (IS_ENABLED(CONFIG_PCI_MSI)) -		rcar_pcie_hw_enable_msi(host); +	if (IS_ENABLED(CONFIG_PCI_MSI)) { +		struct resource res; +		u32 val; + +		of_address_to_resource(dev->of_node, 0, &res); +		rcar_pci_write_reg(pcie, upper_32_bits(res.start), PCIEMSIAUR); +		rcar_pci_write_reg(pcie, lower_32_bits(res.start) | MSIFE, PCIEMSIALR); + +		bitmap_to_arr32(&val, host->msi.used, INT_PCI_MSI_NR); +		rcar_pci_write_reg(pcie, val, PCIEMSIIER); +	}  	rcar_pcie_hw_enable(host); diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 07e36661bbc2..8689311c5ef6 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -26,6 +26,7 @@  /* Bridge core config registers */  #define BRCFG_PCIE_RX0			0x00000000 +#define BRCFG_PCIE_RX1			0x00000004  #define BRCFG_INTERRUPT			0x00000010  #define BRCFG_PCIE_RX_MSG_FILTER	0x00000020 @@ -128,6 +129,7 @@  #define NWL_ECAM_VALUE_DEFAULT		12  #define CFG_DMA_REG_BAR			GENMASK(2, 0) +#define CFG_PCIE_CACHE			GENMASK(7, 0)  #define INT_PCI_MSI_NR			(2 * 32) @@ -675,6 +677,11 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)  	nwl_bridge_writel(pcie, CFG_ENABLE_MSG_FILTER_MASK,  			  BRCFG_PCIE_RX_MSG_FILTER); +	/* This routes the PCIe DMA traffic to go through CCI path */ +	if (of_dma_is_coherent(dev->of_node)) +		nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_PCIE_RX1) | +				  CFG_PCIE_CACHE, BRCFG_PCIE_RX1); +  	err = nwl_wait_for_link(pcie);  	if (err)  		return err; diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index fa5baeb82653..14001febf59a 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -93,25 +93,23 @@  /**   * struct xilinx_pcie_port - PCIe port information   * @reg_base: IO Mapped Register Base - * @irq: Interrupt number - * @msi_pages: MSI pages   * @dev: Device pointer + * @msi_map: Bitmap of allocated MSIs + * @map_lock: Mutex protecting the MSI allocation   * @msi_domain: MSI IRQ domain pointer   * @leg_domain: Legacy IRQ domain pointer   * @resources: Bus Resources   */  struct xilinx_pcie_port {  	void __iomem *reg_base; -	u32 irq; -	unsigned long msi_pages;  	struct device *dev; +	unsigned long msi_map[BITS_TO_LONGS(XILINX_NUM_MSI_IRQS)]; +	struct mutex map_lock;  	struct irq_domain *msi_domain;  	struct irq_domain *leg_domain;  	struct list_head resources;  }; -static DECLARE_BITMAP(msi_irq_in_use, XILINX_NUM_MSI_IRQS); -  static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg)  {  	return readl(port->reg_base + reg); @@ -196,151 +194,118 @@ static struct pci_ops xilinx_pcie_ops = {  /* MSI functions */ -/** - * xilinx_pcie_destroy_msi - Free MSI number - * @irq: IRQ to be freed - */ -static void xilinx_pcie_destroy_msi(unsigned int irq) +static void xilinx_msi_top_irq_ack(struct irq_data *d)  { -	struct msi_desc *msi; -	struct xilinx_pcie_port *port; -	struct irq_data *d = irq_get_irq_data(irq); -	irq_hw_number_t hwirq = irqd_to_hwirq(d); - -	if (!test_bit(hwirq, msi_irq_in_use)) { -		msi = irq_get_msi_desc(irq); -		port = msi_desc_to_pci_sysdata(msi); -		dev_err(port->dev, "Trying to free unused MSI#%d\n", irq); -	} else { -		clear_bit(hwirq, msi_irq_in_use); -	} +	/* +	 * xilinx_pcie_intr_handler() will have performed the Ack. +	 * Eventually, this should be fixed and the Ack be moved in +	 * the respective callbacks for INTx and MSI. +	 */  } -/** - * xilinx_pcie_assign_msi - Allocate MSI number - * - * Return: A valid IRQ on success and error value on failure. - */ -static int xilinx_pcie_assign_msi(void) -{ -	int pos; - -	pos = find_first_zero_bit(msi_irq_in_use, XILINX_NUM_MSI_IRQS); -	if (pos < XILINX_NUM_MSI_IRQS) -		set_bit(pos, msi_irq_in_use); -	else -		return -ENOSPC; +static struct irq_chip xilinx_msi_top_chip = { +	.name		= "PCIe MSI", +	.irq_ack	= xilinx_msi_top_irq_ack, +}; -	return pos; +static int xilinx_msi_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force) +{ +	return -EINVAL;  } -/** - * xilinx_msi_teardown_irq - Destroy the MSI - * @chip: MSI Chip descriptor - * @irq: MSI IRQ to destroy - */ -static void xilinx_msi_teardown_irq(struct msi_controller *chip, -				    unsigned int irq) +static void xilinx_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)  { -	xilinx_pcie_destroy_msi(irq); -	irq_dispose_mapping(irq); +	struct xilinx_pcie_port *pcie = irq_data_get_irq_chip_data(data); +	phys_addr_t pa = ALIGN_DOWN(virt_to_phys(pcie), SZ_4K); + +	msg->address_lo = lower_32_bits(pa); +	msg->address_hi = upper_32_bits(pa); +	msg->data = data->hwirq;  } -/** - * xilinx_pcie_msi_setup_irq - Setup MSI request - * @chip: MSI chip pointer - * @pdev: PCIe device pointer - * @desc: MSI descriptor pointer - * - * Return: '0' on success and error value on failure - */ -static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip, -				     struct pci_dev *pdev, -				     struct msi_desc *desc) -{ -	struct xilinx_pcie_port *port = pdev->bus->sysdata; -	unsigned int irq; -	int hwirq; -	struct msi_msg msg; -	phys_addr_t msg_addr; +static struct irq_chip xilinx_msi_bottom_chip = { +	.name			= "Xilinx MSI", +	.irq_set_affinity 	= xilinx_msi_set_affinity, +	.irq_compose_msi_msg	= xilinx_compose_msi_msg, +}; -	hwirq = xilinx_pcie_assign_msi(); -	if (hwirq < 0) -		return hwirq; +static int xilinx_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, +				  unsigned int nr_irqs, void *args) +{ +	struct xilinx_pcie_port *port = domain->host_data; +	int hwirq, i; -	irq = irq_create_mapping(port->msi_domain, hwirq); -	if (!irq) -		return -EINVAL; +	mutex_lock(&port->map_lock); -	irq_set_msi_desc(irq, desc); +	hwirq = bitmap_find_free_region(port->msi_map, XILINX_NUM_MSI_IRQS, order_base_2(nr_irqs)); -	msg_addr = virt_to_phys((void *)port->msi_pages); +	mutex_unlock(&port->map_lock); -	msg.address_hi = 0; -	msg.address_lo = msg_addr; -	msg.data = irq; +	if (hwirq < 0) +		return -ENOSPC; -	pci_write_msi_msg(irq, &msg); +	for (i = 0; i < nr_irqs; i++) +		irq_domain_set_info(domain, virq + i, hwirq + i, +				    &xilinx_msi_bottom_chip, domain->host_data, +				    handle_edge_irq, NULL, NULL);  	return 0;  } -/* MSI Chip Descriptor */ -static struct msi_controller xilinx_pcie_msi_chip = { -	.setup_irq = xilinx_pcie_msi_setup_irq, -	.teardown_irq = xilinx_msi_teardown_irq, -}; +static void xilinx_msi_domain_free(struct irq_domain *domain, unsigned int virq, +				  unsigned int nr_irqs) +{ +	struct irq_data *d = irq_domain_get_irq_data(domain, virq); +	struct xilinx_pcie_port *port = domain->host_data; -/* HW Interrupt Chip Descriptor */ -static struct irq_chip xilinx_msi_irq_chip = { -	.name = "Xilinx PCIe MSI", -	.irq_enable = pci_msi_unmask_irq, -	.irq_disable = pci_msi_mask_irq, -	.irq_mask = pci_msi_mask_irq, -	.irq_unmask = pci_msi_unmask_irq, -}; +	mutex_lock(&port->map_lock); -/** - * xilinx_pcie_msi_map - Set the handler for the MSI and mark IRQ as valid - * @domain: IRQ domain - * @irq: Virtual IRQ number - * @hwirq: HW interrupt number - * - * Return: Always returns 0. - */ -static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq, -			       irq_hw_number_t hwirq) -{ -	irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq); -	irq_set_chip_data(irq, domain->host_data); +	bitmap_release_region(port->msi_map, d->hwirq, order_base_2(nr_irqs)); -	return 0; +	mutex_unlock(&port->map_lock);  } -/* IRQ Domain operations */ -static const struct irq_domain_ops msi_domain_ops = { -	.map = xilinx_pcie_msi_map, +static const struct irq_domain_ops xilinx_msi_domain_ops = { +	.alloc	= xilinx_msi_domain_alloc, +	.free	= xilinx_msi_domain_free,  }; -/** - * xilinx_pcie_enable_msi - Enable MSI support - * @port: PCIe port information - */ -static int xilinx_pcie_enable_msi(struct xilinx_pcie_port *port) +static struct msi_domain_info xilinx_msi_info = { +	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), +	.chip	= &xilinx_msi_top_chip, +}; + +static int xilinx_allocate_msi_domains(struct xilinx_pcie_port *pcie)  { -	phys_addr_t msg_addr; +	struct fwnode_handle *fwnode = dev_fwnode(pcie->dev); +	struct irq_domain *parent; -	port->msi_pages = __get_free_pages(GFP_KERNEL, 0); -	if (!port->msi_pages) +	parent = irq_domain_create_linear(fwnode, XILINX_NUM_MSI_IRQS, +					  &xilinx_msi_domain_ops, pcie); +	if (!parent) { +		dev_err(pcie->dev, "failed to create IRQ domain\n");  		return -ENOMEM; +	} +	irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS); -	msg_addr = virt_to_phys((void *)port->msi_pages); -	pcie_write(port, 0x0, XILINX_PCIE_REG_MSIBASE1); -	pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2); +	pcie->msi_domain = pci_msi_create_irq_domain(fwnode, &xilinx_msi_info, parent); +	if (!pcie->msi_domain) { +		dev_err(pcie->dev, "failed to create MSI domain\n"); +		irq_domain_remove(parent); +		return -ENOMEM; +	}  	return 0;  } +static void xilinx_free_msi_domains(struct xilinx_pcie_port *pcie) +{ +	struct irq_domain *parent = pcie->msi_domain->parent; + +	irq_domain_remove(pcie->msi_domain); +	irq_domain_remove(parent); +} +  /* INTx Functions */  /** @@ -420,6 +385,8 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)  	}  	if (status & (XILINX_PCIE_INTR_INTX | XILINX_PCIE_INTR_MSI)) { +		unsigned int irq; +  		val = pcie_read(port, XILINX_PCIE_REG_RPIFR1);  		/* Check whether interrupt valid */ @@ -432,20 +399,19 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)  		if (val & XILINX_PCIE_RPIFR1_MSI_INTR) {  			val = pcie_read(port, XILINX_PCIE_REG_RPIFR2) &  				XILINX_PCIE_RPIFR2_MSG_DATA; +			irq = irq_find_mapping(port->msi_domain->parent, val);  		} else {  			val = (val & XILINX_PCIE_RPIFR1_INTR_MASK) >>  				XILINX_PCIE_RPIFR1_INTR_SHIFT; -			val = irq_find_mapping(port->leg_domain, val); +			irq = irq_find_mapping(port->leg_domain, val);  		}  		/* Clear interrupt FIFO register 1 */  		pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK,  			   XILINX_PCIE_REG_RPIFR1); -		/* Handle the interrupt */ -		if (IS_ENABLED(CONFIG_PCI_MSI) || -		    !(val & XILINX_PCIE_RPIFR1_MSI_INTR)) -			generic_handle_irq(val); +		if (irq) +			generic_handle_irq(irq);  	}  	if (status & XILINX_PCIE_INTR_SLV_UNSUPP) @@ -491,12 +457,11 @@ error:  static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)  {  	struct device *dev = port->dev; -	struct device_node *node = dev->of_node;  	struct device_node *pcie_intc_node;  	int ret;  	/* Setup INTx */ -	pcie_intc_node = of_get_next_child(node, NULL); +	pcie_intc_node = of_get_next_child(dev->of_node, NULL);  	if (!pcie_intc_node) {  		dev_err(dev, "No PCIe Intc node found\n");  		return -ENODEV; @@ -513,18 +478,14 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)  	/* Setup MSI */  	if (IS_ENABLED(CONFIG_PCI_MSI)) { -		port->msi_domain = irq_domain_add_linear(node, -							 XILINX_NUM_MSI_IRQS, -							 &msi_domain_ops, -							 &xilinx_pcie_msi_chip); -		if (!port->msi_domain) { -			dev_err(dev, "Failed to get a MSI IRQ domain\n"); -			return -ENODEV; -		} +		phys_addr_t pa = ALIGN_DOWN(virt_to_phys(port), SZ_4K); -		ret = xilinx_pcie_enable_msi(port); +		ret = xilinx_allocate_msi_domains(port);  		if (ret)  			return ret; + +		pcie_write(port, upper_32_bits(pa), XILINX_PCIE_REG_MSIBASE1); +		pcie_write(port, lower_32_bits(pa), XILINX_PCIE_REG_MSIBASE2);  	}  	return 0; @@ -572,6 +533,7 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)  	struct device *dev = port->dev;  	struct device_node *node = dev->of_node;  	struct resource regs; +	unsigned int irq;  	int err;  	err = of_address_to_resource(node, 0, ®s); @@ -584,12 +546,12 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)  	if (IS_ERR(port->reg_base))  		return PTR_ERR(port->reg_base); -	port->irq = irq_of_parse_and_map(node, 0); -	err = devm_request_irq(dev, port->irq, xilinx_pcie_intr_handler, +	irq = irq_of_parse_and_map(node, 0); +	err = devm_request_irq(dev, irq, xilinx_pcie_intr_handler,  			       IRQF_SHARED | IRQF_NO_THREAD,  			       "xilinx-pcie", port);  	if (err) { -		dev_err(dev, "unable to request irq %d\n", port->irq); +		dev_err(dev, "unable to request irq %d\n", irq);  		return err;  	} @@ -617,7 +579,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev)  		return -ENODEV;  	port = pci_host_bridge_priv(bridge); - +	mutex_init(&port->map_lock);  	port->dev = dev;  	err = xilinx_pcie_parse_dt(port); @@ -637,11 +599,11 @@ static int xilinx_pcie_probe(struct platform_device *pdev)  	bridge->sysdata = port;  	bridge->ops = &xilinx_pcie_ops; -#ifdef CONFIG_PCI_MSI -	xilinx_pcie_msi_chip.dev = dev; -	bridge->msi = &xilinx_pcie_msi_chip; -#endif -	return pci_host_probe(bridge); +	err = pci_host_probe(bridge); +	if (err) +		xilinx_free_msi_domains(port); + +	return err;  }  static const struct of_device_id xilinx_pcie_of_match[] = { diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 5e80f28f0119..e3fcdfec58b3 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -28,6 +28,7 @@  #define BUS_RESTRICT_CAP(vmcap)	(vmcap & 0x1)  #define PCI_REG_VMCONFIG	0x44  #define BUS_RESTRICT_CFG(vmcfg)	((vmcfg >> 8) & 0x3) +#define VMCONFIG_MSI_REMAP	0x2  #define PCI_REG_VMLOCK		0x70  #define MB2_SHADOW_EN(vmlock)	(vmlock & 0x2) @@ -59,6 +60,13 @@ enum vmd_features {  	 * be used for MSI remapping  	 */  	VMD_FEAT_OFFSET_FIRST_VECTOR		= (1 << 3), + +	/* +	 * Device can bypass remapping MSI-X transactions into its MSI-X table, +	 * avoiding the requirement of a VMD MSI domain for child device +	 * interrupt handling. +	 */ +	VMD_FEAT_CAN_BYPASS_MSI_REMAP		= (1 << 4),  };  /* @@ -306,6 +314,16 @@ static struct msi_domain_info vmd_msi_domain_info = {  	.chip		= &vmd_msi_controller,  }; +static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool enable) +{ +	u16 reg; + +	pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, ®); +	reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) : +		       (reg | VMCONFIG_MSI_REMAP); +	pci_write_config_word(vmd->dev, PCI_REG_VMCONFIG, reg); +} +  static int vmd_create_irq_domain(struct vmd_dev *vmd)  {  	struct fwnode_handle *fn; @@ -325,6 +343,13 @@ static int vmd_create_irq_domain(struct vmd_dev *vmd)  static void vmd_remove_irq_domain(struct vmd_dev *vmd)  { +	/* +	 * Some production BIOS won't enable remapping between soft reboots. +	 * Ensure remapping is restored before unloading the driver. +	 */ +	if (!vmd->msix_count) +		vmd_set_msi_remapping(vmd, true); +  	if (vmd->irq_domain) {  		struct fwnode_handle *fn = vmd->irq_domain->fwnode; @@ -679,15 +704,32 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)  	sd->node = pcibus_to_node(vmd->dev->bus); -	ret = vmd_create_irq_domain(vmd); -	if (ret) -		return ret; -  	/* -	 * Override the irq domain bus token so the domain can be distinguished -	 * from a regular PCI/MSI domain. +	 * Currently MSI remapping must be enabled in guest passthrough mode +	 * due to some missing interrupt remapping plumbing. This is probably +	 * acceptable because the guest is usually CPU-limited and MSI +	 * remapping doesn't become a performance bottleneck.  	 */ -	irq_domain_update_bus_token(vmd->irq_domain, DOMAIN_BUS_VMD_MSI); +	if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || +	    offset[0] || offset[1]) { +		ret = vmd_alloc_irqs(vmd); +		if (ret) +			return ret; + +		vmd_set_msi_remapping(vmd, true); + +		ret = vmd_create_irq_domain(vmd); +		if (ret) +			return ret; + +		/* +		 * Override the IRQ domain bus token so the domain can be +		 * distinguished from a regular PCI/MSI domain. +		 */ +		irq_domain_update_bus_token(vmd->irq_domain, DOMAIN_BUS_VMD_MSI); +	} else { +		vmd_set_msi_remapping(vmd, false); +	}  	pci_add_resource(&resources, &vmd->resources[0]);  	pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]); @@ -753,10 +795,6 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)  	if (features & VMD_FEAT_OFFSET_FIRST_VECTOR)  		vmd->first_vec = 1; -	err = vmd_alloc_irqs(vmd); -	if (err) -		return err; -  	spin_lock_init(&vmd->cfg_lock);  	pci_set_drvdata(dev, vmd);  	err = vmd_enable_domain(vmd, features); @@ -825,7 +863,8 @@ static const struct pci_device_id vmd_ids[] = {  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP,},  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0),  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | -				VMD_FEAT_HAS_BUS_RESTRICTIONS,}, +				VMD_FEAT_HAS_BUS_RESTRICTIONS | +				VMD_FEAT_CAN_BYPASS_MSI_REMAP,},  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f),  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |  				VMD_FEAT_HAS_BUS_RESTRICTIONS | diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 338148cf56f5..bce274d02dcf 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: GPL-2.0 -/** +/*   * Endpoint Function Driver to implement Non-Transparent Bridge functionality   *   * Copyright (C) 2020 Texas Instruments @@ -696,7 +696,8 @@ reset_handler:  /**   * epf_ntb_peer_spad_bar_clear() - Clear Peer Scratchpad BAR - * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound + *	     address.   *   *+-----------------+------->+------------------+        +-----------------+   *|       BAR0      |        |  CONFIG REGION   |        |       BAR0      | @@ -740,6 +741,7 @@ static void epf_ntb_peer_spad_bar_clear(struct epf_ntb_epc *ntb_epc)  /**   * epf_ntb_peer_spad_bar_set() - Set peer scratchpad BAR   * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @type: PRIMARY interface or SECONDARY interface   *   *+-----------------+------->+------------------+        +-----------------+   *|       BAR0      |        |  CONFIG REGION   |        |       BAR0      | @@ -808,7 +810,8 @@ static int epf_ntb_peer_spad_bar_set(struct epf_ntb *ntb,  /**   * epf_ntb_config_sspad_bar_clear() - Clear Config + Self scratchpad BAR - * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound + *	     address.   *   * +-----------------+------->+------------------+        +-----------------+   * |       BAR0      |        |  CONFIG REGION   |        |       BAR0      | @@ -851,7 +854,8 @@ static void epf_ntb_config_sspad_bar_clear(struct epf_ntb_epc *ntb_epc)  /**   * epf_ntb_config_sspad_bar_set() - Set Config + Self scratchpad BAR - * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound + *	     address.   *   * +-----------------+------->+------------------+        +-----------------+   * |       BAR0      |        |  CONFIG REGION   |        |       BAR0      | @@ -1312,6 +1316,7 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb,  /**   * epf_ntb_alloc_peer_mem() - Allocate memory in peer's outbound address space + * @dev: The PCI device.   * @ntb_epc: EPC associated with one of the HOST whose BAR holds peer's outbound   *   address   * @bar: BAR of @ntb_epc in for which memory has to be allocated (could be @@ -1660,7 +1665,6 @@ static int epf_ntb_init_epc_bar_interface(struct epf_ntb *ntb,   * epf_ntb_init_epc_bar() - Identify BARs to be used for each of the NTB   * constructs (scratchpad region, doorbell, memorywindow)   * @ntb: NTB device that facilitates communication between HOST1 and HOST2 - * @type: PRIMARY interface or SECONDARY interface   *   * Wrapper to epf_ntb_init_epc_bar_interface() to identify the free BARs   * to be used for each of BAR_CONFIG, BAR_PEER_SPAD, BAR_DB_MW1, BAR_MW2, @@ -2037,6 +2041,8 @@ static const struct config_item_type ntb_group_type = {  /**   * epf_ntb_add_cfs() - Add configfs directory specific to NTB   * @epf: NTB endpoint function device + * @group: A pointer to the config_group structure referencing a group of + *	   config_items of a specific type that belong to a specific sub-system.   *   * Add configfs directory specific to NTB. This directory will hold   * NTB specific properties like db_count, spad_count, num_mws etc., diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index c0ac4e9cbe72..d2708ca4bece 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: GPL-2.0 -/** +/*   * Test driver to test endpoint functionality   *   * Copyright (C) 2017 Texas Instruments @@ -833,15 +833,18 @@ static int pci_epf_test_bind(struct pci_epf *epf)  		return -EINVAL;  	epc_features = pci_epc_get_features(epc, epf->func_no); -	if (epc_features) { -		linkup_notifier = epc_features->linkup_notifier; -		core_init_notifier = epc_features->core_init_notifier; -		test_reg_bar = pci_epc_get_first_free_bar(epc_features); -		if (test_reg_bar < 0) -			return -EINVAL; -		pci_epf_configure_bar(epf, epc_features); +	if (!epc_features) { +		dev_err(&epf->dev, "epc_features not implemented\n"); +		return -EOPNOTSUPP;  	} +	linkup_notifier = epc_features->linkup_notifier; +	core_init_notifier = epc_features->core_init_notifier; +	test_reg_bar = pci_epc_get_first_free_bar(epc_features); +	if (test_reg_bar < 0) +		return -EINVAL; +	pci_epf_configure_bar(epf, epc_features); +  	epf_test->test_reg_bar = test_reg_bar;  	epf_test->epc_features = epc_features; @@ -922,6 +925,7 @@ static int __init pci_epf_test_init(void)  	ret = pci_epf_register_driver(&test_driver);  	if (ret) { +		destroy_workqueue(kpcitest_workqueue);  		pr_err("Failed to register pci epf test driver --> %d\n", ret);  		return ret;  	} @@ -932,6 +936,8 @@ module_init(pci_epf_test_init);  static void __exit pci_epf_test_exit(void)  { +	if (kpcitest_workqueue) +		destroy_workqueue(kpcitest_workqueue);  	pci_epf_unregister_driver(&test_driver);  }  module_exit(pci_epf_test_exit); diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index cc8f9eb2b177..adec9bee72cf 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -594,6 +594,8 @@ EXPORT_SYMBOL_GPL(pci_epc_add_epf);   * pci_epc_remove_epf() - remove PCI endpoint function from endpoint controller   * @epc: the EPC device from which the endpoint function should be removed   * @epf: the endpoint function to be removed + * @type: identifies if the EPC is connected to the primary or secondary + *        interface of EPF   *   * Invoke to remove PCI endpoint function from the endpoint controller.   */ diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 7646c8660d42..e9289d10f822 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -113,7 +113,7 @@ EXPORT_SYMBOL_GPL(pci_epf_bind);  void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar,  			enum pci_epc_interface_type type)  { -	struct device *dev = epf->epc->dev.parent; +	struct device *dev;  	struct pci_epf_bar *epf_bar;  	struct pci_epc *epc; diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 2750a64cecd3..4fedebf2f8c1 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -157,7 +157,7 @@ static int pcihp_is_ejectable(acpi_handle handle)  }  /** - * acpi_pcihp_check_ejectable - check if handle is ejectable ACPI PCI slot + * acpi_pci_check_ejectable - check if handle is ejectable ACPI PCI slot   * @pbus: the PCI bus of the PCI slot corresponding to 'handle'   * @handle: ACPI handle to check   * diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index a74b274a8c45..1f8ab4377ad8 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -148,8 +148,7 @@ static inline struct acpiphp_root_context *to_acpiphp_root_context(struct acpi_h   * ACPI has no generic method of setting/getting attention status   * this allows for device specific driver registration   */ -struct acpiphp_attention_info -{ +struct acpiphp_attention_info {  	int (*set_attn)(struct hotplug_slot *slot, u8 status);  	int (*get_attn)(struct hotplug_slot *slot, u8 *status);  	struct module *owner; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 3365c93abf0e..f031302ad401 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -533,6 +533,7 @@ static void enable_slot(struct acpiphp_slot *slot, bool bridge)  			slot->flags &= ~SLOT_ENABLED;  			continue;  		} +		pci_dev_put(dev);  	}  } diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c index 00cd2b43364f..7a65d427ac11 100644 --- a/drivers/pci/hotplug/cpqphp_nvram.c +++ b/drivers/pci/hotplug/cpqphp_nvram.c @@ -80,7 +80,7 @@ static u8 evbuffer[1024];  static void __iomem *compaq_int15_entry_point;  /* lock for ordering int15_bios_call() */ -static spinlock_t int15_lock; +static DEFINE_SPINLOCK(int15_lock);  /* This is a series of function that deals with @@ -415,9 +415,6 @@ void compaq_nvram_init(void __iomem *rom_start)  		compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);  	dbg("int15 entry  = %p\n", compaq_int15_entry_point); - -	/* initialize our int15 lock */ -	spin_lock_init(&int15_lock);  } diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index db047284c291..9e3b27744305 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -174,11 +174,6 @@ static inline u8 shpc_readb(struct controller *ctrl, int reg)  	return readb(ctrl->creg + reg);  } -static inline void shpc_writeb(struct controller *ctrl, int reg, u8 val) -{ -	writeb(val, ctrl->creg + reg); -} -  static inline u16 shpc_readw(struct controller *ctrl, int reg)  {  	return readw(ctrl->creg + reg); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 3162f88fe940..217dc9f0231f 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -64,39 +64,18 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev)  /* Arch hooks */  int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)  { -	struct msi_controller *chip = dev->bus->msi; -	int err; - -	if (!chip || !chip->setup_irq) -		return -EINVAL; - -	err = chip->setup_irq(chip, dev, desc); -	if (err < 0) -		return err; - -	irq_set_chip_data(desc->irq, chip); - -	return 0; +	return -EINVAL;  }  void __weak arch_teardown_msi_irq(unsigned int irq)  { -	struct msi_controller *chip = irq_get_chip_data(irq); - -	if (!chip || !chip->teardown_irq) -		return; - -	chip->teardown_irq(chip, irq);  }  int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)  { -	struct msi_controller *chip = dev->bus->msi;  	struct msi_desc *entry;  	int ret; -	if (chip && chip->setup_irqs) -		return chip->setup_irqs(chip, dev, nvec, type);  	/*  	 * If an architecture wants to support multiple MSI, it needs to  	 * override arch_setup_msi_irqs() @@ -115,11 +94,7 @@ int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)  	return 0;  } -/* - * We have a default implementation available as a separate non-weak - * function, as it is used by the Xen x86 PCI code - */ -void default_teardown_msi_irqs(struct pci_dev *dev) +void __weak arch_teardown_msi_irqs(struct pci_dev *dev)  {  	int i;  	struct msi_desc *entry; @@ -129,11 +104,6 @@ void default_teardown_msi_irqs(struct pci_dev *dev)  			for (i = 0; i < entry->nvec_used; i++)  				arch_teardown_msi_irq(entry->irq + i);  } - -void __weak arch_teardown_msi_irqs(struct pci_dev *dev) -{ -	return default_teardown_msi_irqs(dev); -}  #endif /* CONFIG_PCI_MSI_ARCH_FALLBACKS */  static void default_restore_msi_irq(struct pci_dev *dev, int irq) @@ -901,8 +871,15 @@ static int pci_msi_supported(struct pci_dev *dev, int nvec)  	 * Any bridge which does NOT route MSI transactions from its  	 * secondary bus to its primary bus must set NO_MSI flag on  	 * the secondary pci_bus. -	 * We expect only arch-specific PCI host bus controller driver -	 * or quirks for specific PCI bridges to be setting NO_MSI. +	 * +	 * The NO_MSI flag can either be set directly by: +	 * - arch-specific PCI host bus controller drivers (deprecated) +	 * - quirks for specific PCI bridges +	 * +	 * or indirectly by platform-specific PCI host bridge drivers by +	 * advertising the 'msi_domain' property, which results in +	 * the NO_MSI flag when no MSI domain is found for this bridge +	 * at probe time.  	 */  	for (bus = dev->bus; bus; bus = bus->parent)  		if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 5ea472ae22ac..da5b414d585a 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -190,10 +190,18 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res)  EXPORT_SYMBOL_GPL(of_pci_parse_bus_range);  /** - * This function will try to obtain the host bridge domain number by - * finding a property called "linux,pci-domain" of the given device node. + * of_get_pci_domain_nr - Find the host bridge domain number + *			  of the given device node. + * @node: Device tree node with the domain information.   * - * @node: device tree node with the domain information + * This function will try to obtain the host bridge domain number by finding + * a property called "linux,pci-domain" of the given device node. + * + * Return: + * * > 0	- On success, an associated domain number. + * * -EINVAL	- The property "linux,pci-domain" does not exist. + * * -ENODATA	- The linux,pci-domain" property does not have value. + * * -EOVERFLOW	- Invalid "linux,pci-domain" property value.   *   * Returns the associated domain number from DT in the range [0-0xffff], or   * a negative value if the required property is not found. @@ -585,10 +593,16 @@ int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge)  #endif /* CONFIG_PCI */  /** + * of_pci_get_max_link_speed - Find the maximum link speed of the given device node. + * @node: Device tree node with the maximum link speed information. + *   * This function will try to find the limitation of link speed by finding   * a property called "max-link-speed" of the given device node.   * - * @node: device tree node with the max link speed information + * Return: + * * > 0	- On success, a maximum link speed. + * * -EINVAL	- Invalid "max-link-speed" property value, or failure to access + *		  the property of the device tree node.   *   * Returns the associated max link speed from DT, or a negative value if the   * required property is not found or is invalid. diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 53502a751914..36bc23e21759 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1021,7 +1021,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)  	if (!error)  		pci_dbg(dev, "power state changed by ACPI to %s\n", -			 acpi_power_state_string(state_conv[state])); +		        acpi_power_state_string(adev->power.state));  	return error;  } diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index 781e45cf60d1..c32f3b7540e8 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -33,6 +33,21 @@  #include <linux/pci-acpi.h>  #include "pci.h" +static bool device_has_acpi_name(struct device *dev) +{ +#ifdef CONFIG_ACPI +	acpi_handle handle = ACPI_HANDLE(dev); + +	if (!handle) +		return false; + +	return acpi_check_dsm(handle, &pci_acpi_dsm_guid, 0x2, +			      1 << DSM_PCI_DEVICE_NAME); +#else +	return false; +#endif +} +  #ifdef CONFIG_DMI  enum smbios_attr_enum {  	SMBIOS_ATTR_NONE = 0, @@ -45,13 +60,9 @@ static size_t find_smbios_instance_string(struct pci_dev *pdev, char *buf,  {  	const struct dmi_device *dmi;  	struct dmi_dev_onboard *donboard; -	int domain_nr; -	int bus; -	int devfn; - -	domain_nr = pci_domain_nr(pdev->bus); -	bus = pdev->bus->number; -	devfn = pdev->devfn; +	int domain_nr = pci_domain_nr(pdev->bus); +	int bus = pdev->bus->number; +	int devfn = pdev->devfn;  	dmi = NULL;  	while ((dmi = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, @@ -62,13 +73,11 @@ static size_t find_smbios_instance_string(struct pci_dev *pdev, char *buf,  				donboard->devfn == devfn) {  			if (buf) {  				if (attribute == SMBIOS_ATTR_INSTANCE_SHOW) -					return scnprintf(buf, PAGE_SIZE, -							 "%d\n", -							 donboard->instance); +					return sysfs_emit(buf, "%d\n", +							  donboard->instance);  				else if (attribute == SMBIOS_ATTR_LABEL_SHOW) -					return scnprintf(buf, PAGE_SIZE, -							 "%s\n", -							 dmi->name); +					return sysfs_emit(buf, "%s\n", +							  dmi->name);  			}  			return strlen(dmi->name);  		} @@ -76,78 +85,52 @@ static size_t find_smbios_instance_string(struct pci_dev *pdev, char *buf,  	return 0;  } -static umode_t smbios_instance_string_exist(struct kobject *kobj, -					    struct attribute *attr, int n) +static ssize_t smbios_label_show(struct device *dev, +				 struct device_attribute *attr, char *buf)  { -	struct device *dev; -	struct pci_dev *pdev; - -	dev = kobj_to_dev(kobj); -	pdev = to_pci_dev(dev); - -	return find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE) ? -					   S_IRUGO : 0; -} - -static ssize_t smbioslabel_show(struct device *dev, -				struct device_attribute *attr, char *buf) -{ -	struct pci_dev *pdev; -	pdev = to_pci_dev(dev); +	struct pci_dev *pdev = to_pci_dev(dev);  	return find_smbios_instance_string(pdev, buf,  					   SMBIOS_ATTR_LABEL_SHOW);  } +static struct device_attribute dev_attr_smbios_label = __ATTR(label, 0444, +						    smbios_label_show, NULL); -static ssize_t smbiosinstance_show(struct device *dev, -				   struct device_attribute *attr, char *buf) +static ssize_t index_show(struct device *dev, struct device_attribute *attr, +			  char *buf)  { -	struct pci_dev *pdev; -	pdev = to_pci_dev(dev); +	struct pci_dev *pdev = to_pci_dev(dev);  	return find_smbios_instance_string(pdev, buf,  					   SMBIOS_ATTR_INSTANCE_SHOW);  } +static DEVICE_ATTR_RO(index); -static struct device_attribute smbios_attr_label = { -	.attr = {.name = "label", .mode = 0444}, -	.show = smbioslabel_show, -}; - -static struct device_attribute smbios_attr_instance = { -	.attr = {.name = "index", .mode = 0444}, -	.show = smbiosinstance_show, -}; - -static struct attribute *smbios_attributes[] = { -	&smbios_attr_label.attr, -	&smbios_attr_instance.attr, +static struct attribute *smbios_attrs[] = { +	&dev_attr_smbios_label.attr, +	&dev_attr_index.attr,  	NULL,  }; -static const struct attribute_group smbios_attr_group = { -	.attrs = smbios_attributes, -	.is_visible = smbios_instance_string_exist, -}; - -static int pci_create_smbiosname_file(struct pci_dev *pdev) +static umode_t smbios_attr_is_visible(struct kobject *kobj, struct attribute *a, +				      int n)  { -	return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group); -} +	struct device *dev = kobj_to_dev(kobj); +	struct pci_dev *pdev = to_pci_dev(dev); -static void pci_remove_smbiosname_file(struct pci_dev *pdev) -{ -	sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); -} -#else -static inline int pci_create_smbiosname_file(struct pci_dev *pdev) -{ -	return -1; -} +	if (device_has_acpi_name(dev)) +		return 0; -static inline void pci_remove_smbiosname_file(struct pci_dev *pdev) -{ +	if (!find_smbios_instance_string(pdev, NULL, SMBIOS_ATTR_NONE)) +		return 0; + +	return a->mode;  } + +const struct attribute_group pci_dev_smbios_attr_group = { +	.attrs = smbios_attrs, +	.is_visible = smbios_attr_is_visible, +};  #endif  #ifdef CONFIG_ACPI @@ -169,11 +152,10 @@ static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)  static int dsm_get_label(struct device *dev, char *buf,  			 enum acpi_attr_enum attr)  { -	acpi_handle handle; +	acpi_handle handle = ACPI_HANDLE(dev);  	union acpi_object *obj, *tmp;  	int len = -1; -	handle = ACPI_HANDLE(dev);  	if (!handle)  		return -1; @@ -209,103 +191,39 @@ static int dsm_get_label(struct device *dev, char *buf,  	return len;  } -static bool device_has_dsm(struct device *dev) -{ -	acpi_handle handle; - -	handle = ACPI_HANDLE(dev); -	if (!handle) -		return false; - -	return !!acpi_check_dsm(handle, &pci_acpi_dsm_guid, 0x2, -				1 << DSM_PCI_DEVICE_NAME); -} - -static umode_t acpi_index_string_exist(struct kobject *kobj, -				       struct attribute *attr, int n) -{ -	struct device *dev; - -	dev = kobj_to_dev(kobj); - -	if (device_has_dsm(dev)) -		return S_IRUGO; - -	return 0; -} - -static ssize_t acpilabel_show(struct device *dev, -			      struct device_attribute *attr, char *buf) +static ssize_t label_show(struct device *dev, struct device_attribute *attr, +			  char *buf)  {  	return dsm_get_label(dev, buf, ACPI_ATTR_LABEL_SHOW);  } +static DEVICE_ATTR_RO(label); -static ssize_t acpiindex_show(struct device *dev, +static ssize_t acpi_index_show(struct device *dev,  			      struct device_attribute *attr, char *buf)  {  	return dsm_get_label(dev, buf, ACPI_ATTR_INDEX_SHOW);  } +static DEVICE_ATTR_RO(acpi_index); -static struct device_attribute acpi_attr_label = { -	.attr = {.name = "label", .mode = 0444}, -	.show = acpilabel_show, -}; - -static struct device_attribute acpi_attr_index = { -	.attr = {.name = "acpi_index", .mode = 0444}, -	.show = acpiindex_show, -}; - -static struct attribute *acpi_attributes[] = { -	&acpi_attr_label.attr, -	&acpi_attr_index.attr, +static struct attribute *acpi_attrs[] = { +	&dev_attr_label.attr, +	&dev_attr_acpi_index.attr,  	NULL,  }; -static const struct attribute_group acpi_attr_group = { -	.attrs = acpi_attributes, -	.is_visible = acpi_index_string_exist, -}; - -static int pci_create_acpi_index_label_files(struct pci_dev *pdev) +static umode_t acpi_attr_is_visible(struct kobject *kobj, struct attribute *a, +				    int n)  { -	return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group); -} +	struct device *dev = kobj_to_dev(kobj); -static int pci_remove_acpi_index_label_files(struct pci_dev *pdev) -{ -	sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group); -	return 0; -} -#else -static inline int pci_create_acpi_index_label_files(struct pci_dev *pdev) -{ -	return -1; -} +	if (!device_has_acpi_name(dev)) +		return 0; -static inline int pci_remove_acpi_index_label_files(struct pci_dev *pdev) -{ -	return -1; +	return a->mode;  } -static inline bool device_has_dsm(struct device *dev) -{ -	return false; -} +const struct attribute_group pci_dev_acpi_attr_group = { +	.attrs = acpi_attrs, +	.is_visible = acpi_attr_is_visible, +};  #endif - -void pci_create_firmware_label_files(struct pci_dev *pdev) -{ -	if (device_has_dsm(&pdev->dev)) -		pci_create_acpi_index_label_files(pdev); -	else -		pci_create_smbiosname_file(pdev); -} - -void pci_remove_firmware_label_files(struct pci_dev *pdev) -{ -	if (device_has_dsm(&pdev->dev)) -		pci_remove_acpi_index_label_files(pdev); -	else -		pci_remove_smbiosname_file(pdev); -} diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index a6b8fbbba6d2..beb8d1f4fafe 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -39,7 +39,7 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf)				\  	struct pci_dev *pdev;						\  									\  	pdev = to_pci_dev(dev);						\ -	return sprintf(buf, format_string, pdev->field);		\ +	return sysfs_emit(buf, format_string, pdev->field);		\  }									\  static DEVICE_ATTR_RO(field) @@ -56,7 +56,7 @@ static ssize_t broken_parity_status_show(struct device *dev,  					 char *buf)  {  	struct pci_dev *pdev = to_pci_dev(dev); -	return sprintf(buf, "%u\n", pdev->broken_parity_status); +	return sysfs_emit(buf, "%u\n", pdev->broken_parity_status);  }  static ssize_t broken_parity_status_store(struct device *dev, @@ -129,7 +129,7 @@ static ssize_t power_state_show(struct device *dev,  {  	struct pci_dev *pdev = to_pci_dev(dev); -	return sprintf(buf, "%s\n", pci_power_name(pdev->current_state)); +	return sysfs_emit(buf, "%s\n", pci_power_name(pdev->current_state));  }  static DEVICE_ATTR_RO(power_state); @@ -138,10 +138,10 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,  			     char *buf)  {  	struct pci_dev *pci_dev = to_pci_dev(dev); -	char *str = buf;  	int i;  	int max;  	resource_size_t start, end; +	size_t len = 0;  	if (pci_dev->subordinate)  		max = DEVICE_COUNT_RESOURCE; @@ -151,12 +151,12 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,  	for (i = 0; i < max; i++) {  		struct resource *res =  &pci_dev->resource[i];  		pci_resource_to_user(pci_dev, i, res, &start, &end); -		str += sprintf(str, "0x%016llx 0x%016llx 0x%016llx\n", -			       (unsigned long long)start, -			       (unsigned long long)end, -			       (unsigned long long)res->flags); +		len += sysfs_emit_at(buf, len, "0x%016llx 0x%016llx 0x%016llx\n", +				     (unsigned long long)start, +				     (unsigned long long)end, +				     (unsigned long long)res->flags);  	} -	return (str - buf); +	return len;  }  static DEVICE_ATTR_RO(resource); @@ -165,8 +165,8 @@ static ssize_t max_link_speed_show(struct device *dev,  {  	struct pci_dev *pdev = to_pci_dev(dev); -	return sprintf(buf, "%s\n", -		       pci_speed_string(pcie_get_speed_cap(pdev))); +	return sysfs_emit(buf, "%s\n", +			  pci_speed_string(pcie_get_speed_cap(pdev)));  }  static DEVICE_ATTR_RO(max_link_speed); @@ -175,7 +175,7 @@ static ssize_t max_link_width_show(struct device *dev,  {  	struct pci_dev *pdev = to_pci_dev(dev); -	return sprintf(buf, "%u\n", pcie_get_width_cap(pdev)); +	return sysfs_emit(buf, "%u\n", pcie_get_width_cap(pdev));  }  static DEVICE_ATTR_RO(max_link_width); @@ -193,7 +193,7 @@ static ssize_t current_link_speed_show(struct device *dev,  	speed = pcie_link_speed[linkstat & PCI_EXP_LNKSTA_CLS]; -	return sprintf(buf, "%s\n", pci_speed_string(speed)); +	return sysfs_emit(buf, "%s\n", pci_speed_string(speed));  }  static DEVICE_ATTR_RO(current_link_speed); @@ -208,7 +208,7 @@ static ssize_t current_link_width_show(struct device *dev,  	if (err)  		return -EINVAL; -	return sprintf(buf, "%u\n", +	return sysfs_emit(buf, "%u\n",  		(linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT);  }  static DEVICE_ATTR_RO(current_link_width); @@ -225,7 +225,7 @@ static ssize_t secondary_bus_number_show(struct device *dev,  	if (err)  		return -EINVAL; -	return sprintf(buf, "%u\n", sec_bus); +	return sysfs_emit(buf, "%u\n", sec_bus);  }  static DEVICE_ATTR_RO(secondary_bus_number); @@ -241,7 +241,7 @@ static ssize_t subordinate_bus_number_show(struct device *dev,  	if (err)  		return -EINVAL; -	return sprintf(buf, "%u\n", sub_bus); +	return sysfs_emit(buf, "%u\n", sub_bus);  }  static DEVICE_ATTR_RO(subordinate_bus_number); @@ -251,7 +251,7 @@ static ssize_t ari_enabled_show(struct device *dev,  {  	struct pci_dev *pci_dev = to_pci_dev(dev); -	return sprintf(buf, "%u\n", pci_ari_enabled(pci_dev->bus)); +	return sysfs_emit(buf, "%u\n", pci_ari_enabled(pci_dev->bus));  }  static DEVICE_ATTR_RO(ari_enabled); @@ -260,11 +260,11 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,  {  	struct pci_dev *pci_dev = to_pci_dev(dev); -	return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X\n", -		       pci_dev->vendor, pci_dev->device, -		       pci_dev->subsystem_vendor, pci_dev->subsystem_device, -		       (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), -		       (u8)(pci_dev->class)); +	return sysfs_emit(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X\n", +			  pci_dev->vendor, pci_dev->device, +			  pci_dev->subsystem_vendor, pci_dev->subsystem_device, +			  (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), +			  (u8)(pci_dev->class));  }  static DEVICE_ATTR_RO(modalias); @@ -302,7 +302,7 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr,  	struct pci_dev *pdev;  	pdev = to_pci_dev(dev); -	return sprintf(buf, "%u\n", atomic_read(&pdev->enable_cnt)); +	return sysfs_emit(buf, "%u\n", atomic_read(&pdev->enable_cnt));  }  static DEVICE_ATTR_RW(enable); @@ -338,7 +338,7 @@ static ssize_t numa_node_store(struct device *dev,  static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr,  			      char *buf)  { -	return sprintf(buf, "%d\n", dev->numa_node); +	return sysfs_emit(buf, "%d\n", dev->numa_node);  }  static DEVICE_ATTR_RW(numa_node);  #endif @@ -348,7 +348,7 @@ static ssize_t dma_mask_bits_show(struct device *dev,  {  	struct pci_dev *pdev = to_pci_dev(dev); -	return sprintf(buf, "%d\n", fls64(pdev->dma_mask)); +	return sysfs_emit(buf, "%d\n", fls64(pdev->dma_mask));  }  static DEVICE_ATTR_RO(dma_mask_bits); @@ -356,7 +356,7 @@ static ssize_t consistent_dma_mask_bits_show(struct device *dev,  					     struct device_attribute *attr,  					     char *buf)  { -	return sprintf(buf, "%d\n", fls64(dev->coherent_dma_mask)); +	return sysfs_emit(buf, "%d\n", fls64(dev->coherent_dma_mask));  }  static DEVICE_ATTR_RO(consistent_dma_mask_bits); @@ -366,9 +366,9 @@ static ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr,  	struct pci_dev *pdev = to_pci_dev(dev);  	struct pci_bus *subordinate = pdev->subordinate; -	return sprintf(buf, "%u\n", subordinate ? -		       !(subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) -			   : !pdev->no_msi); +	return sysfs_emit(buf, "%u\n", subordinate ? +			  !(subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) +			    : !pdev->no_msi);  }  static ssize_t msi_bus_store(struct device *dev, struct device_attribute *attr, @@ -523,7 +523,7 @@ static ssize_t d3cold_allowed_show(struct device *dev,  				   struct device_attribute *attr, char *buf)  {  	struct pci_dev *pdev = to_pci_dev(dev); -	return sprintf(buf, "%u\n", pdev->d3cold_allowed); +	return sysfs_emit(buf, "%u\n", pdev->d3cold_allowed);  }  static DEVICE_ATTR_RW(d3cold_allowed);  #endif @@ -537,7 +537,7 @@ static ssize_t devspec_show(struct device *dev,  	if (np == NULL)  		return 0; -	return sprintf(buf, "%pOF", np); +	return sysfs_emit(buf, "%pOF", np);  }  static DEVICE_ATTR_RO(devspec);  #endif @@ -583,7 +583,7 @@ static ssize_t driver_override_show(struct device *dev,  	ssize_t len;  	device_lock(dev); -	len = scnprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override); +	len = sysfs_emit(buf, "%s\n", pdev->driver_override);  	device_unlock(dev);  	return len;  } @@ -658,11 +658,11 @@ static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,  	struct pci_dev *vga_dev = vga_default_device();  	if (vga_dev) -		return sprintf(buf, "%u\n", (pdev == vga_dev)); +		return sysfs_emit(buf, "%u\n", (pdev == vga_dev)); -	return sprintf(buf, "%u\n", -		!!(pdev->resource[PCI_ROM_RESOURCE].flags & -		   IORESOURCE_ROM_SHADOW)); +	return sysfs_emit(buf, "%u\n", +			  !!(pdev->resource[PCI_ROM_RESOURCE].flags & +			     IORESOURCE_ROM_SHADOW));  }  static DEVICE_ATTR_RO(boot_vga); @@ -808,6 +808,29 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,  	return count;  } +static BIN_ATTR(config, 0644, pci_read_config, pci_write_config, 0); + +static struct bin_attribute *pci_dev_config_attrs[] = { +	&bin_attr_config, +	NULL, +}; + +static umode_t pci_dev_config_attr_is_visible(struct kobject *kobj, +					      struct bin_attribute *a, int n) +{ +	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + +	a->size = PCI_CFG_SPACE_SIZE; +	if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) +		a->size = PCI_CFG_SPACE_EXP_SIZE; + +	return a->attr.mode; +} + +static const struct attribute_group pci_dev_config_attr_group = { +	.bin_attrs = pci_dev_config_attrs, +	.is_bin_visible = pci_dev_config_attr_is_visible, +};  #ifdef HAVE_PCI_LEGACY  /** @@ -1283,25 +1306,32 @@ static ssize_t pci_read_rom(struct file *filp, struct kobject *kobj,  	return count;  } +static BIN_ATTR(rom, 0600, pci_read_rom, pci_write_rom, 0); -static const struct bin_attribute pci_config_attr = { -	.attr =	{ -		.name = "config", -		.mode = 0644, -	}, -	.size = PCI_CFG_SPACE_SIZE, -	.read = pci_read_config, -	.write = pci_write_config, +static struct bin_attribute *pci_dev_rom_attrs[] = { +	&bin_attr_rom, +	NULL,  }; -static const struct bin_attribute pcie_config_attr = { -	.attr =	{ -		.name = "config", -		.mode = 0644, -	}, -	.size = PCI_CFG_SPACE_EXP_SIZE, -	.read = pci_read_config, -	.write = pci_write_config, +static umode_t pci_dev_rom_attr_is_visible(struct kobject *kobj, +					   struct bin_attribute *a, int n) +{ +	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); +	size_t rom_size; + +	/* If the device has a ROM, try to expose it in sysfs. */ +	rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); +	if (!rom_size) +		return 0; + +	a->size = rom_size; + +	return a->attr.mode; +} + +static const struct attribute_group pci_dev_rom_attr_group = { +	.bin_attrs = pci_dev_rom_attrs, +	.is_bin_visible = pci_dev_rom_attr_is_visible,  };  static ssize_t reset_store(struct device *dev, struct device_attribute *attr, @@ -1325,102 +1355,35 @@ static ssize_t reset_store(struct device *dev, struct device_attribute *attr,  	return count;  } +static DEVICE_ATTR_WO(reset); -static DEVICE_ATTR(reset, 0200, NULL, reset_store); +static struct attribute *pci_dev_reset_attrs[] = { +	&dev_attr_reset.attr, +	NULL, +}; -static int pci_create_capabilities_sysfs(struct pci_dev *dev) +static umode_t pci_dev_reset_attr_is_visible(struct kobject *kobj, +					     struct attribute *a, int n)  { -	int retval; - -	pcie_vpd_create_sysfs_dev_files(dev); +	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); -	if (dev->reset_fn) { -		retval = device_create_file(&dev->dev, &dev_attr_reset); -		if (retval) -			goto error; -	} -	return 0; +	if (!pdev->reset_fn) +		return 0; -error: -	pcie_vpd_remove_sysfs_dev_files(dev); -	return retval; +	return a->mode;  } +static const struct attribute_group pci_dev_reset_attr_group = { +	.attrs = pci_dev_reset_attrs, +	.is_visible = pci_dev_reset_attr_is_visible, +}; +  int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)  { -	int retval; -	int rom_size; -	struct bin_attribute *attr; -  	if (!sysfs_initialized)  		return -EACCES; -	if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) -		retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); -	else -		retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); -	if (retval) -		goto err; - -	retval = pci_create_resource_files(pdev); -	if (retval) -		goto err_config_file; - -	/* If the device has a ROM, try to expose it in sysfs. */ -	rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); -	if (rom_size) { -		attr = kzalloc(sizeof(*attr), GFP_ATOMIC); -		if (!attr) { -			retval = -ENOMEM; -			goto err_resource_files; -		} -		sysfs_bin_attr_init(attr); -		attr->size = rom_size; -		attr->attr.name = "rom"; -		attr->attr.mode = 0600; -		attr->read = pci_read_rom; -		attr->write = pci_write_rom; -		retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); -		if (retval) { -			kfree(attr); -			goto err_resource_files; -		} -		pdev->rom_attr = attr; -	} - -	/* add sysfs entries for various capabilities */ -	retval = pci_create_capabilities_sysfs(pdev); -	if (retval) -		goto err_rom_file; - -	pci_create_firmware_label_files(pdev); - -	return 0; - -err_rom_file: -	if (pdev->rom_attr) { -		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); -		kfree(pdev->rom_attr); -		pdev->rom_attr = NULL; -	} -err_resource_files: -	pci_remove_resource_files(pdev); -err_config_file: -	if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) -		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); -	else -		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); -err: -	return retval; -} - -static void pci_remove_capabilities_sysfs(struct pci_dev *dev) -{ -	pcie_vpd_remove_sysfs_dev_files(dev); -	if (dev->reset_fn) { -		device_remove_file(&dev->dev, &dev_attr_reset); -		dev->reset_fn = 0; -	} +	return pci_create_resource_files(pdev);  }  /** @@ -1434,22 +1397,7 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)  	if (!sysfs_initialized)  		return; -	pci_remove_capabilities_sysfs(pdev); - -	if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) -		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); -	else -		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); -  	pci_remove_resource_files(pdev); - -	if (pdev->rom_attr) { -		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); -		kfree(pdev->rom_attr); -		pdev->rom_attr = NULL; -	} - -	pci_remove_firmware_label_files(pdev);  }  static int __init pci_sysfs_init(void) @@ -1540,6 +1488,16 @@ static const struct attribute_group pci_dev_group = {  const struct attribute_group *pci_dev_groups[] = {  	&pci_dev_group, +	&pci_dev_config_attr_group, +	&pci_dev_rom_attr_group, +	&pci_dev_reset_attr_group, +	&pci_dev_vpd_attr_group, +#ifdef CONFIG_DMI +	&pci_dev_smbios_attr_group, +#endif +#ifdef CONFIG_ACPI +	&pci_dev_acpi_attr_group, +#endif  	NULL,  }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1027f180792c..b717680377a9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4072,6 +4072,7 @@ phys_addr_t pci_pio_to_address(unsigned long pio)  	return address;  } +EXPORT_SYMBOL_GPL(pci_pio_to_address);  unsigned long __weak pci_address_to_pio(phys_addr_t address)  { @@ -4474,6 +4475,23 @@ void pci_clear_mwi(struct pci_dev *dev)  EXPORT_SYMBOL(pci_clear_mwi);  /** + * pci_disable_parity - disable parity checking for device + * @dev: the PCI device to operate on + * + * Disable parity checking for device @dev + */ +void pci_disable_parity(struct pci_dev *dev) +{ +	u16 cmd; + +	pci_read_config_word(dev, PCI_COMMAND, &cmd); +	if (cmd & PCI_COMMAND_PARITY) { +		cmd &= ~PCI_COMMAND_PARITY; +		pci_write_config_word(dev, PCI_COMMAND, cmd); +	} +} + +/**   * pci_intx - enables/disables PCI INTx for device dev   * @pdev: the PCI device to operate on   * @enable: boolean: whether to enable or disable PCI INTx diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index afb87b917f07..37c913bbc6e1 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -21,16 +21,10 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev);  int pci_create_sysfs_dev_files(struct pci_dev *pdev);  void pci_remove_sysfs_dev_files(struct pci_dev *pdev); -#if !defined(CONFIG_DMI) && !defined(CONFIG_ACPI) -static inline void pci_create_firmware_label_files(struct pci_dev *pdev) -{ return; } -static inline void pci_remove_firmware_label_files(struct pci_dev *pdev) -{ return; } -#else -void pci_create_firmware_label_files(struct pci_dev *pdev); -void pci_remove_firmware_label_files(struct pci_dev *pdev); -#endif  void pci_cleanup_rom(struct pci_dev *dev); +#ifdef CONFIG_DMI +extern const struct attribute_group pci_dev_smbios_attr_group; +#endif  enum pci_mmap_api {  	PCI_MMAP_SYSFS,	/* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */ @@ -141,10 +135,9 @@ static inline bool pcie_downstream_port(const struct pci_dev *dev)  	       type == PCI_EXP_TYPE_PCIE_BRIDGE;  } -int pci_vpd_init(struct pci_dev *dev); +void pci_vpd_init(struct pci_dev *dev);  void pci_vpd_release(struct pci_dev *dev); -void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev); -void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev); +extern const struct attribute_group pci_dev_vpd_attr_group;  /* PCI Virtual Channel */  int pci_save_vc_state(struct pci_dev *dev); @@ -625,6 +618,12 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)  #if defined(CONFIG_PCI_QUIRKS) && defined(CONFIG_ARM64)  int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment,  			  struct resource *res); +#else +static inline int acpi_get_rc_resources(struct device *dev, const char *hid, +					u16 segment, struct resource *res) +{ +	return -ENODEV; +}  #endif  int pci_rebar_get_current_size(struct pci_dev *pdev, int bar); @@ -697,6 +696,7 @@ static inline int pci_aer_raw_clear_status(struct pci_dev *dev) { return -EINVAL  #ifdef CONFIG_ACPI  int pci_acpi_program_hp_params(struct pci_dev *dev); +extern const struct attribute_group pci_dev_acpi_attr_group;  #else  static inline int pci_acpi_program_hp_params(struct pci_dev *dev)  { diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index ba22388342d1..ec943cee5ecc 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -129,7 +129,7 @@ static const char * const ecrc_policy_str[] = {  };  /** - * enable_ercr_checking - enable PCIe ECRC checking for a device + * enable_ecrc_checking - enable PCIe ECRC checking for a device   * @dev: the PCI device   *   * Returns 0 on success, or negative on failure. @@ -153,7 +153,7 @@ static int enable_ecrc_checking(struct pci_dev *dev)  }  /** - * disable_ercr_checking - disables PCIe ECRC checking for a device + * disable_ecrc_checking - disables PCIe ECRC checking for a device   * @dev: the PCI device   *   * Returns 0 on success, or negative on failure. @@ -1442,7 +1442,7 @@ static struct pcie_port_service_driver aerdriver = {  };  /** - * aer_service_init - register AER root service driver + * pcie_aer_init - register AER root service driver   *   * Invoked when AER root service driver is loaded.   */ diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 3fc08488d65f..1d0dd77fed3a 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -463,7 +463,7 @@ static struct pcie_port_service_driver pcie_pme_driver = {  };  /** - * pcie_pme_service_init - Register the PCIe PME service driver. + * pcie_pme_init - Register the PCIe PME service driver.   */  int __init pcie_pme_init(void)  { diff --git a/drivers/pci/pcie/rcec.c b/drivers/pci/pcie/rcec.c index 2c5c552994e4..d0bcd141ac9c 100644 --- a/drivers/pci/pcie/rcec.c +++ b/drivers/pci/pcie/rcec.c @@ -32,7 +32,7 @@ static bool rcec_assoc_rciep(struct pci_dev *rcec, struct pci_dev *rciep)  	/* Same bus, so check bitmap */  	for_each_set_bit(devn, &bitmap, 32) -		if (devn == rciep->devfn) +		if (devn == PCI_SLOT(rciep->devfn))  			return true;  	return false; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 953f15abc850..3a62d09b8869 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -895,7 +895,6 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)  	/* Temporarily move resources off the list */  	list_splice_init(&bridge->windows, &resources);  	bus->sysdata = bridge->sysdata; -	bus->msi = bridge->msi;  	bus->ops = bridge->ops;  	bus->number = bus->busn_res.start = bridge->busnr;  #ifdef CONFIG_PCI_DOMAINS_GENERIC @@ -926,6 +925,8 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)  	device_enable_async_suspend(bus->bridge);  	pci_set_bus_of_node(bus);  	pci_set_bus_msi_domain(bus); +	if (bridge->msi_domain && !dev_get_msi_domain(&bus->dev)) +		bus->bus_flags |= PCI_BUS_FLAGS_NO_MSI;  	if (!parent)  		set_dev_node(bus->bridge, pcibus_to_node(bus)); @@ -1053,7 +1054,6 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,  		return NULL;  	child->parent = parent; -	child->msi = parent->msi;  	child->sysdata = parent->sysdata;  	child->bus_flags = parent->bus_flags; @@ -2353,6 +2353,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)  	pci_set_of_node(dev);  	if (pci_setup_device(dev)) { +		pci_release_of_node(dev);  		pci_bus_put(dev->bus);  		kfree(dev);  		return NULL; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 653660e3ba9e..dcb229de1acb 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -206,16 +206,11 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID,  				PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on);  /* - * The Mellanox Tavor device gives false positive parity errors.  Mark this - * device with a broken_parity_status to allow PCI scanning code to "skip" - * this now blacklisted device. + * The Mellanox Tavor device gives false positive parity errors.  Disable + * parity error reporting.   */ -static void quirk_mellanox_tavor(struct pci_dev *dev) -{ -	dev->broken_parity_status = 1;	/* This device gives false positives */ -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR, quirk_mellanox_tavor); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE, quirk_mellanox_tavor); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR, pci_disable_parity); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE, pci_disable_parity);  /*   * Deal with broken BIOSes that neglect to enable passive release, @@ -2585,10 +2580,8 @@ static int msi_ht_cap_enabled(struct pci_dev *dev)  /* Check the HyperTransport MSI mapping to know whether MSI is enabled or not */  static void quirk_msi_ht_cap(struct pci_dev *dev)  { -	if (dev->subordinate && !msi_ht_cap_enabled(dev)) { -		pci_warn(dev, "MSI quirk detected; subordinate MSI disabled\n"); -		dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; -	} +	if (!msi_ht_cap_enabled(dev)) +		quirk_disable_msi(dev);  }  DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE,  			quirk_msi_ht_cap); @@ -2601,9 +2594,6 @@ static void quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)  {  	struct pci_dev *pdev; -	if (!dev->subordinate) -		return; -  	/*  	 * Check HT MSI cap on this chipset and the root one.  A single one  	 * having MSI is enough to be sure that MSI is supported. @@ -2611,10 +2601,8 @@ static void quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)  	pdev = pci_get_slot(dev->bus, 0);  	if (!pdev)  		return; -	if (!msi_ht_cap_enabled(dev) && !msi_ht_cap_enabled(pdev)) { -		pci_warn(dev, "MSI quirk detected; subordinate MSI disabled\n"); -		dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; -	} +	if (!msi_ht_cap_enabled(pdev)) +		quirk_msi_ht_cap(dev);  	pci_dev_put(pdev);  }  DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, @@ -3922,6 +3910,7 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {  		reset_ivb_igd },  	{ PCI_VENDOR_ID_SAMSUNG, 0xa804, nvme_disable_and_flr },  	{ PCI_VENDOR_ID_INTEL, 0x0953, delay_250ms_after_flr }, +	{ PCI_VENDOR_ID_INTEL, 0x0a54, delay_250ms_after_flr },  	{ PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,  		reset_chelsio_generic_dev },  	{ 0 } diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 95dec03d9f2a..dd12c2fcc7dc 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -19,6 +19,8 @@ static void pci_stop_dev(struct pci_dev *dev)  	pci_pme_active(dev, false);  	if (pci_dev_is_added(dev)) { +		dev->reset_fn = 0; +  		device_release_driver(&dev->dev);  		pci_proc_detach_device(dev);  		pci_remove_sysfs_dev_files(dev); diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 7915d10f9aa1..26bf7c877de5 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -16,12 +16,10 @@  struct pci_vpd_ops {  	ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);  	ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); -	int (*set_size)(struct pci_dev *dev, size_t len);  };  struct pci_vpd {  	const struct pci_vpd_ops *ops; -	struct bin_attribute *attr;	/* Descriptor for sysfs VPD entry */  	struct mutex	lock;  	unsigned int	len;  	u16		flag; @@ -30,6 +28,11 @@ struct pci_vpd {  	unsigned int	valid:1;  }; +static struct pci_dev *pci_get_func0_dev(struct pci_dev *dev) +{ +	return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); +} +  /**   * pci_read_vpd - Read one entry from Vital Product Data   * @dev:	pci device struct @@ -60,19 +63,6 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void  }  EXPORT_SYMBOL(pci_write_vpd); -/** - * pci_set_vpd_size - Set size of Vital Product Data space - * @dev:	pci device struct - * @len:	size of vpd space - */ -int pci_set_vpd_size(struct pci_dev *dev, size_t len) -{ -	if (!dev->vpd || !dev->vpd->ops) -		return -ENODEV; -	return dev->vpd->ops->set_size(dev, len); -} -EXPORT_SYMBOL(pci_set_vpd_size); -  #define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1)  /** @@ -85,10 +75,14 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)  	size_t off = 0;  	unsigned char header[1+2];	/* 1 byte tag, 2 bytes length */ -	while (off < old_size && -	       pci_read_vpd(dev, off, 1, header) == 1) { +	while (off < old_size && pci_read_vpd(dev, off, 1, header) == 1) {  		unsigned char tag; +		if (!header[0] && !off) { +			pci_info(dev, "Invalid VPD tag 00, assume missing optional VPD EPROM\n"); +			return 0; +		} +  		if (header[0] & PCI_VPD_LRDT) {  			/* Large Resource Data Type Tag */  			tag = pci_vpd_lrdt_tag(header); @@ -297,30 +291,15 @@ out:  	return ret ? ret : count;  } -static int pci_vpd_set_size(struct pci_dev *dev, size_t len) -{ -	struct pci_vpd *vpd = dev->vpd; - -	if (len == 0 || len > PCI_VPD_MAX_SIZE) -		return -EIO; - -	vpd->valid = 1; -	vpd->len = len; - -	return 0; -} -  static const struct pci_vpd_ops pci_vpd_ops = {  	.read = pci_vpd_read,  	.write = pci_vpd_write, -	.set_size = pci_vpd_set_size,  };  static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count,  			       void *arg)  { -	struct pci_dev *tdev = pci_get_slot(dev->bus, -					    PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); +	struct pci_dev *tdev = pci_get_func0_dev(dev);  	ssize_t ret;  	if (!tdev) @@ -334,8 +313,7 @@ static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count,  static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count,  				const void *arg)  { -	struct pci_dev *tdev = pci_get_slot(dev->bus, -					    PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); +	struct pci_dev *tdev = pci_get_func0_dev(dev);  	ssize_t ret;  	if (!tdev) @@ -346,38 +324,23 @@ static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count,  	return ret;  } -static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len) -{ -	struct pci_dev *tdev = pci_get_slot(dev->bus, -					    PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); -	int ret; - -	if (!tdev) -		return -ENODEV; - -	ret = pci_set_vpd_size(tdev, len); -	pci_dev_put(tdev); -	return ret; -} -  static const struct pci_vpd_ops pci_vpd_f0_ops = {  	.read = pci_vpd_f0_read,  	.write = pci_vpd_f0_write, -	.set_size = pci_vpd_f0_set_size,  }; -int pci_vpd_init(struct pci_dev *dev) +void pci_vpd_init(struct pci_dev *dev)  {  	struct pci_vpd *vpd;  	u8 cap;  	cap = pci_find_capability(dev, PCI_CAP_ID_VPD);  	if (!cap) -		return -ENODEV; +		return;  	vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC);  	if (!vpd) -		return -ENOMEM; +		return;  	vpd->len = PCI_VPD_MAX_SIZE;  	if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) @@ -389,7 +352,6 @@ int pci_vpd_init(struct pci_dev *dev)  	vpd->busy = 0;  	vpd->valid = 0;  	dev->vpd = vpd; -	return 0;  }  void pci_vpd_release(struct pci_dev *dev) @@ -397,102 +359,56 @@ void pci_vpd_release(struct pci_dev *dev)  	kfree(dev->vpd);  } -static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj, -			     struct bin_attribute *bin_attr, char *buf, -			     loff_t off, size_t count) +static ssize_t vpd_read(struct file *filp, struct kobject *kobj, +			struct bin_attribute *bin_attr, char *buf, loff_t off, +			size_t count)  {  	struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); -	if (bin_attr->size > 0) { -		if (off > bin_attr->size) -			count = 0; -		else if (count > bin_attr->size - off) -			count = bin_attr->size - off; -	} -  	return pci_read_vpd(dev, off, count, buf);  } -static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj, -			      struct bin_attribute *bin_attr, char *buf, -			      loff_t off, size_t count) +static ssize_t vpd_write(struct file *filp, struct kobject *kobj, +			 struct bin_attribute *bin_attr, char *buf, loff_t off, +			 size_t count)  {  	struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); -	if (bin_attr->size > 0) { -		if (off > bin_attr->size) -			count = 0; -		else if (count > bin_attr->size - off) -			count = bin_attr->size - off; -	} -  	return pci_write_vpd(dev, off, count, buf);  } +static BIN_ATTR(vpd, 0600, vpd_read, vpd_write, 0); -void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev) -{ -	int retval; -	struct bin_attribute *attr; - -	if (!dev->vpd) -		return; +static struct bin_attribute *vpd_attrs[] = { +	&bin_attr_vpd, +	NULL, +}; -	attr = kzalloc(sizeof(*attr), GFP_ATOMIC); -	if (!attr) -		return; +static umode_t vpd_attr_is_visible(struct kobject *kobj, +				   struct bin_attribute *a, int n) +{ +	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); -	sysfs_bin_attr_init(attr); -	attr->size = 0; -	attr->attr.name = "vpd"; -	attr->attr.mode = S_IRUSR | S_IWUSR; -	attr->read = read_vpd_attr; -	attr->write = write_vpd_attr; -	retval = sysfs_create_bin_file(&dev->dev.kobj, attr); -	if (retval) { -		kfree(attr); -		return; -	} +	if (!pdev->vpd) +		return 0; -	dev->vpd->attr = attr; +	return a->attr.mode;  } -void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev) -{ -	if (dev->vpd && dev->vpd->attr) { -		sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); -		kfree(dev->vpd->attr); -	} -} +const struct attribute_group pci_dev_vpd_attr_group = { +	.bin_attrs = vpd_attrs, +	.is_bin_visible = vpd_attr_is_visible, +}; -int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt) +int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt)  { -	int i; +	int i = 0; -	for (i = off; i < len; ) { -		u8 val = buf[i]; - -		if (val & PCI_VPD_LRDT) { -			/* Don't return success of the tag isn't complete */ -			if (i + PCI_VPD_LRDT_TAG_SIZE > len) -				break; - -			if (val == rdt) -				return i; - -			i += PCI_VPD_LRDT_TAG_SIZE + -			     pci_vpd_lrdt_size(&buf[i]); -		} else { -			u8 tag = val & ~PCI_VPD_SRDT_LEN_MASK; - -			if (tag == rdt) -				return i; - -			if (tag == PCI_VPD_SRDT_END) -				break; +	/* look for LRDT tags only, end tag is the only SRDT tag */ +	while (i + PCI_VPD_LRDT_TAG_SIZE <= len && buf[i] & PCI_VPD_LRDT) { +		if (buf[i] == rdt) +			return i; -			i += PCI_VPD_SRDT_TAG_SIZE + -			     pci_vpd_srdt_size(&buf[i]); -		} +		i += PCI_VPD_LRDT_TAG_SIZE + pci_vpd_lrdt_size(buf + i);  	}  	return -ENOENT; @@ -530,7 +446,7 @@ static void quirk_f0_vpd_link(struct pci_dev *dev)  	if (!PCI_FUNC(dev->devfn))  		return; -	f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); +	f0 = pci_get_func0_dev(dev);  	if (!f0)  		return; @@ -570,7 +486,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);  DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);  DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,  		quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd);  /*   * The Amazon Annapurna Labs 0x0031 device id is reused for other non Root Port   * device types, so the quirk is registered for the PCI_CLASS_BRIDGE_PCI class. @@ -578,51 +493,16 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd);  DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS, 0x0031,  			      PCI_CLASS_BRIDGE_PCI, 8, quirk_blacklist_vpd); -/* - * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the - * VPD end tag will hang the device.  This problem was initially - * observed when a vpd entry was created in sysfs - * ('/sys/bus/pci/devices/<id>/vpd').   A read to this sysfs entry - * will dump 32k of data.  Reading a full 32k will cause an access - * beyond the VPD end tag causing the device to hang.  Once the device - * is hung, the bnx2 driver will not be able to reset the device. - * We believe that it is legal to read beyond the end tag and - * therefore the solution is to limit the read/write length. - */ -static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev) +static void pci_vpd_set_size(struct pci_dev *dev, size_t len)  { -	/* -	 * Only disable the VPD capability for 5706, 5706S, 5708, -	 * 5708S and 5709 rev. A -	 */ -	if ((dev->device == PCI_DEVICE_ID_NX2_5706) || -	    (dev->device == PCI_DEVICE_ID_NX2_5706S) || -	    (dev->device == PCI_DEVICE_ID_NX2_5708) || -	    (dev->device == PCI_DEVICE_ID_NX2_5708S) || -	    ((dev->device == PCI_DEVICE_ID_NX2_5709) && -	     (dev->revision & 0xf0) == 0x0)) { -		if (dev->vpd) -			dev->vpd->len = 0x80; -	} +	struct pci_vpd *vpd = dev->vpd; + +	if (!vpd || len == 0 || len > PCI_VPD_MAX_SIZE) +		return; + +	vpd->valid = 1; +	vpd->len = len;  } -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, -			PCI_DEVICE_ID_NX2_5706, -			quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, -			PCI_DEVICE_ID_NX2_5706S, -			quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, -			PCI_DEVICE_ID_NX2_5708, -			quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, -			PCI_DEVICE_ID_NX2_5708S, -			quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, -			PCI_DEVICE_ID_NX2_5709, -			quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, -			PCI_DEVICE_ID_NX2_5709S, -			quirk_brcm_570x_limit_vpd);  static void quirk_chelsio_extend_vpd(struct pci_dev *dev)  { @@ -642,9 +522,9 @@ static void quirk_chelsio_extend_vpd(struct pci_dev *dev)  	 * limits.  	 */  	if (chip == 0x0 && prod >= 0x20) -		pci_set_vpd_size(dev, 8192); +		pci_vpd_set_size(dev, 8192);  	else if (chip >= 0x4 && func < 0x8) -		pci_set_vpd_size(dev, 2048); +		pci_vpd_set_size(dev, 2048);  }  DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID, | 
