diff options
Diffstat (limited to 'drivers/pci/controller')
56 files changed, 5752 insertions, 1309 deletions
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index fe9f9f13ce11..20bf00f587bd 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -22,34 +22,6 @@ config PCI_AARDVARK controller is part of the South Bridge of the Marvel Armada 3700 SoC. -menu "Cadence PCIe controllers support" - -config PCIE_CADENCE - bool - -config PCIE_CADENCE_HOST - bool "Cadence PCIe host controller" - depends on OF - depends on PCI - select IRQ_DOMAIN - select PCIE_CADENCE - help - Say Y here if you want to support the Cadence PCIe controller in host - mode. This PCIe controller may be embedded into many different vendors - SoCs. - -config PCIE_CADENCE_EP - bool "Cadence PCIe endpoint controller" - depends on OF - depends on PCI_ENDPOINT - select PCIE_CADENCE - help - Say Y here if you want to support the Cadence PCIe controller in - endpoint mode. This PCIe controller may be embedded into many - different vendors SoCs. - -endmenu - config PCIE_XILINX_NWL bool "NWL PCIe Core" depends on ARCH_ZYNQMP || COMPILE_TEST @@ -135,7 +107,7 @@ config PCI_V3_SEMI config PCI_VERSATILE bool "ARM Versatile PB PCI controller" - depends on ARCH_VERSATILE + depends on ARCH_VERSATILE || COMPILE_TEST config PCIE_IPROC tristate @@ -267,7 +239,6 @@ config PCIE_TANGO_SMP8759 config VMD depends on PCI_MSI && X86_64 && SRCU - select X86_DEV_DMA_OPS tristate "Intel Volume Management Device Driver" ---help--- Adds support for the Intel Volume Management Device (VMD). VMD is a @@ -281,5 +252,22 @@ config VMD To compile this driver as a module, choose M here: the module will be called vmd. +config PCIE_BRCMSTB + tristate "Broadcom Brcmstb PCIe host controller" + depends on ARCH_BCM2835 || COMPILE_TEST + depends on OF + depends on PCI_MSI_IRQ_DOMAIN + help + Say Y here to enable PCIe host controller support for + Broadcom STB based SoCs, like the Raspberry Pi 4. + +config PCI_HYPERV_INTERFACE + tristate "Hyper-V PCI Interface" + depends on X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64 + help + The Hyper-V PCI Interface is a helper driver allows other drivers to + have a common interface with the Hyper-V PCI frontend driver. + source "drivers/pci/controller/dwc/Kconfig" +source "drivers/pci/controller/cadence/Kconfig" endmenu diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index d56a507495c5..01b2502a5323 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -1,9 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o -obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o -obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o +obj-$(CONFIG_PCIE_CADENCE) += cadence/ obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o +obj-$(CONFIG_PCI_HYPERV_INTERFACE) += pci-hyperv-intf.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o @@ -29,6 +28,7 @@ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o obj-$(CONFIG_VMD) += vmd.o +obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig new file mode 100644 index 000000000000..b76b3cf55ce5 --- /dev/null +++ b/drivers/pci/controller/cadence/Kconfig @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0 + +menu "Cadence PCIe controllers support" + depends on PCI + +config PCIE_CADENCE + bool + +config PCIE_CADENCE_HOST + bool + depends on OF + select IRQ_DOMAIN + select PCIE_CADENCE + +config PCIE_CADENCE_EP + bool + depends on OF + depends on PCI_ENDPOINT + select PCIE_CADENCE + +config PCIE_CADENCE_PLAT + bool + +config PCIE_CADENCE_PLAT_HOST + bool "Cadence PCIe platform host controller" + depends on OF + select PCIE_CADENCE_HOST + select PCIE_CADENCE_PLAT + help + Say Y here if you want to support the Cadence PCIe platform controller in + host mode. This PCIe controller may be embedded into many different + vendors SoCs. + +config PCIE_CADENCE_PLAT_EP + bool "Cadence PCIe platform endpoint controller" + depends on OF + depends on PCI_ENDPOINT + select PCIE_CADENCE_EP + select PCIE_CADENCE_PLAT + help + Say Y here if you want to support the Cadence PCIe platform controller in + endpoint mode. This PCIe controller may be embedded into many + different vendors SoCs. + +endmenu diff --git a/drivers/pci/controller/cadence/Makefile b/drivers/pci/controller/cadence/Makefile new file mode 100644 index 000000000000..232a3f20876a --- /dev/null +++ b/drivers/pci/controller/cadence/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o +obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o +obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o +obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index def7820cb824..1c173dad67d1 100644 --- a/drivers/pci/controller/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -17,35 +17,6 @@ #define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE 0x1 #define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x3 -/** - * struct cdns_pcie_ep - private data for this PCIe endpoint controller driver - * @pcie: Cadence PCIe controller - * @max_regions: maximum number of regions supported by hardware - * @ob_region_map: bitmask of mapped outbound regions - * @ob_addr: base addresses in the AXI bus where the outbound regions start - * @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ - * dedicated outbound regions is mapped. - * @irq_cpu_addr: base address in the CPU space where a write access triggers - * the sending of a memory write (MSI) / normal message (legacy - * IRQ) TLP through the PCIe bus. - * @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ - * dedicated outbound region. - * @irq_pci_fn: the latest PCI function that has updated the mapping of - * the MSI/legacy IRQ dedicated outbound region. - * @irq_pending: bitmask of asserted legacy IRQs. - */ -struct cdns_pcie_ep { - struct cdns_pcie pcie; - u32 max_regions; - unsigned long ob_region_map; - phys_addr_t *ob_addr; - phys_addr_t irq_phys_addr; - void __iomem *irq_cpu_addr; - u64 irq_pci_addr; - u8 irq_pci_fn; - u8 irq_pending; -}; - static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, struct pci_epf_header *hdr) { @@ -424,28 +395,17 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = { .get_features = cdns_pcie_ep_get_features, }; -static const struct of_device_id cdns_pcie_ep_of_match[] = { - { .compatible = "cdns,cdns-pcie-ep" }, - - { }, -}; -static int cdns_pcie_ep_probe(struct platform_device *pdev) +int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) { - struct device *dev = &pdev->dev; + struct device *dev = ep->pcie.dev; + struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; - struct cdns_pcie_ep *ep; - struct cdns_pcie *pcie; - struct pci_epc *epc; + struct cdns_pcie *pcie = &ep->pcie; struct resource *res; + struct pci_epc *epc; int ret; - int phy_count; - - ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); - if (!ep) - return -ENOMEM; - pcie = &ep->pcie; pcie->is_rc = false; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); @@ -474,19 +434,6 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) if (!ep->ob_addr) return -ENOMEM; - ret = cdns_pcie_init_phy(dev, pcie); - if (ret) { - dev_err(dev, "failed to init phy\n"); - return ret; - } - platform_set_drvdata(pdev, pcie); - pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - dev_err(dev, "pm_runtime_get_sync() failed\n"); - goto err_get_sync; - } - /* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */ cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0)); @@ -528,38 +475,5 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) err_init: pm_runtime_put_sync(dev); - err_get_sync: - pm_runtime_disable(dev); - cdns_pcie_disable_phy(pcie); - phy_count = pcie->phy_count; - while (phy_count--) - device_link_del(pcie->link[phy_count]); - return ret; } - -static void cdns_pcie_ep_shutdown(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct cdns_pcie *pcie = dev_get_drvdata(dev); - int ret; - - ret = pm_runtime_put_sync(dev); - if (ret < 0) - dev_dbg(dev, "pm_runtime_put_sync failed\n"); - - pm_runtime_disable(dev); - - cdns_pcie_disable_phy(pcie); -} - -static struct platform_driver cdns_pcie_ep_driver = { - .driver = { - .name = "cdns-pcie-ep", - .of_match_table = cdns_pcie_ep_of_match, - .pm = &cdns_pcie_pm_ops, - }, - .probe = cdns_pcie_ep_probe, - .shutdown = cdns_pcie_ep_shutdown, -}; -builtin_platform_driver(cdns_pcie_ep_driver); diff --git a/drivers/pci/controller/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 97e251090b4f..9b1c3966414b 100644 --- a/drivers/pci/controller/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -11,33 +11,6 @@ #include "pcie-cadence.h" -/** - * struct cdns_pcie_rc - private data for this PCIe Root Complex driver - * @pcie: Cadence PCIe controller - * @dev: pointer to PCIe device - * @cfg_res: start/end offsets in the physical system memory to map PCI - * configuration space accesses - * @bus_range: first/last buses behind the PCIe host controller - * @cfg_base: IO mapped window to access the PCI configuration space of a - * single function at a time - * @max_regions: maximum number of regions supported by the hardware - * @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address - * translation (nbits sets into the "no BAR match" register) - * @vendor_id: PCI vendor ID - * @device_id: PCI device ID - */ -struct cdns_pcie_rc { - struct cdns_pcie pcie; - struct device *dev; - struct resource *cfg_res; - struct resource *bus_range; - void __iomem *cfg_base; - u32 max_regions; - u32 no_bar_nbits; - u16 vendor_id; - u16 device_id; -}; - static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { @@ -92,11 +65,6 @@ static struct pci_ops cdns_pcie_host_ops = { .write = pci_generic_config_write, }; -static const struct of_device_id cdns_pcie_host_of_match[] = { - { .compatible = "cdns,cdns-pcie-host" }, - - { }, -}; static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { @@ -136,10 +104,10 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) { struct cdns_pcie *pcie = &rc->pcie; - struct resource *cfg_res = rc->cfg_res; struct resource *mem_res = pcie->mem_res; struct resource *bus_range = rc->bus_range; - struct device *dev = rc->dev; + struct resource *cfg_res = rc->cfg_res; + struct device *dev = pcie->dev; struct device_node *np = dev->of_node; struct of_pci_range_parser parser; struct of_pci_range range; @@ -211,7 +179,7 @@ static int cdns_pcie_host_init(struct device *dev, int err; /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); + err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range); if (err) return err; @@ -233,25 +201,21 @@ static int cdns_pcie_host_init(struct device *dev, return err; } -static int cdns_pcie_host_probe(struct platform_device *pdev) +int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) { - struct device *dev = &pdev->dev; + struct device *dev = rc->pcie.dev; + struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; struct pci_host_bridge *bridge; struct list_head resources; - struct cdns_pcie_rc *rc; struct cdns_pcie *pcie; struct resource *res; int ret; - int phy_count; - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); + bridge = pci_host_bridge_from_priv(rc); if (!bridge) return -ENOMEM; - rc = pci_host_bridge_priv(bridge); - rc->dev = dev; - pcie = &rc->pcie; pcie->is_rc = true; @@ -287,21 +251,8 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) dev_err(dev, "missing \"mem\"\n"); return -EINVAL; } - pcie->mem_res = res; - ret = cdns_pcie_init_phy(dev, pcie); - if (ret) { - dev_err(dev, "failed to init phy\n"); - return ret; - } - platform_set_drvdata(pdev, pcie); - - pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - dev_err(dev, "pm_runtime_get_sync() failed\n"); - goto err_get_sync; - } + pcie->mem_res = res; ret = cdns_pcie_host_init(dev, &resources, rc); if (ret) @@ -326,37 +277,5 @@ static int cdns_pcie_host_probe(struct platform_device *pdev) err_init: pm_runtime_put_sync(dev); - err_get_sync: - pm_runtime_disable(dev); - cdns_pcie_disable_phy(pcie); - phy_count = pcie->phy_count; - while (phy_count--) - device_link_del(pcie->link[phy_count]); - return ret; } - -static void cdns_pcie_shutdown(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct cdns_pcie *pcie = dev_get_drvdata(dev); - int ret; - - ret = pm_runtime_put_sync(dev); - if (ret < 0) - dev_dbg(dev, "pm_runtime_put_sync failed\n"); - - pm_runtime_disable(dev); - cdns_pcie_disable_phy(pcie); -} - -static struct platform_driver cdns_pcie_host_driver = { - .driver = { - .name = "cdns-pcie-host", - .of_match_table = cdns_pcie_host_of_match, - .pm = &cdns_pcie_pm_ops, - }, - .probe = cdns_pcie_host_probe, - .shutdown = cdns_pcie_shutdown, -}; -builtin_platform_driver(cdns_pcie_host_driver); diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c new file mode 100644 index 000000000000..f5c6bf6dfcb8 --- /dev/null +++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence PCIe platform driver. + * + * Copyright (c) 2019, Cadence Design Systems + * Author: Tom Joseph <tjoseph@cadence.com> + */ +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/of_device.h> +#include "pcie-cadence.h" + +/** + * struct cdns_plat_pcie - private data for this PCIe platform driver + * @pcie: Cadence PCIe controller + * @is_rc: Set to 1 indicates the PCIe controller mode is Root Complex, + * if 0 it is in Endpoint mode. + */ +struct cdns_plat_pcie { + struct cdns_pcie *pcie; + bool is_rc; +}; + +struct cdns_plat_pcie_of_data { + bool is_rc; +}; + +static const struct of_device_id cdns_plat_pcie_of_match[]; + +static int cdns_plat_pcie_probe(struct platform_device *pdev) +{ + const struct cdns_plat_pcie_of_data *data; + struct cdns_plat_pcie *cdns_plat_pcie; + const struct of_device_id *match; + struct device *dev = &pdev->dev; + struct pci_host_bridge *bridge; + struct cdns_pcie_ep *ep; + struct cdns_pcie_rc *rc; + int phy_count; + bool is_rc; + int ret; + + match = of_match_device(cdns_plat_pcie_of_match, dev); + if (!match) + return -EINVAL; + + data = (struct cdns_plat_pcie_of_data *)match->data; + is_rc = data->is_rc; + + pr_debug(" Started %s with is_rc: %d\n", __func__, is_rc); + cdns_plat_pcie = devm_kzalloc(dev, sizeof(*cdns_plat_pcie), GFP_KERNEL); + if (!cdns_plat_pcie) + return -ENOMEM; + + platform_set_drvdata(pdev, cdns_plat_pcie); + if (is_rc) { + if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_HOST)) + return -ENODEV; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); + if (!bridge) + return -ENOMEM; + + rc = pci_host_bridge_priv(bridge); + rc->pcie.dev = dev; + cdns_plat_pcie->pcie = &rc->pcie; + cdns_plat_pcie->is_rc = is_rc; + + ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie); + if (ret) { + dev_err(dev, "failed to init phy\n"); + return ret; + } + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync() failed\n"); + goto err_get_sync; + } + + ret = cdns_pcie_host_setup(rc); + if (ret) + goto err_init; + } else { + if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_EP)) + return -ENODEV; + + ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + ep->pcie.dev = dev; + cdns_plat_pcie->pcie = &ep->pcie; + cdns_plat_pcie->is_rc = is_rc; + + ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie); + if (ret) { + dev_err(dev, "failed to init phy\n"); + return ret; + } + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync() failed\n"); + goto err_get_sync; + } + + ret = cdns_pcie_ep_setup(ep); + if (ret) + goto err_init; + } + + err_init: + pm_runtime_put_sync(dev); + + err_get_sync: + pm_runtime_disable(dev); + cdns_pcie_disable_phy(cdns_plat_pcie->pcie); + phy_count = cdns_plat_pcie->pcie->phy_count; + while (phy_count--) + device_link_del(cdns_plat_pcie->pcie->link[phy_count]); + + return 0; +} + +static void cdns_plat_pcie_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cdns_pcie *pcie = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_put_sync(dev); + if (ret < 0) + dev_dbg(dev, "pm_runtime_put_sync failed\n"); + + pm_runtime_disable(dev); + + cdns_pcie_disable_phy(pcie); +} + +static const struct cdns_plat_pcie_of_data cdns_plat_pcie_host_of_data = { + .is_rc = true, +}; + +static const struct cdns_plat_pcie_of_data cdns_plat_pcie_ep_of_data = { + .is_rc = false, +}; + +static const struct of_device_id cdns_plat_pcie_of_match[] = { + { + .compatible = "cdns,cdns-pcie-host", + .data = &cdns_plat_pcie_host_of_data, + }, + { + .compatible = "cdns,cdns-pcie-ep", + .data = &cdns_plat_pcie_ep_of_data, + }, + {}, +}; + +static struct platform_driver cdns_plat_pcie_driver = { + .driver = { + .name = "cdns-pcie", + .of_match_table = cdns_plat_pcie_of_match, + .pm = &cdns_pcie_pm_ops, + }, + .probe = cdns_plat_pcie_probe, + .shutdown = cdns_plat_pcie_shutdown, +}; +builtin_platform_driver(cdns_plat_pcie_driver); diff --git a/drivers/pci/controller/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c index cd795f6fc1e2..cd795f6fc1e2 100644 --- a/drivers/pci/controller/pcie-cadence.c +++ b/drivers/pci/controller/cadence/pcie-cadence.c diff --git a/drivers/pci/controller/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index ae6bf2a2b3d3..a2b28b912ca4 100644 --- a/drivers/pci/controller/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ // Copyright (c) 2017 Cadence // Cadence PCIe controller driver. // Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com> @@ -190,6 +190,8 @@ enum cdns_pcie_rp_bar { (((code) << 8) & CDNS_PCIE_NORMAL_MSG_CODE_MASK) #define CDNS_PCIE_MSG_NO_DATA BIT(16) +struct cdns_pcie; + enum cdns_pcie_msg_code { MSG_CODE_ASSERT_INTA = 0x20, MSG_CODE_ASSERT_INTB = 0x21, @@ -231,13 +233,71 @@ enum cdns_pcie_msg_routing { struct cdns_pcie { void __iomem *reg_base; struct resource *mem_res; + struct device *dev; bool is_rc; u8 bus; int phy_count; struct phy **phy; struct device_link **link; + const struct cdns_pcie_common_ops *ops; +}; + +/** + * struct cdns_pcie_rc - private data for this PCIe Root Complex driver + * @pcie: Cadence PCIe controller + * @dev: pointer to PCIe device + * @cfg_res: start/end offsets in the physical system memory to map PCI + * configuration space accesses + * @bus_range: first/last buses behind the PCIe host controller + * @cfg_base: IO mapped window to access the PCI configuration space of a + * single function at a time + * @max_regions: maximum number of regions supported by the hardware + * @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address + * translation (nbits sets into the "no BAR match" register) + * @vendor_id: PCI vendor ID + * @device_id: PCI device ID + */ +struct cdns_pcie_rc { + struct cdns_pcie pcie; + struct resource *cfg_res; + struct resource *bus_range; + void __iomem *cfg_base; + u32 max_regions; + u32 no_bar_nbits; + u16 vendor_id; + u16 device_id; }; +/** + * struct cdns_pcie_ep - private data for this PCIe endpoint controller driver + * @pcie: Cadence PCIe controller + * @max_regions: maximum number of regions supported by hardware + * @ob_region_map: bitmask of mapped outbound regions + * @ob_addr: base addresses in the AXI bus where the outbound regions start + * @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ + * dedicated outbound regions is mapped. + * @irq_cpu_addr: base address in the CPU space where a write access triggers + * the sending of a memory write (MSI) / normal message (legacy + * IRQ) TLP through the PCIe bus. + * @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ + * dedicated outbound region. + * @irq_pci_fn: the latest PCI function that has updated the mapping of + * the MSI/legacy IRQ dedicated outbound region. + * @irq_pending: bitmask of asserted legacy IRQs. + */ +struct cdns_pcie_ep { + struct cdns_pcie pcie; + u32 max_regions; + unsigned long ob_region_map; + phys_addr_t *ob_addr; + phys_addr_t irq_phys_addr; + void __iomem *irq_cpu_addr; + u64 irq_pci_addr; + u8 irq_pci_fn; + u8 irq_pending; +}; + + /* Register access */ static inline void cdns_pcie_writeb(struct cdns_pcie *pcie, u32 reg, u8 value) { @@ -306,6 +366,23 @@ static inline u32 cdns_pcie_ep_fn_readl(struct cdns_pcie *pcie, u8 fn, u32 reg) return readl(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); } +#ifdef CONFIG_PCIE_CADENCE_HOST +int cdns_pcie_host_setup(struct cdns_pcie_rc *rc); +#else +static inline int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) +{ + return 0; +} +#endif + +#ifdef CONFIG_PCIE_CADENCE_EP +int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep); +#else +static inline int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) +{ + return 0; +} +#endif void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, u32 r, bool is_io, u64 cpu_addr, u64 pci_addr, size_t size); diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 6ea778ae4877..0830dfcfa43a 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -7,9 +7,9 @@ config PCIE_DW bool config PCIE_DW_HOST - bool + bool depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW + select PCIE_DW config PCIE_DW_EP bool @@ -131,13 +131,29 @@ config PCI_KEYSTONE_EP DesignWare core functions to implement the driver. config PCI_LAYERSCAPE - bool "Freescale Layerscape PCIe controller" + bool "Freescale Layerscape PCIe controller - Host mode" depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST) depends on PCI_MSI_IRQ_DOMAIN select MFD_SYSCON select PCIE_DW_HOST help - Say Y here if you want PCIe controller support on Layerscape SoCs. + Say Y here if you want to enable PCIe controller support on Layerscape + SoCs to work in Host mode. + This controller can work either as EP or RC. The RCW[HOST_AGT_PEX] + determines which PCIe controller works in EP mode and which PCIe + controller works in RC mode. + +config PCI_LAYERSCAPE_EP + bool "Freescale Layerscape PCIe controller - Endpoint mode" + depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST) + depends on PCI_ENDPOINT + select PCIE_DW_EP + help + Say Y here if you want to enable PCIe controller support on Layerscape + SoCs to work in Endpoint mode. + This controller can work either as EP or RC. The RCW[HOST_AGT_PEX] + determines which PCIe controller works in EP mode and which PCIe + controller works in RC mode. config PCI_HISI depends on OF && (ARM64 || COMPILE_TEST) @@ -193,6 +209,17 @@ config PCIE_ARTPEC6_EP Enables support for the PCIe controller in the ARTPEC-6 SoC to work in endpoint mode. This uses the DesignWare core. +config PCIE_INTEL_GW + bool "Intel Gateway PCIe host controller support" + depends on OF && (X86 || COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say 'Y' here to enable PCIe Host controller support on Intel + Gateway SoCs. + The PCIe controller uses the DesignWare core plus Intel-specific + hardware wrappers. + config PCIE_KIRIN depends on OF && (ARM64 || COMPILE_TEST) bool "HiSilicon Kirin series SoCs PCIe controllers" @@ -208,7 +235,7 @@ config PCIE_HISI_STB depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST help - Say Y here if you want PCIe controller support on HiSilicon STB SoCs + Say Y here if you want PCIe controller support on HiSilicon STB SoCs config PCI_MESON bool "MESON PCIe controller" @@ -220,6 +247,16 @@ config PCI_MESON and therefore the driver re-uses the DesignWare core functions to implement the driver. +config PCIE_TEGRA194 + tristate "NVIDIA Tegra194 (and later) PCIe controller" + depends on ARCH_TEGRA_194_SOC || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + select PHY_TEGRA194_P2U + help + Say Y here if you want support for DesignWare core based PCIe host + controller found in NVIDIA Tegra194 SoC. + config PCIE_UNIPHIER bool "Socionext UniPhier PCIe controllers" depends on ARCH_UNIPHIER || COMPILE_TEST @@ -230,4 +267,16 @@ config PCIE_UNIPHIER Say Y here if you want PCIe controller support on UniPhier SoCs. This driver supports LD20 and PXs3 SoCs. +config PCIE_AL + bool "Amazon Annapurna Labs PCIe controller" + depends on OF && (ARM64 || COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + 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 + core plus Annapurna Labs proprietary hardware wrappers. This is + required only for DT-based platforms. ACPI platforms with the + Annapurna Labs PCIe controller don't need to enable this. + endmenu diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index b085dfd4fab7..8a637cfcf6e9 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -8,13 +8,16 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o -obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o +obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o +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 # The following drivers are for devices that use the generic ACPI diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 4234ddb4722f..9bf7fa99b103 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -353,7 +353,7 @@ static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); enum pci_barno bar; - for (bar = BAR_0; bar <= BAR_5; bar++) + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) dw_pcie_ep_reset_bar(pci, bar); dra7xx_pcie_enable_wrapper_interrupts(dra7xx); @@ -719,7 +719,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf"); - base = devm_ioremap_nocache(dev, res->start, resource_size(res)); + base = devm_ioremap(dev, res->start, resource_size(res)); if (!base) return -ENOMEM; diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index cee5f2f590e2..c5043d951e80 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PCIe host controller driver for Samsung EXYNOS SoCs + * PCIe host controller driver for Samsung Exynos SoCs * * Copyright (C) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com @@ -465,7 +465,7 @@ static int __init exynos_pcie_probe(struct platform_device *pdev) ep->phy = devm_of_phy_get(dev, np, NULL); if (IS_ERR(ep->phy)) { - if (PTR_ERR(ep->phy) == -EPROBE_DEFER) + if (PTR_ERR(ep->phy) != -ENODEV) return PTR_ERR(ep->phy); ep->phy = NULL; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 9b5cb5b70389..acfbd34032a8 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -57,6 +57,7 @@ enum imx6_pcie_variants { struct imx6_pcie_drvdata { enum imx6_pcie_variants variant; u32 flags; + int dbi_length; }; struct imx6_pcie { @@ -1173,8 +1174,8 @@ static int imx6_pcie_probe(struct platform_device *pdev) imx6_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie"); if (IS_ERR(imx6_pcie->vpcie)) { - if (PTR_ERR(imx6_pcie->vpcie) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (PTR_ERR(imx6_pcie->vpcie) != -ENODEV) + return PTR_ERR(imx6_pcie->vpcie); imx6_pcie->vpcie = NULL; } @@ -1212,6 +1213,7 @@ static const struct imx6_pcie_drvdata drvdata[] = { .variant = IMX6Q, .flags = IMX6_PCIE_FLAG_IMX6_PHY | IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, + .dbi_length = 0x200, }, [IMX6SX] = { .variant = IMX6SX, @@ -1254,6 +1256,37 @@ static struct platform_driver imx6_pcie_driver = { .shutdown = imx6_pcie_shutdown, }; +static void imx6_pcie_quirk(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + struct pcie_port *pp = bus->sysdata; + + /* Bus parent is the PCI bridge, its parent is this platform driver */ + if (!bus->dev.parent || !bus->dev.parent->parent) + return; + + /* Make sure we only quirk devices associated with this driver */ + if (bus->dev.parent->parent->driver != &imx6_pcie_driver.driver) + return; + + if (bus->number == pp->root_bus_nr) { + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); + + /* + * Limit config length to avoid the kernel reading beyond + * the register set and causing an abort on i.MX 6Quad + */ + if (imx6_pcie->drvdata->dbi_length) { + dev->cfg_size = imx6_pcie->drvdata->dbi_length; + dev_info(&dev->dev, "Limiting cfg_size to %d\n", + dev->cfg_size); + } + } +} +DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_SYNOPSYS, 0xabcd, + PCI_CLASS_BRIDGE_PCI, 8, imx6_pcie_quirk); + static int __init imx6_pcie_init(void) { #ifdef CONFIG_ARM diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index af677254a072..c8c702c494a2 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -422,7 +422,7 @@ static void ks_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) lower_32_bits(start) | OB_ENABLEN); ks_pcie_app_writel(ks_pcie, OB_OFFSET_HI(i), upper_32_bits(start)); - start += OB_WIN_SIZE; + start += OB_WIN_SIZE * SZ_1M; } val = ks_pcie_app_readl(ks_pcie, CMD_STATUS); @@ -510,7 +510,7 @@ static void ks_pcie_stop_link(struct dw_pcie *pci) /* Disable Link training */ val = ks_pcie_app_readl(ks_pcie, CMD_STATUS); val &= ~LTSSM_EN_VAL; - ks_pcie_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); + ks_pcie_app_writel(ks_pcie, CMD_STATUS, val); } static int ks_pcie_start_link(struct dw_pcie *pci) @@ -1354,7 +1354,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev) ret = of_property_read_u32(np, "num-viewport", &num_viewport); if (ret < 0) { dev_err(dev, "unable to read *num-viewport* property\n"); - return ret; + goto err_get_sync; } /* diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index be61d96cc95e..0d151cead1b7 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -44,6 +44,7 @@ static const struct pci_epc_features ls_pcie_epc_features = { .linkup_notifier = false, .msi_capable = true, .msix_capable = false, + .bar_fixed_64bit = (1 << BAR_2) | (1 << BAR_4), }; static const struct pci_epc_features* @@ -57,7 +58,7 @@ static void ls_pcie_ep_init(struct dw_pcie_ep *ep) struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar; - for (bar = BAR_0; bar <= BAR_5; bar++) + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) dw_pcie_ep_reset_bar(pci, bar); } diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index 3a5fa26d5e56..f24f79a70d9a 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -263,6 +263,7 @@ static const struct ls_pcie_drvdata ls2088_drvdata = { static const struct of_device_id ls_pcie_of_match[] = { { .compatible = "fsl,ls1012a-pcie", .data = &ls1046_drvdata }, { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata }, + { .compatible = "fsl,ls1028a-pcie", .data = &ls2088_drvdata }, { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata }, { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata }, { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata }, diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index e35e9eaa50ee..3772b02a5c55 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -16,6 +16,7 @@ #include <linux/reset.h> #include <linux/resource.h> #include <linux/types.h> +#include <linux/phy/phy.h> #include "pcie-designware.h" @@ -96,12 +97,18 @@ struct meson_pcie_rc_reset { struct reset_control *apb; }; +struct meson_pcie_param { + bool has_shared_phy; +}; + struct meson_pcie { struct dw_pcie pci; struct meson_pcie_mem_res mem_res; struct meson_pcie_clk_res clk_res; struct meson_pcie_rc_reset mrst; struct gpio_desc *reset_gpio; + struct phy *phy; + const struct meson_pcie_param *param; }; static struct reset_control *meson_pcie_get_reset(struct meson_pcie *mp, @@ -123,10 +130,12 @@ static int meson_pcie_get_resets(struct meson_pcie *mp) { struct meson_pcie_rc_reset *mrst = &mp->mrst; - mrst->phy = meson_pcie_get_reset(mp, "phy", PCIE_SHARED_RESET); - if (IS_ERR(mrst->phy)) - return PTR_ERR(mrst->phy); - reset_control_deassert(mrst->phy); + if (!mp->param->has_shared_phy) { + mrst->phy = meson_pcie_get_reset(mp, "phy", PCIE_SHARED_RESET); + if (IS_ERR(mrst->phy)) + return PTR_ERR(mrst->phy); + reset_control_deassert(mrst->phy); + } mrst->port = meson_pcie_get_reset(mp, "port", PCIE_NORMAL_RESET); if (IS_ERR(mrst->port)) @@ -180,27 +189,52 @@ static int meson_pcie_get_mems(struct platform_device *pdev, if (IS_ERR(mp->mem_res.cfg_base)) return PTR_ERR(mp->mem_res.cfg_base); - /* Meson SoC has two PCI controllers use same phy register*/ - mp->mem_res.phy_base = meson_pcie_get_mem_shared(pdev, mp, "phy"); - if (IS_ERR(mp->mem_res.phy_base)) - return PTR_ERR(mp->mem_res.phy_base); + /* Meson AXG SoC has two PCI controllers use same phy register */ + if (!mp->param->has_shared_phy) { + mp->mem_res.phy_base = + meson_pcie_get_mem_shared(pdev, mp, "phy"); + if (IS_ERR(mp->mem_res.phy_base)) + return PTR_ERR(mp->mem_res.phy_base); + } return 0; } -static void meson_pcie_power_on(struct meson_pcie *mp) +static int meson_pcie_power_on(struct meson_pcie *mp) { - writel(MESON_PCIE_PHY_POWERUP, mp->mem_res.phy_base); + int ret = 0; + + if (mp->param->has_shared_phy) { + ret = phy_init(mp->phy); + if (ret) + return ret; + + ret = phy_power_on(mp->phy); + if (ret) { + phy_exit(mp->phy); + return ret; + } + } else + writel(MESON_PCIE_PHY_POWERUP, mp->mem_res.phy_base); + + return 0; } -static void meson_pcie_reset(struct meson_pcie *mp) +static int meson_pcie_reset(struct meson_pcie *mp) { struct meson_pcie_rc_reset *mrst = &mp->mrst; - - reset_control_assert(mrst->phy); - udelay(PCIE_RESET_DELAY); - reset_control_deassert(mrst->phy); - udelay(PCIE_RESET_DELAY); + int ret = 0; + + if (mp->param->has_shared_phy) { + ret = phy_reset(mp->phy); + if (ret) + return ret; + } else { + reset_control_assert(mrst->phy); + udelay(PCIE_RESET_DELAY); + reset_control_deassert(mrst->phy); + udelay(PCIE_RESET_DELAY); + } reset_control_assert(mrst->port); reset_control_assert(mrst->apb); @@ -208,6 +242,8 @@ static void meson_pcie_reset(struct meson_pcie *mp) reset_control_deassert(mrst->port); reset_control_deassert(mrst->apb); udelay(PCIE_RESET_DELAY); + + return 0; } static inline struct clk *meson_pcie_probe_clock(struct device *dev, @@ -250,15 +286,17 @@ static int meson_pcie_probe_clocks(struct meson_pcie *mp) if (IS_ERR(res->port_clk)) return PTR_ERR(res->port_clk); - res->mipi_gate = meson_pcie_probe_clock(dev, "pcie_mipi_en", 0); - if (IS_ERR(res->mipi_gate)) - return PTR_ERR(res->mipi_gate); + if (!mp->param->has_shared_phy) { + res->mipi_gate = meson_pcie_probe_clock(dev, "mipi", 0); + if (IS_ERR(res->mipi_gate)) + return PTR_ERR(res->mipi_gate); + } - res->general_clk = meson_pcie_probe_clock(dev, "pcie_general", 0); + res->general_clk = meson_pcie_probe_clock(dev, "general", 0); if (IS_ERR(res->general_clk)) return PTR_ERR(res->general_clk); - res->clk = meson_pcie_probe_clock(dev, "pcie", 0); + res->clk = meson_pcie_probe_clock(dev, "pclk", 0); if (IS_ERR(res->clk)) return PTR_ERR(res->clk); @@ -287,9 +325,9 @@ static inline void meson_cfg_writel(struct meson_pcie *mp, u32 val, u32 reg) static void meson_pcie_assert_reset(struct meson_pcie *mp) { - gpiod_set_value_cansleep(mp->reset_gpio, 0); - udelay(500); gpiod_set_value_cansleep(mp->reset_gpio, 1); + udelay(500); + gpiod_set_value_cansleep(mp->reset_gpio, 0); } static void meson_pcie_init_dw(struct meson_pcie *mp) @@ -524,6 +562,7 @@ static const struct dw_pcie_ops dw_pcie_ops = { static int meson_pcie_probe(struct platform_device *pdev) { + const struct meson_pcie_param *match_data; struct device *dev = &pdev->dev; struct dw_pcie *pci; struct meson_pcie *mp; @@ -537,6 +576,19 @@ static int meson_pcie_probe(struct platform_device *pdev) pci->dev = dev; pci->ops = &dw_pcie_ops; + match_data = of_device_get_match_data(dev); + if (!match_data) { + dev_err(dev, "failed to get match data\n"); + return -ENODEV; + } + mp->param = match_data; + + if (mp->param->has_shared_phy) { + mp->phy = devm_phy_get(dev, "pcie"); + if (IS_ERR(mp->phy)) + return PTR_ERR(mp->phy); + } + mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(mp->reset_gpio)) { dev_err(dev, "get reset gpio failed\n"); @@ -555,13 +607,22 @@ static int meson_pcie_probe(struct platform_device *pdev) return ret; } - meson_pcie_power_on(mp); - meson_pcie_reset(mp); + ret = meson_pcie_power_on(mp); + if (ret) { + dev_err(dev, "phy power on failed, %d\n", ret); + return ret; + } + + ret = meson_pcie_reset(mp); + if (ret) { + dev_err(dev, "reset failed, %d\n", ret); + goto err_phy; + } ret = meson_pcie_probe_clocks(mp); if (ret) { dev_err(dev, "init clock resources failed, %d\n", ret); - return ret; + goto err_phy; } platform_set_drvdata(pdev, mp); @@ -569,15 +630,36 @@ static int meson_pcie_probe(struct platform_device *pdev) ret = meson_add_pcie_port(mp, pdev); if (ret < 0) { dev_err(dev, "Add PCIe port failed, %d\n", ret); - return ret; + goto err_phy; } return 0; + +err_phy: + if (mp->param->has_shared_phy) { + phy_power_off(mp->phy); + phy_exit(mp->phy); + } + + return ret; } +static struct meson_pcie_param meson_pcie_axg_param = { + .has_shared_phy = false, +}; + +static struct meson_pcie_param meson_pcie_g12a_param = { + .has_shared_phy = true, +}; + static const struct of_device_id meson_pcie_of_match[] = { { .compatible = "amlogic,axg-pcie", + .data = &meson_pcie_axg_param, + }, + { + .compatible = "amlogic,g12a-pcie", + .data = &meson_pcie_g12a_param, }, {}, }; diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index 3ab58f0584a8..1eeda2f6371f 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -91,3 +91,368 @@ struct pci_ecam_ops al_pcie_ops = { }; #endif /* defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) */ + +#ifdef CONFIG_PCIE_AL + +#include <linux/of_pci.h> +#include "pcie-designware.h" + +#define AL_PCIE_REV_ID_2 2 +#define AL_PCIE_REV_ID_3 3 +#define AL_PCIE_REV_ID_4 4 + +#define AXI_BASE_OFFSET 0x0 + +#define DEVICE_ID_OFFSET 0x16c + +#define DEVICE_REV_ID 0x0 +#define DEVICE_REV_ID_DEV_ID_MASK GENMASK(31, 16) + +#define DEVICE_REV_ID_DEV_ID_X4 0 +#define DEVICE_REV_ID_DEV_ID_X8 2 +#define DEVICE_REV_ID_DEV_ID_X16 4 + +#define OB_CTRL_REV1_2_OFFSET 0x0040 +#define OB_CTRL_REV3_5_OFFSET 0x0030 + +#define CFG_TARGET_BUS 0x0 +#define CFG_TARGET_BUS_MASK_MASK GENMASK(7, 0) +#define CFG_TARGET_BUS_BUSNUM_MASK GENMASK(15, 8) + +#define CFG_CONTROL 0x4 +#define CFG_CONTROL_SUBBUS_MASK GENMASK(15, 8) +#define CFG_CONTROL_SEC_BUS_MASK GENMASK(23, 16) + +struct al_pcie_reg_offsets { + unsigned int ob_ctrl; +}; + +struct al_pcie_target_bus_cfg { + u8 reg_val; + u8 reg_mask; + u8 ecam_mask; +}; + +struct al_pcie { + struct dw_pcie *pci; + void __iomem *controller_base; /* base of PCIe unit (not DW core) */ + struct device *dev; + resource_size_t ecam_size; + unsigned int controller_rev_id; + struct al_pcie_reg_offsets reg_offsets; + struct al_pcie_target_bus_cfg target_bus_cfg; +}; + +#define PCIE_ECAM_DEVFN(x) (((x) & 0xff) << 12) + +#define to_al_pcie(x) dev_get_drvdata((x)->dev) + +static inline u32 al_pcie_controller_readl(struct al_pcie *pcie, u32 offset) +{ + return readl_relaxed(pcie->controller_base + offset); +} + +static inline void al_pcie_controller_writel(struct al_pcie *pcie, u32 offset, + u32 val) +{ + writel_relaxed(val, pcie->controller_base + offset); +} + +static int al_pcie_rev_id_get(struct al_pcie *pcie, unsigned int *rev_id) +{ + u32 dev_rev_id_val; + u32 dev_id_val; + + dev_rev_id_val = al_pcie_controller_readl(pcie, AXI_BASE_OFFSET + + DEVICE_ID_OFFSET + + DEVICE_REV_ID); + dev_id_val = FIELD_GET(DEVICE_REV_ID_DEV_ID_MASK, dev_rev_id_val); + + switch (dev_id_val) { + case DEVICE_REV_ID_DEV_ID_X4: + *rev_id = AL_PCIE_REV_ID_2; + break; + case DEVICE_REV_ID_DEV_ID_X8: + *rev_id = AL_PCIE_REV_ID_3; + break; + case DEVICE_REV_ID_DEV_ID_X16: + *rev_id = AL_PCIE_REV_ID_4; + break; + default: + dev_err(pcie->dev, "Unsupported dev_id_val (0x%x)\n", + dev_id_val); + return -EINVAL; + } + + dev_dbg(pcie->dev, "dev_id_val: 0x%x\n", dev_id_val); + + return 0; +} + +static int al_pcie_reg_offsets_set(struct al_pcie *pcie) +{ + switch (pcie->controller_rev_id) { + case AL_PCIE_REV_ID_2: + pcie->reg_offsets.ob_ctrl = OB_CTRL_REV1_2_OFFSET; + break; + case AL_PCIE_REV_ID_3: + case AL_PCIE_REV_ID_4: + pcie->reg_offsets.ob_ctrl = OB_CTRL_REV3_5_OFFSET; + break; + default: + dev_err(pcie->dev, "Unsupported controller rev_id: 0x%x\n", + pcie->controller_rev_id); + return -EINVAL; + } + + return 0; +} + +static inline void al_pcie_target_bus_set(struct al_pcie *pcie, + u8 target_bus, + u8 mask_target_bus) +{ + u32 reg; + + reg = FIELD_PREP(CFG_TARGET_BUS_MASK_MASK, mask_target_bus) | + FIELD_PREP(CFG_TARGET_BUS_BUSNUM_MASK, target_bus); + + al_pcie_controller_writel(pcie, AXI_BASE_OFFSET + + pcie->reg_offsets.ob_ctrl + CFG_TARGET_BUS, + reg); +} + +static void __iomem *al_pcie_conf_addr_map(struct al_pcie *pcie, + unsigned int busnr, + unsigned int devfn) +{ + struct al_pcie_target_bus_cfg *target_bus_cfg = &pcie->target_bus_cfg; + unsigned int busnr_ecam = busnr & target_bus_cfg->ecam_mask; + unsigned int busnr_reg = busnr & target_bus_cfg->reg_mask; + struct pcie_port *pp = &pcie->pci->pp; + void __iomem *pci_base_addr; + + pci_base_addr = (void __iomem *)((uintptr_t)pp->va_cfg0_base + + (busnr_ecam << 20) + + PCIE_ECAM_DEVFN(devfn)); + + if (busnr_reg != target_bus_cfg->reg_val) { + dev_dbg(pcie->pci->dev, "Changing target bus busnum val from 0x%x to 0x%x\n", + target_bus_cfg->reg_val, busnr_reg); + target_bus_cfg->reg_val = busnr_reg; + al_pcie_target_bus_set(pcie, + target_bus_cfg->reg_val, + target_bus_cfg->reg_mask); + } + + return pci_base_addr; +} + +static int al_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, + u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct al_pcie *pcie = to_al_pcie(pci); + unsigned int busnr = bus->number; + void __iomem *pci_addr; + int rc; + + pci_addr = al_pcie_conf_addr_map(pcie, busnr, devfn); + + rc = dw_pcie_read(pci_addr + where, size, val); + + dev_dbg(pci->dev, "%d-byte config read from %04x:%02x:%02x.%d offset 0x%x (pci_addr: 0x%px) - val:0x%x\n", + size, pci_domain_nr(bus), bus->number, + PCI_SLOT(devfn), PCI_FUNC(devfn), where, + (pci_addr + where), *val); + + return rc; +} + +static int al_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, + u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct al_pcie *pcie = to_al_pcie(pci); + unsigned int busnr = bus->number; + void __iomem *pci_addr; + int rc; + + pci_addr = al_pcie_conf_addr_map(pcie, busnr, devfn); + + rc = dw_pcie_write(pci_addr + where, size, val); + + dev_dbg(pci->dev, "%d-byte config write to %04x:%02x:%02x.%d offset 0x%x (pci_addr: 0x%px) - val:0x%x\n", + size, pci_domain_nr(bus), bus->number, + PCI_SLOT(devfn), PCI_FUNC(devfn), where, + (pci_addr + where), val); + + return rc; +} + +static void al_pcie_config_prepare(struct al_pcie *pcie) +{ + struct al_pcie_target_bus_cfg *target_bus_cfg; + struct pcie_port *pp = &pcie->pci->pp; + unsigned int ecam_bus_mask; + u32 cfg_control_offset; + u8 subordinate_bus; + u8 secondary_bus; + u32 cfg_control; + u32 reg; + + target_bus_cfg = &pcie->target_bus_cfg; + + ecam_bus_mask = (pcie->ecam_size >> 20) - 1; + if (ecam_bus_mask > 255) { + dev_warn(pcie->dev, "ECAM window size is larger than 256MB. Cutting off at 256\n"); + ecam_bus_mask = 255; + } + + /* This portion is taken from the transaction address */ + target_bus_cfg->ecam_mask = ecam_bus_mask; + /* This portion is taken from the cfg_target_bus reg */ + target_bus_cfg->reg_mask = ~target_bus_cfg->ecam_mask; + target_bus_cfg->reg_val = pp->busn->start & target_bus_cfg->reg_mask; + + al_pcie_target_bus_set(pcie, target_bus_cfg->reg_val, + target_bus_cfg->reg_mask); + + secondary_bus = pp->busn->start + 1; + subordinate_bus = pp->busn->end; + + /* Set the valid values of secondary and subordinate buses */ + cfg_control_offset = AXI_BASE_OFFSET + pcie->reg_offsets.ob_ctrl + + CFG_CONTROL; + + cfg_control = al_pcie_controller_readl(pcie, cfg_control_offset); + + reg = cfg_control & + ~(CFG_CONTROL_SEC_BUS_MASK | CFG_CONTROL_SUBBUS_MASK); + + reg |= FIELD_PREP(CFG_CONTROL_SUBBUS_MASK, subordinate_bus) | + FIELD_PREP(CFG_CONTROL_SEC_BUS_MASK, secondary_bus); + + al_pcie_controller_writel(pcie, cfg_control_offset, reg); +} + +static int al_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct al_pcie *pcie = to_al_pcie(pci); + int rc; + + rc = al_pcie_rev_id_get(pcie, &pcie->controller_rev_id); + if (rc) + return rc; + + rc = al_pcie_reg_offsets_set(pcie); + if (rc) + return rc; + + al_pcie_config_prepare(pcie); + + return 0; +} + +static const struct dw_pcie_host_ops al_pcie_host_ops = { + .rd_other_conf = al_pcie_rd_other_conf, + .wr_other_conf = al_pcie_wr_other_conf, + .host_init = al_pcie_host_init, +}; + +static int al_add_pcie_port(struct pcie_port *pp, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + pp->ops = &al_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { +}; + +static int al_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *controller_res; + struct resource *ecam_res; + struct resource *dbi_res; + struct al_pcie *al_pcie; + struct dw_pcie *pci; + + al_pcie = devm_kzalloc(dev, sizeof(*al_pcie), GFP_KERNEL); + if (!al_pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + al_pcie->pci = pci; + al_pcie->dev = dev; + + dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_res); + if (IS_ERR(pci->dbi_base)) { + dev_err(dev, "couldn't remap dbi base %pR\n", dbi_res); + return PTR_ERR(pci->dbi_base); + } + + ecam_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (!ecam_res) { + dev_err(dev, "couldn't find 'config' reg in DT\n"); + return -ENOENT; + } + al_pcie->ecam_size = resource_size(ecam_res); + + controller_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "controller"); + al_pcie->controller_base = devm_ioremap_resource(dev, controller_res); + if (IS_ERR(al_pcie->controller_base)) { + dev_err(dev, "couldn't remap controller base %pR\n", + controller_res); + return PTR_ERR(al_pcie->controller_base); + } + + dev_dbg(dev, "From DT: dbi_base: %pR, controller_base: %pR\n", + dbi_res, controller_res); + + platform_set_drvdata(pdev, al_pcie); + + return al_add_pcie_port(&pci->pp, pdev); +} + +static const struct of_device_id al_pcie_of_match[] = { + { .compatible = "amazon,al-alpine-v2-pcie", + }, + { .compatible = "amazon,al-alpine-v3-pcie", + }, + {}, +}; + +static struct platform_driver al_pcie_driver = { + .driver = { + .name = "al-pcie", + .of_match_table = al_pcie_of_match, + .suppress_bind_attrs = true, + }, + .probe = al_pcie_probe, +}; +builtin_platform_driver(al_pcie_driver); + +#endif /* CONFIG_PCIE_AL*/ diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 3d55dc78d999..49596547e8c2 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -118,11 +118,10 @@ static int armada8k_pcie_setup_phys(struct armada8k_pcie *pcie) for (i = 0; i < ARMADA8K_PCIE_MAX_LANES; i++) { pcie->phy[i] = devm_of_phy_get_by_index(dev, node, i); - if (IS_ERR(pcie->phy[i]) && - (PTR_ERR(pcie->phy[i]) == -EPROBE_DEFER)) - return PTR_ERR(pcie->phy[i]); - if (IS_ERR(pcie->phy[i])) { + if (PTR_ERR(pcie->phy[i]) != -ENODEV) + return PTR_ERR(pcie->phy[i]); + pcie->phy[i] = NULL; continue; } diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index d00252bd8fae..28d5a1095200 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -51,9 +51,6 @@ static const struct of_device_id artpec6_pcie_of_match[]; #define ACK_N_FTS_MASK GENMASK(15, 8) #define ACK_N_FTS(x) (((x) << 8) & ACK_N_FTS_MASK) -#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0) -#define FAST_TRAINING_SEQ(x) (((x) << 0) & FAST_TRAINING_SEQ_MASK) - /* ARTPEC-6 specific registers */ #define PCIECFG 0x18 #define PCIECFG_DBG_OEN BIT(24) @@ -313,10 +310,7 @@ static void artpec6_pcie_set_nfts(struct artpec6_pcie *artpec6_pcie) * Set the Number of Fast Training Sequences that the core * advertises as its N_FTS during Gen2 or Gen3 link training. */ - val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - val &= ~FAST_TRAINING_SEQ_MASK; - val |= FAST_TRAINING_SEQ(180); - dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + dw_pcie_link_set_n_fts(pci, 180); } static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie) @@ -422,7 +416,7 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) artpec6_pcie_wait_for_phy(artpec6_pcie); artpec6_pcie_set_nfts(artpec6_pcie); - for (bar = BAR_0; bar <= BAR_5; bar++) + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) dw_pcie_ep_reset_bar(pci, bar); } diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 2bf5a35c0570..cfeccd7e9fff 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -40,39 +40,6 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) __dw_pcie_ep_reset_bar(pci, bar, 0); } -static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr, - u8 cap) -{ - u8 cap_id, next_cap_ptr; - u16 reg; - - if (!cap_ptr) - return 0; - - reg = dw_pcie_readw_dbi(pci, cap_ptr); - cap_id = (reg & 0x00ff); - - if (cap_id > PCI_CAP_ID_MAX) - return 0; - - if (cap_id == cap) - return cap_ptr; - - next_cap_ptr = (reg & 0xff00) >> 8; - return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap); -} - -static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap) -{ - u8 next_cap_ptr; - u16 reg; - - reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST); - next_cap_ptr = (reg & 0x00ff); - - return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap); -} - static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr) { @@ -467,7 +434,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, tbl_addr += (tbl_offset + ((interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE)); tbl_addr &= PCI_BASE_ADDRESS_MEM_MASK; - msix_tbl = ioremap_nocache(ep->phys_base + tbl_addr, + msix_tbl = ioremap(ep->phys_base + tbl_addr, PCI_MSIX_ENTRY_SIZE); if (!msix_tbl) return -EINVAL; @@ -531,6 +498,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) int ret; u32 reg; void *addr; + u8 hdr_type; unsigned int nbars; unsigned int offset; struct pci_epc *epc; @@ -595,6 +563,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) if (ep->ops->ep_init) ep->ops->ep_init(ep); + hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE); + if (hdr_type != PCI_HEADER_TYPE_NORMAL) { + dev_err(pci->dev, "PCIe controller is not set to EP mode (hdr_type:0x%x)!\n", + hdr_type); + return -EIO; + } + ret = of_property_read_u8(np, "max-functions", &epc->max_functions); if (ret < 0) epc->max_functions = 1; @@ -612,9 +587,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n"); return -ENOMEM; } - ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI); + ep->msi_cap = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI); - ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX); + ep->msix_cap = dw_pcie_find_capability(pci, PCI_CAP_ID_MSIX); offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); if (offset) { diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index f93252d0da5b..395feb8ca051 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -10,6 +10,7 @@ #include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> +#include <linux/msi.h> #include <linux/of_address.h> #include <linux/of_pci.h> #include <linux/pci_regs.h> @@ -78,7 +79,8 @@ static struct msi_domain_info dw_pcie_msi_domain_info = { irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) { int i, pos, irq; - u32 val, num_ctrls; + unsigned long val; + u32 status, num_ctrls; irqreturn_t ret = IRQ_NONE; num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; @@ -86,14 +88,14 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) for (i = 0; i < num_ctrls; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + (i * MSI_REG_CTRL_BLOCK_SIZE), - 4, &val); - if (!val) + 4, &status); + if (!status) continue; ret = IRQ_HANDLED; + val = status; pos = 0; - while ((pos = find_next_bit((unsigned long *) &val, - MAX_MSI_IRQS_PER_CTRL, + while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, pos)) != MAX_MSI_IRQS_PER_CTRL) { irq = irq_find_mapping(pp->irq_domain, (i * MAX_MSI_IRQS_PER_CTRL) + @@ -319,10 +321,11 @@ int dw_pcie_host_init(struct pcie_port *pp) struct device *dev = pci->dev; struct device_node *np = dev->of_node; struct platform_device *pdev = to_platform_device(dev); - struct resource_entry *win, *tmp; + struct resource_entry *win; struct pci_bus *child; struct pci_host_bridge *bridge; struct resource *cfg_res; + u32 hdr_type; int ret; raw_spin_lock_init(&pci->pp.lock); @@ -341,31 +344,20 @@ int dw_pcie_host_init(struct pcie_port *pp) if (!bridge) return -ENOMEM; - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &bridge->windows, &pp->io_base); - if (ret) - return ret; - - ret = devm_request_pci_bus_resources(dev, &bridge->windows); + ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, NULL); if (ret) return ret; /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry_safe(win, tmp, &bridge->windows) { + resource_list_for_each_entry(win, &bridge->windows) { switch (resource_type(win->res)) { case IORESOURCE_IO: - ret = devm_pci_remap_iospace(dev, win->res, - pp->io_base); - if (ret) { - dev_warn(dev, "Error %d: failed to map resource %pR\n", - ret, win->res); - resource_list_destroy_entry(win); - } else { - pp->io = win->res; - pp->io->name = "I/O"; - pp->io_size = resource_size(pp->io); - pp->io_bus_addr = pp->io->start - win->offset; - } + pp->io = win->res; + pp->io->name = "I/O"; + pp->io_size = resource_size(pp->io); + pp->io_bus_addr = pp->io->start - win->offset; + pp->io_base = pci_pio_to_address(pp->io->start); break; case IORESOURCE_MEM: pp->mem = win->res; @@ -464,6 +456,21 @@ int dw_pcie_host_init(struct pcie_port *pp) goto err_free_msi; } + ret = dw_pcie_rd_own_conf(pp, PCI_HEADER_TYPE, 1, &hdr_type); + if (ret != PCIBIOS_SUCCESSFUL) { + dev_err(pci->dev, "Failed reading PCI_HEADER_TYPE cfg space reg (ret: 0x%x)\n", + ret); + ret = pcibios_err_to_errno(ret); + goto err_free_msi; + } + if (hdr_type != PCI_HEADER_TYPE_BRIDGE) { + dev_err(pci->dev, + "PCIe controller is not set to bridge type (hdr_type: 0x%x)!\n", + hdr_type); + ret = -EIO; + goto err_free_msi; + } + pp->root_bus_nr = pp->busn->start; bridge->dev.parent = dev; @@ -628,6 +635,12 @@ void dw_pcie_setup_rc(struct pcie_port *pp) u32 val, ctrl, num_ctrls; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + /* + * Enable DBI read-only registers for writing/updating configuration. + * Write permission gets disabled towards the end of this function. + */ + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_setup(pci); if (!pp->ops->msi_host_init) { @@ -650,12 +663,10 @@ void dw_pcie_setup_rc(struct pcie_port *pp) dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); /* Setup interrupt pins */ - dw_pcie_dbi_ro_wr_en(pci); val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE); val &= 0xffff00ff; val |= 0x00000100; dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val); - dw_pcie_dbi_ro_wr_dis(pci); /* Setup bus numbers */ val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS); @@ -687,15 +698,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp) dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); - /* Enable write permission for the DBI read-only register */ - dw_pcie_dbi_ro_wr_en(pci); /* Program correct class for RC */ dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI); - /* Better disable write permission right after the update */ - dw_pcie_dbi_ro_wr_dis(pci); dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val); val |= PORT_LOGIC_SPEED_CHANGE; dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); + + dw_pcie_dbi_ro_wr_dis(pci); } EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index b58fdcbc664b..73646b677aff 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -70,7 +70,7 @@ static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar; - for (bar = BAR_0; bar <= BAR_5; bar++) + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) dw_pcie_ep_reset_bar(pci, bar); } diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 7d25102c304c..681548c88282 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -12,8 +12,89 @@ #include <linux/of.h> #include <linux/types.h> +#include "../../pci.h" #include "pcie-designware.h" +/* + * These interfaces resemble the pci_find_*capability() interfaces, but these + * are for configuring host controllers, which are bridges *to* PCI devices but + * are not PCI devices themselves. + */ +static u8 __dw_pcie_find_next_cap(struct dw_pcie *pci, u8 cap_ptr, + u8 cap) +{ + u8 cap_id, next_cap_ptr; + u16 reg; + + if (!cap_ptr) + return 0; + + reg = dw_pcie_readw_dbi(pci, cap_ptr); + cap_id = (reg & 0x00ff); + + if (cap_id > PCI_CAP_ID_MAX) + return 0; + + if (cap_id == cap) + return cap_ptr; + + next_cap_ptr = (reg & 0xff00) >> 8; + return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); +} + +u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap) +{ + u8 next_cap_ptr; + u16 reg; + + reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST); + next_cap_ptr = (reg & 0x00ff); + + return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); +} +EXPORT_SYMBOL_GPL(dw_pcie_find_capability); + +static u16 dw_pcie_find_next_ext_capability(struct dw_pcie *pci, u16 start, + u8 cap) +{ + u32 header; + int ttl; + int pos = PCI_CFG_SPACE_SIZE; + + /* minimum 8 bytes per capability */ + ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; + + if (start) + pos = start; + + header = dw_pcie_readl_dbi(pci, pos); + /* + * If we have no capabilities, this is indicated by cap ID, + * cap version and next pointer all being 0. + */ + if (header == 0) + return 0; + + while (ttl-- > 0) { + if (PCI_EXT_CAP_ID(header) == cap && pos != start) + return pos; + + pos = PCI_EXT_CAP_NEXT(header); + if (pos < PCI_CFG_SPACE_SIZE) + break; + + header = dw_pcie_readl_dbi(pci, pos); + } + + return 0; +} + +u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap) +{ + return dw_pcie_find_next_ext_capability(pci, 0, cap); +} +EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability); + int dw_pcie_read(void __iomem *addr, int size, u32 *val) { if (!IS_ALIGNED((uintptr_t)addr, size)) { @@ -376,10 +457,11 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); } - dev_err(pci->dev, "Phy link never came up\n"); + dev_info(pci->dev, "Phy link never came up\n"); return -ETIMEDOUT; } +EXPORT_SYMBOL_GPL(dw_pcie_wait_for_link); int dw_pcie_link_up(struct dw_pcie *pci) { @@ -393,6 +475,61 @@ int dw_pcie_link_up(struct dw_pcie *pci) (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING))); } +void dw_pcie_upconfig_setup(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL); + val |= PORT_MLTI_UPCFG_SUPPORT; + dw_pcie_writel_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL, val); +} +EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup); + +void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen) +{ + u32 reg, val; + u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + + reg = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCTL2); + reg &= ~PCI_EXP_LNKCTL2_TLS; + + switch (pcie_link_speed[link_gen]) { + case PCIE_SPEED_2_5GT: + reg |= PCI_EXP_LNKCTL2_TLS_2_5GT; + break; + case PCIE_SPEED_5_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_5_0GT; + break; + case PCIE_SPEED_8_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_8_0GT; + break; + case PCIE_SPEED_16_0GT: + reg |= PCI_EXP_LNKCTL2_TLS_16_0GT; + break; + default: + /* Use hardware capability */ + val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP); + val = FIELD_GET(PCI_EXP_LNKCAP_SLS, val); + reg &= ~PCI_EXP_LNKCTL2_HASD; + reg |= FIELD_PREP(PCI_EXP_LNKCTL2_TLS, val); + break; + } + + dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCTL2, reg); +} +EXPORT_SYMBOL_GPL(dw_pcie_link_set_max_speed); + +void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_N_FTS_MASK; + val |= n_fts & PORT_LOGIC_N_FTS_MASK; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); +} +EXPORT_SYMBOL_GPL(dw_pcie_link_set_n_fts); + static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) { u32 val; @@ -423,8 +560,10 @@ void dw_pcie_setup(struct dw_pcie *pci) ret = of_property_read_u32(np, "num-lanes", &lanes); - if (ret) - lanes = 0; + if (ret) { + dev_dbg(pci->dev, "property num-lanes isn't found\n"); + return; + } /* Set the number of lanes */ val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); @@ -466,4 +605,11 @@ void dw_pcie_setup(struct dw_pcie *pci) break; } dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + + if (of_property_read_bool(np, "snps,enable-cdm-check")) { + val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS); + val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS | + PCIE_PL_CHK_REG_CHK_REG_START; + dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val); + } } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index ffed084a0b4f..a22ea5982817 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -30,7 +30,12 @@ #define LINK_WAIT_IATU 9 /* Synopsys-specific PCIe configuration registers */ +#define PCIE_PORT_AFR 0x70C +#define PORT_AFR_N_FTS_MASK GENMASK(15, 8) +#define PORT_AFR_CC_N_FTS_MASK GENMASK(23, 16) + #define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_DLL_LINK_EN BIT(5) #define PORT_LINK_MODE_MASK GENMASK(21, 16) #define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n) #define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1) @@ -46,6 +51,7 @@ #define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29) #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_N_FTS_MASK GENMASK(7, 0) #define PORT_LOGIC_SPEED_CHANGE BIT(17) #define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8) #define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n) @@ -60,6 +66,9 @@ #define PCIE_MSI_INTR0_MASK 0x82C #define PCIE_MSI_INTR0_STATUS 0x830 +#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 +#define PORT_MLTI_UPCFG_SUPPORT BIT(7) + #define PCIE_ATU_VIEWPORT 0x900 #define PCIE_ATU_REGION_INBOUND BIT(31) #define PCIE_ATU_REGION_OUTBOUND 0 @@ -86,6 +95,15 @@ #define PCIE_MISC_CONTROL_1_OFF 0x8BC #define PCIE_DBI_RO_WR_EN BIT(0) +#define PCIE_PL_CHK_REG_CONTROL_STATUS 0xB20 +#define PCIE_PL_CHK_REG_CHK_REG_START BIT(0) +#define PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS BIT(1) +#define PCIE_PL_CHK_REG_CHK_REG_COMPARISON_ERROR BIT(16) +#define PCIE_PL_CHK_REG_CHK_REG_LOGIC_ERROR BIT(17) +#define PCIE_PL_CHK_REG_CHK_REG_COMPLETE BIT(18) + +#define PCIE_PL_CHK_REG_ERR_ADDR 0xB28 + /* * iATU Unroll-specific register definitions * From 4.80 core version the address translation will be made by unroll @@ -205,7 +223,7 @@ struct dw_pcie_ep { phys_addr_t phys_base; size_t addr_size; size_t page_size; - u8 bar_to_atu[6]; + u8 bar_to_atu[PCI_STD_NUM_BARS]; phys_addr_t *outbound_addr; unsigned long *ib_window_map; unsigned long *ob_window_map; @@ -251,6 +269,9 @@ struct dw_pcie { #define to_dw_pcie_from_ep(endpoint) \ container_of((endpoint), struct dw_pcie, ep) +u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap); +u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap); + int dw_pcie_read(void __iomem *addr, int size, u32 *val); int dw_pcie_write(void __iomem *addr, int size, u32 val); @@ -261,6 +282,9 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val); u32 dw_pcie_read_atu(struct dw_pcie *pci, u32 reg, size_t size); void dw_pcie_write_atu(struct dw_pcie *pci, u32 reg, size_t size, u32 val); int dw_pcie_link_up(struct dw_pcie *pci); +void dw_pcie_upconfig_setup(struct dw_pcie *pci); +void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen); +void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts); int dw_pcie_wait_for_link(struct dw_pcie *pci); void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, u64 cpu_addr, u64 pci_addr, diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 954bc2b74bbc..811b5c6d62ea 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -340,8 +340,8 @@ static int histb_pcie_probe(struct platform_device *pdev) hipcie->vpcie = devm_regulator_get_optional(dev, "vpcie"); if (IS_ERR(hipcie->vpcie)) { - if (PTR_ERR(hipcie->vpcie) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (PTR_ERR(hipcie->vpcie) != -ENODEV) + return PTR_ERR(hipcie->vpcie); hipcie->vpcie = NULL; } diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c new file mode 100644 index 000000000000..fc2a12212dec --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Intel Gateway SoCs + * + * Copyright (c) 2019 Intel Corporation. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/gpio/consumer.h> +#include <linux/iopoll.h> +#include <linux/pci_regs.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#include "../../pci.h" +#include "pcie-designware.h" + +#define PORT_AFR_N_FTS_GEN12_DFT (SZ_128 - 1) +#define PORT_AFR_N_FTS_GEN3 180 +#define PORT_AFR_N_FTS_GEN4 196 + +/* PCIe Application logic Registers */ +#define PCIE_APP_CCR 0x10 +#define PCIE_APP_CCR_LTSSM_ENABLE BIT(0) + +#define PCIE_APP_MSG_CR 0x30 +#define PCIE_APP_MSG_XMT_PM_TURNOFF BIT(0) + +#define PCIE_APP_PMC 0x44 +#define PCIE_APP_PMC_IN_L2 BIT(20) + +#define PCIE_APP_IRNEN 0xF4 +#define PCIE_APP_IRNCR 0xF8 +#define PCIE_APP_IRN_AER_REPORT BIT(0) +#define PCIE_APP_IRN_PME BIT(2) +#define PCIE_APP_IRN_RX_VDM_MSG BIT(4) +#define PCIE_APP_IRN_PM_TO_ACK BIT(9) +#define PCIE_APP_IRN_LINK_AUTO_BW_STAT BIT(11) +#define PCIE_APP_IRN_BW_MGT BIT(12) +#define PCIE_APP_IRN_MSG_LTR BIT(18) +#define PCIE_APP_IRN_SYS_ERR_RC BIT(29) +#define PCIE_APP_INTX_OFST 12 + +#define PCIE_APP_IRN_INT \ + (PCIE_APP_IRN_AER_REPORT | PCIE_APP_IRN_PME | \ + PCIE_APP_IRN_RX_VDM_MSG | PCIE_APP_IRN_SYS_ERR_RC | \ + PCIE_APP_IRN_PM_TO_ACK | PCIE_APP_IRN_MSG_LTR | \ + PCIE_APP_IRN_BW_MGT | PCIE_APP_IRN_LINK_AUTO_BW_STAT | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTA) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTB) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTC) | \ + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTD)) + +#define BUS_IATU_OFFSET SZ_256M +#define RESET_INTERVAL_MS 100 + +struct intel_pcie_soc { + unsigned int pcie_ver; + unsigned int pcie_atu_offset; + u32 num_viewport; +}; + +struct intel_pcie_port { + struct dw_pcie pci; + void __iomem *app_base; + struct gpio_desc *reset_gpio; + u32 rst_intrvl; + u32 max_speed; + u32 link_gen; + u32 max_width; + u32 n_fts; + struct clk *core_clk; + struct reset_control *core_rst; + struct phy *phy; + u8 pcie_cap_ofst; +}; + +static void pcie_update_bits(void __iomem *base, u32 ofs, u32 mask, u32 val) +{ + u32 old; + + old = readl(base + ofs); + val = (old & ~mask) | (val & mask); + + if (val != old) + 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); +} + +static void pcie_app_wr_mask(struct intel_pcie_port *lpp, u32 ofs, + u32 mask, u32 val) +{ + pcie_update_bits(lpp->app_base, ofs, mask, val); +} + +static inline u32 pcie_rc_cfg_rd(struct intel_pcie_port *lpp, u32 ofs) +{ + return dw_pcie_readl_dbi(&lpp->pci, ofs); +} + +static inline void pcie_rc_cfg_wr(struct intel_pcie_port *lpp, u32 ofs, u32 val) +{ + dw_pcie_writel_dbi(&lpp->pci, ofs, val); +} + +static void pcie_rc_cfg_wr_mask(struct intel_pcie_port *lpp, u32 ofs, + u32 mask, u32 val) +{ + pcie_update_bits(lpp->pci.dbi_base, ofs, mask, val); +} + +static void intel_pcie_ltssm_enable(struct intel_pcie_port *lpp) +{ + pcie_app_wr_mask(lpp, PCIE_APP_CCR, PCIE_APP_CCR_LTSSM_ENABLE, + PCIE_APP_CCR_LTSSM_ENABLE); +} + +static void intel_pcie_ltssm_disable(struct intel_pcie_port *lpp) +{ + pcie_app_wr_mask(lpp, PCIE_APP_CCR, PCIE_APP_CCR_LTSSM_ENABLE, 0); +} + +static void intel_pcie_link_setup(struct intel_pcie_port *lpp) +{ + u32 val; + u8 offset = lpp->pcie_cap_ofst; + + val = pcie_rc_cfg_rd(lpp, offset + PCI_EXP_LNKCAP); + lpp->max_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, val); + lpp->max_width = FIELD_GET(PCI_EXP_LNKCAP_MLW, val); + + val = pcie_rc_cfg_rd(lpp, offset + PCI_EXP_LNKCTL); + + val &= ~(PCI_EXP_LNKCTL_LD | PCI_EXP_LNKCTL_ASPMC); + pcie_rc_cfg_wr(lpp, offset + PCI_EXP_LNKCTL, val); +} + +static void intel_pcie_port_logic_setup(struct intel_pcie_port *lpp) +{ + u32 val, mask; + + switch (pcie_link_speed[lpp->max_speed]) { + case PCIE_SPEED_8_0GT: + lpp->n_fts = PORT_AFR_N_FTS_GEN3; + break; + case PCIE_SPEED_16_0GT: + lpp->n_fts = PORT_AFR_N_FTS_GEN4; + break; + default: + lpp->n_fts = PORT_AFR_N_FTS_GEN12_DFT; + break; + } + + mask = PORT_AFR_N_FTS_MASK | PORT_AFR_CC_N_FTS_MASK; + val = FIELD_PREP(PORT_AFR_N_FTS_MASK, lpp->n_fts) | + FIELD_PREP(PORT_AFR_CC_N_FTS_MASK, lpp->n_fts); + pcie_rc_cfg_wr_mask(lpp, PCIE_PORT_AFR, mask, val); + + /* Port Link Control Register */ + pcie_rc_cfg_wr_mask(lpp, PCIE_PORT_LINK_CONTROL, PORT_LINK_DLL_LINK_EN, + PORT_LINK_DLL_LINK_EN); +} + +static void intel_pcie_rc_setup(struct intel_pcie_port *lpp) +{ + intel_pcie_ltssm_disable(lpp); + intel_pcie_link_setup(lpp); + dw_pcie_setup_rc(&lpp->pci.pp); + dw_pcie_upconfig_setup(&lpp->pci); + intel_pcie_port_logic_setup(lpp); + dw_pcie_link_set_max_speed(&lpp->pci, lpp->link_gen); + dw_pcie_link_set_n_fts(&lpp->pci, lpp->n_fts); +} + +static int intel_pcie_ep_rst_init(struct intel_pcie_port *lpp) +{ + struct device *dev = lpp->pci.dev; + int ret; + + lpp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(lpp->reset_gpio)) { + ret = PTR_ERR(lpp->reset_gpio); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request PCIe GPIO: %d\n", ret); + return ret; + } + + /* Make initial reset last for 100us */ + usleep_range(100, 200); + + return 0; +} + +static void intel_pcie_core_rst_assert(struct intel_pcie_port *lpp) +{ + reset_control_assert(lpp->core_rst); +} + +static void intel_pcie_core_rst_deassert(struct intel_pcie_port *lpp) +{ + /* + * One micro-second delay to make sure the reset pulse + * wide enough so that core reset is clean. + */ + udelay(1); + reset_control_deassert(lpp->core_rst); + + /* + * Some SoC core reset also reset PHY, more delay needed + * to make sure the reset process is done. + */ + usleep_range(1000, 2000); +} + +static void intel_pcie_device_rst_assert(struct intel_pcie_port *lpp) +{ + gpiod_set_value_cansleep(lpp->reset_gpio, 1); +} + +static void intel_pcie_device_rst_deassert(struct intel_pcie_port *lpp) +{ + msleep(lpp->rst_intrvl); + gpiod_set_value_cansleep(lpp->reset_gpio, 0); +} + +static int intel_pcie_app_logic_setup(struct intel_pcie_port *lpp) +{ + intel_pcie_device_rst_deassert(lpp); + intel_pcie_ltssm_enable(lpp); + + return dw_pcie_wait_for_link(&lpp->pci); +} + +static void intel_pcie_core_irq_disable(struct intel_pcie_port *lpp) +{ + pcie_app_wr(lpp, PCIE_APP_IRNEN, 0); + pcie_app_wr(lpp, PCIE_APP_IRNCR, PCIE_APP_IRN_INT); +} + +static int intel_pcie_get_resources(struct platform_device *pdev) +{ + struct intel_pcie_port *lpp = platform_get_drvdata(pdev); + struct dw_pcie *pci = &lpp->pci; + struct device *dev = pci->dev; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + lpp->core_clk = devm_clk_get(dev, NULL); + if (IS_ERR(lpp->core_clk)) { + ret = PTR_ERR(lpp->core_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get clks: %d\n", ret); + return ret; + } + + lpp->core_rst = devm_reset_control_get(dev, NULL); + if (IS_ERR(lpp->core_rst)) { + ret = PTR_ERR(lpp->core_rst); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get resets: %d\n", ret); + return ret; + } + + ret = device_property_match_string(dev, "device_type", "pci"); + if (ret) { + dev_err(dev, "Failed to find pci device type: %d\n", ret); + return ret; + } + + ret = device_property_read_u32(dev, "reset-assert-ms", + &lpp->rst_intrvl); + if (ret) + lpp->rst_intrvl = RESET_INTERVAL_MS; + + ret = of_pci_get_max_link_speed(dev->of_node); + lpp->link_gen = ret < 0 ? 0 : ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app"); + lpp->app_base = devm_ioremap_resource(dev, res); + if (IS_ERR(lpp->app_base)) + return PTR_ERR(lpp->app_base); + + lpp->phy = devm_phy_get(dev, "pcie"); + if (IS_ERR(lpp->phy)) { + ret = PTR_ERR(lpp->phy); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Couldn't get pcie-phy: %d\n", ret); + return ret; + } + + return 0; +} + +static void intel_pcie_deinit_phy(struct intel_pcie_port *lpp) +{ + phy_exit(lpp->phy); +} + +static int intel_pcie_wait_l2(struct intel_pcie_port *lpp) +{ + u32 value; + int ret; + + if (pcie_link_speed[lpp->max_speed] < PCIE_SPEED_8_0GT) + return 0; + + /* Send PME_TURN_OFF message */ + pcie_app_wr_mask(lpp, PCIE_APP_MSG_CR, PCIE_APP_MSG_XMT_PM_TURNOFF, + PCIE_APP_MSG_XMT_PM_TURNOFF); + + /* Read PMC status and wait for falling into L2 link state */ + ret = readl_poll_timeout(lpp->app_base + PCIE_APP_PMC, value, + value & PCIE_APP_PMC_IN_L2, 20, + jiffies_to_usecs(5 * HZ)); + if (ret) + dev_err(lpp->pci.dev, "PCIe link enter L2 timeout!\n"); + + return ret; +} + +static void intel_pcie_turn_off(struct intel_pcie_port *lpp) +{ + if (dw_pcie_link_up(&lpp->pci)) + intel_pcie_wait_l2(lpp); + + /* Put endpoint device in reset state */ + intel_pcie_device_rst_assert(lpp); + pcie_rc_cfg_wr_mask(lpp, PCI_COMMAND, PCI_COMMAND_MEMORY, 0); +} + +static int intel_pcie_host_setup(struct intel_pcie_port *lpp) +{ + struct device *dev = lpp->pci.dev; + int ret; + + intel_pcie_core_rst_assert(lpp); + intel_pcie_device_rst_assert(lpp); + + ret = phy_init(lpp->phy); + if (ret) + return ret; + + intel_pcie_core_rst_deassert(lpp); + + ret = clk_prepare_enable(lpp->core_clk); + if (ret) { + dev_err(lpp->pci.dev, "Core clock enable failed: %d\n", ret); + goto clk_err; + } + + if (!lpp->pcie_cap_ofst) { + ret = dw_pcie_find_capability(&lpp->pci, PCI_CAP_ID_EXP); + if (!ret) { + ret = -ENXIO; + dev_err(dev, "Invalid PCIe capability offset\n"); + goto app_init_err; + } + + lpp->pcie_cap_ofst = ret; + } + + intel_pcie_rc_setup(lpp); + ret = intel_pcie_app_logic_setup(lpp); + if (ret) + goto app_init_err; + + /* Enable integrated interrupts */ + pcie_app_wr_mask(lpp, PCIE_APP_IRNEN, PCIE_APP_IRN_INT, + PCIE_APP_IRN_INT); + + return 0; + +app_init_err: + clk_disable_unprepare(lpp->core_clk); +clk_err: + intel_pcie_core_rst_assert(lpp); + intel_pcie_deinit_phy(lpp); + + return ret; +} + +static void __intel_pcie_remove(struct intel_pcie_port *lpp) +{ + intel_pcie_core_irq_disable(lpp); + intel_pcie_turn_off(lpp); + clk_disable_unprepare(lpp->core_clk); + intel_pcie_core_rst_assert(lpp); + intel_pcie_deinit_phy(lpp); +} + +static int intel_pcie_remove(struct platform_device *pdev) +{ + struct intel_pcie_port *lpp = platform_get_drvdata(pdev); + struct pcie_port *pp = &lpp->pci.pp; + + dw_pcie_host_deinit(pp); + __intel_pcie_remove(lpp); + + return 0; +} + +static int __maybe_unused intel_pcie_suspend_noirq(struct device *dev) +{ + struct intel_pcie_port *lpp = dev_get_drvdata(dev); + int ret; + + intel_pcie_core_irq_disable(lpp); + ret = intel_pcie_wait_l2(lpp); + if (ret) + return ret; + + intel_pcie_deinit_phy(lpp); + clk_disable_unprepare(lpp->core_clk); + return ret; +} + +static int __maybe_unused intel_pcie_resume_noirq(struct device *dev) +{ + struct intel_pcie_port *lpp = dev_get_drvdata(dev); + + return intel_pcie_host_setup(lpp); +} + +static int intel_pcie_rc_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct intel_pcie_port *lpp = dev_get_drvdata(pci->dev); + + return intel_pcie_host_setup(lpp); +} + +/* + * Dummy function so that DW core doesn't configure MSI + */ +static int intel_pcie_msi_init(struct pcie_port *pp) +{ + return 0; +} + +u64 intel_pcie_cpu_addr(struct dw_pcie *pcie, u64 cpu_addr) +{ + return cpu_addr + BUS_IATU_OFFSET; +} + +static const struct dw_pcie_ops intel_pcie_ops = { + .cpu_addr_fixup = intel_pcie_cpu_addr, +}; + +static const struct dw_pcie_host_ops intel_pcie_dw_ops = { + .host_init = intel_pcie_rc_init, + .msi_host_init = intel_pcie_msi_init, +}; + +static const struct intel_pcie_soc pcie_data = { + .pcie_ver = 0x520A, + .pcie_atu_offset = 0xC0000, + .num_viewport = 3, +}; + +static int intel_pcie_probe(struct platform_device *pdev) +{ + const struct intel_pcie_soc *data; + struct device *dev = &pdev->dev; + struct intel_pcie_port *lpp; + struct pcie_port *pp; + struct dw_pcie *pci; + int ret; + + lpp = devm_kzalloc(dev, sizeof(*lpp), GFP_KERNEL); + if (!lpp) + return -ENOMEM; + + platform_set_drvdata(pdev, lpp); + pci = &lpp->pci; + pci->dev = dev; + pp = &pci->pp; + + ret = intel_pcie_get_resources(pdev); + if (ret) + return ret; + + ret = intel_pcie_ep_rst_init(lpp); + if (ret) + return ret; + + data = device_get_match_data(dev); + if (!data) + return -ENODEV; + + pci->ops = &intel_pcie_ops; + pci->version = data->pcie_ver; + pci->atu_base = pci->dbi_base + data->pcie_atu_offset; + pp->ops = &intel_pcie_dw_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Cannot initialize host\n"); + return ret; + } + + /* + * Intel PCIe doesn't configure IO region, so set viewport + * to not perform IO region access. + */ + pci->num_viewport = data->num_viewport; + + return 0; +} + +static const struct dev_pm_ops intel_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pcie_suspend_noirq, + intel_pcie_resume_noirq) +}; + +static const struct of_device_id of_intel_pcie_match[] = { + { .compatible = "intel,lgm-pcie", .data = &pcie_data }, + {} +}; + +static struct platform_driver intel_pcie_driver = { + .probe = intel_pcie_probe, + .remove = intel_pcie_remove, + .driver = { + .name = "intel-gw-pcie", + .of_match_table = of_intel_pcie_match, + .pm = &intel_pcie_pm_ops, + }, +}; +builtin_platform_driver(intel_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index 8df1914226be..c19617a912bd 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -436,7 +436,7 @@ static int kirin_pcie_host_init(struct pcie_port *pp) return 0; } -static struct dw_pcie_ops kirin_dw_pcie_ops = { +static const struct dw_pcie_ops kirin_dw_pcie_ops = { .read_dbi = kirin_pcie_read_dbi, .write_dbi = kirin_pcie_write_dbi, .link_up = kirin_pcie_link_up, diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 7e581748ee9f..5ea527a6bd9f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -54,6 +54,7 @@ #define PCIE20_PARF_LTSSM 0x1B0 #define PCIE20_PARF_SID_OFFSET 0x234 #define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C +#define PCIE20_PARF_DEVICE_TYPE 0x1000 #define PCIE20_ELBI_SYS_CTRL 0x04 #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) @@ -80,6 +81,8 @@ #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 #define SLV_ADDR_SPACE_SZ 0x10000000 +#define DEVICE_TYPE_RC 0x4 + #define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 struct qcom_pcie_resources_2_1_0 { struct clk *iface_clk; @@ -139,12 +142,20 @@ struct qcom_pcie_resources_2_3_3 { struct reset_control *rst[7]; }; +struct qcom_pcie_resources_2_7_0 { + struct clk_bulk_data clks[6]; + struct regulator_bulk_data supplies[2]; + struct reset_control *pci_reset; + struct clk *pipe_clk; +}; + union qcom_pcie_resources { struct qcom_pcie_resources_1_0_0 v1_0_0; struct qcom_pcie_resources_2_1_0 v2_1_0; struct qcom_pcie_resources_2_3_2 v2_3_2; struct qcom_pcie_resources_2_3_3 v2_3_3; struct qcom_pcie_resources_2_4_0 v2_4_0; + struct qcom_pcie_resources_2_7_0 v2_7_0; }; struct qcom_pcie; @@ -1068,6 +1079,134 @@ err_clk_iface: return ret; } +static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int ret; + + res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); + if (IS_ERR(res->pci_reset)) + return PTR_ERR(res->pci_reset); + + res->supplies[0].supply = "vdda"; + res->supplies[1].supply = "vddpe-3v3"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), + res->supplies); + if (ret) + return ret; + + res->clks[0].id = "aux"; + res->clks[1].id = "cfg"; + res->clks[2].id = "bus_master"; + res->clks[3].id = "bus_slave"; + res->clks[4].id = "slave_q2a"; + res->clks[5].id = "tbu"; + + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); + if (ret < 0) + return ret; + + res->pipe_clk = devm_clk_get(dev, "pipe"); + return PTR_ERR_OR_ZERO(res->pipe_clk); +} + +static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + u32 val; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); + if (ret < 0) { + dev_err(dev, "cannot enable regulators\n"); + return ret; + } + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); + if (ret < 0) + goto err_disable_regulators; + + ret = reset_control_assert(res->pci_reset); + if (ret < 0) { + dev_err(dev, "cannot deassert pci reset\n"); + goto err_disable_clocks; + } + + usleep_range(1000, 1500); + + ret = reset_control_deassert(res->pci_reset); + if (ret < 0) { + dev_err(dev, "cannot deassert pci reset\n"); + goto err_disable_clocks; + } + + ret = clk_prepare_enable(res->pipe_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable pipe clock\n"); + goto err_disable_clocks; + } + + /* configure PCIe to RC mode */ + writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE); + + /* enable PCIe clocks and resets */ + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~BIT(0); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + + /* change DBI base address */ + writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + + /* MAC PHY_POWERDOWN MUX DISABLE */ + val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); + val &= ~BIT(29); + writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); + + val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + val |= BIT(4); + writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + val |= BIT(31); + writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + } + + return 0; +err_disable_clocks: + clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); +err_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); + + return ret; +} + +static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); +} + +static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + return clk_prepare_enable(res->pipe_clk); +} + +static void qcom_pcie_post_deinit_2_7_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + + clk_disable_unprepare(res->pipe_clk); +} + static int qcom_pcie_link_up(struct dw_pcie *pci) { u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA); @@ -1167,6 +1306,16 @@ static const struct qcom_pcie_ops ops_2_3_3 = { .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, }; +/* Qcom IP rev.: 2.7.0 Synopsys IP rev.: 4.30a */ +static const struct qcom_pcie_ops ops_2_7_0 = { + .get_resources = qcom_pcie_get_resources_2_7_0, + .init = qcom_pcie_init_2_7_0, + .deinit = qcom_pcie_deinit_2_7_0, + .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, + .post_init = qcom_pcie_post_init_2_7_0, + .post_deinit = qcom_pcie_post_deinit_2_7_0, +}; + static const struct dw_pcie_ops dw_pcie_ops = { .link_up = qcom_pcie_link_up, }; @@ -1282,6 +1431,7 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 }, { .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 }, { .compatible = "qcom,pcie-qcs404", .data = &ops_2_4_0 }, + { .compatible = "qcom,pcie-sdm845", .data = &ops_2_7_0 }, { } }; diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c new file mode 100644 index 000000000000..cbe95f0ea0ca --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -0,0 +1,1730 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for Tegra194 SoC + * + * Copyright (C) 2019 NVIDIA Corporation. + * + * Author: Vidya Sagar <vidyas@nvidia.com> + */ + +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/of_pci.h> +#include <linux/pci.h> +#include <linux/phy/phy.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/random.h> +#include <linux/reset.h> +#include <linux/resource.h> +#include <linux/types.h> +#include "pcie-designware.h" +#include <soc/tegra/bpmp.h> +#include <soc/tegra/bpmp-abi.h> +#include "../../pci.h" + +#define APPL_PINMUX 0x0 +#define APPL_PINMUX_PEX_RST BIT(0) +#define APPL_PINMUX_CLKREQ_OVERRIDE_EN BIT(2) +#define APPL_PINMUX_CLKREQ_OVERRIDE BIT(3) +#define APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE_EN BIT(4) +#define APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE BIT(5) + +#define APPL_CTRL 0x4 +#define APPL_CTRL_SYS_PRE_DET_STATE BIT(6) +#define APPL_CTRL_LTSSM_EN BIT(7) +#define APPL_CTRL_HW_HOT_RST_EN BIT(20) +#define APPL_CTRL_HW_HOT_RST_MODE_MASK GENMASK(1, 0) +#define APPL_CTRL_HW_HOT_RST_MODE_SHIFT 22 +#define APPL_CTRL_HW_HOT_RST_MODE_IMDT_RST 0x1 + +#define APPL_INTR_EN_L0_0 0x8 +#define APPL_INTR_EN_L0_0_LINK_STATE_INT_EN BIT(0) +#define APPL_INTR_EN_L0_0_MSI_RCV_INT_EN BIT(4) +#define APPL_INTR_EN_L0_0_INT_INT_EN BIT(8) +#define APPL_INTR_EN_L0_0_CDM_REG_CHK_INT_EN BIT(19) +#define APPL_INTR_EN_L0_0_SYS_INTR_EN BIT(30) +#define APPL_INTR_EN_L0_0_SYS_MSI_INTR_EN BIT(31) + +#define APPL_INTR_STATUS_L0 0xC +#define APPL_INTR_STATUS_L0_LINK_STATE_INT BIT(0) +#define APPL_INTR_STATUS_L0_INT_INT BIT(8) +#define APPL_INTR_STATUS_L0_CDM_REG_CHK_INT BIT(18) + +#define APPL_INTR_EN_L1_0_0 0x1C +#define APPL_INTR_EN_L1_0_0_LINK_REQ_RST_NOT_INT_EN BIT(1) + +#define APPL_INTR_STATUS_L1_0_0 0x20 +#define APPL_INTR_STATUS_L1_0_0_LINK_REQ_RST_NOT_CHGED BIT(1) + +#define APPL_INTR_STATUS_L1_1 0x2C +#define APPL_INTR_STATUS_L1_2 0x30 +#define APPL_INTR_STATUS_L1_3 0x34 +#define APPL_INTR_STATUS_L1_6 0x3C +#define APPL_INTR_STATUS_L1_7 0x40 + +#define APPL_INTR_EN_L1_8_0 0x44 +#define APPL_INTR_EN_L1_8_BW_MGT_INT_EN BIT(2) +#define APPL_INTR_EN_L1_8_AUTO_BW_INT_EN BIT(3) +#define APPL_INTR_EN_L1_8_INTX_EN BIT(11) +#define APPL_INTR_EN_L1_8_AER_INT_EN BIT(15) + +#define APPL_INTR_STATUS_L1_8_0 0x4C +#define APPL_INTR_STATUS_L1_8_0_EDMA_INT_MASK GENMASK(11, 6) +#define APPL_INTR_STATUS_L1_8_0_BW_MGT_INT_STS BIT(2) +#define APPL_INTR_STATUS_L1_8_0_AUTO_BW_INT_STS BIT(3) + +#define APPL_INTR_STATUS_L1_9 0x54 +#define APPL_INTR_STATUS_L1_10 0x58 +#define APPL_INTR_STATUS_L1_11 0x64 +#define APPL_INTR_STATUS_L1_13 0x74 +#define APPL_INTR_STATUS_L1_14 0x78 +#define APPL_INTR_STATUS_L1_15 0x7C +#define APPL_INTR_STATUS_L1_17 0x88 + +#define APPL_INTR_EN_L1_18 0x90 +#define APPL_INTR_EN_L1_18_CDM_REG_CHK_CMPLT BIT(2) +#define APPL_INTR_EN_L1_18_CDM_REG_CHK_CMP_ERR BIT(1) +#define APPL_INTR_EN_L1_18_CDM_REG_CHK_LOGIC_ERR BIT(0) + +#define APPL_INTR_STATUS_L1_18 0x94 +#define APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMPLT BIT(2) +#define APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMP_ERR BIT(1) +#define APPL_INTR_STATUS_L1_18_CDM_REG_CHK_LOGIC_ERR BIT(0) + +#define APPL_MSI_CTRL_2 0xB0 + +#define APPL_LTR_MSG_1 0xC4 +#define LTR_MSG_REQ BIT(15) +#define LTR_MST_NO_SNOOP_SHIFT 16 + +#define APPL_LTR_MSG_2 0xC8 +#define APPL_LTR_MSG_2_LTR_MSG_REQ_STATE BIT(3) + +#define APPL_LINK_STATUS 0xCC +#define APPL_LINK_STATUS_RDLH_LINK_UP BIT(0) + +#define APPL_DEBUG 0xD0 +#define APPL_DEBUG_PM_LINKST_IN_L2_LAT BIT(21) +#define APPL_DEBUG_PM_LINKST_IN_L0 0x11 +#define APPL_DEBUG_LTSSM_STATE_MASK GENMASK(8, 3) +#define APPL_DEBUG_LTSSM_STATE_SHIFT 3 +#define LTSSM_STATE_PRE_DETECT 5 + +#define APPL_RADM_STATUS 0xE4 +#define APPL_PM_XMT_TURNOFF_STATE BIT(0) + +#define APPL_DM_TYPE 0x100 +#define APPL_DM_TYPE_MASK GENMASK(3, 0) +#define APPL_DM_TYPE_RP 0x4 +#define APPL_DM_TYPE_EP 0x0 + +#define APPL_CFG_BASE_ADDR 0x104 +#define APPL_CFG_BASE_ADDR_MASK GENMASK(31, 12) + +#define APPL_CFG_IATU_DMA_BASE_ADDR 0x108 +#define APPL_CFG_IATU_DMA_BASE_ADDR_MASK GENMASK(31, 18) + +#define APPL_CFG_MISC 0x110 +#define APPL_CFG_MISC_SLV_EP_MODE BIT(14) +#define APPL_CFG_MISC_ARCACHE_MASK GENMASK(13, 10) +#define APPL_CFG_MISC_ARCACHE_SHIFT 10 +#define APPL_CFG_MISC_ARCACHE_VAL 3 + +#define APPL_CFG_SLCG_OVERRIDE 0x114 +#define APPL_CFG_SLCG_OVERRIDE_SLCG_EN_MASTER BIT(0) + +#define APPL_CAR_RESET_OVRD 0x12C +#define APPL_CAR_RESET_OVRD_CYA_OVERRIDE_CORE_RST_N BIT(0) + +#define IO_BASE_IO_DECODE BIT(0) +#define IO_BASE_IO_DECODE_BIT8 BIT(8) + +#define CFG_PREF_MEM_LIMIT_BASE_MEM_DECODE BIT(0) +#define CFG_PREF_MEM_LIMIT_BASE_MEM_LIMIT_DECODE BIT(16) + +#define CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF 0x718 +#define CFG_TIMER_CTRL_ACK_NAK_SHIFT (19) + +#define EVENT_COUNTER_ALL_CLEAR 0x3 +#define EVENT_COUNTER_ENABLE_ALL 0x7 +#define EVENT_COUNTER_ENABLE_SHIFT 2 +#define EVENT_COUNTER_EVENT_SEL_MASK GENMASK(7, 0) +#define EVENT_COUNTER_EVENT_SEL_SHIFT 16 +#define EVENT_COUNTER_EVENT_Tx_L0S 0x2 +#define EVENT_COUNTER_EVENT_Rx_L0S 0x3 +#define EVENT_COUNTER_EVENT_L1 0x5 +#define EVENT_COUNTER_EVENT_L1_1 0x7 +#define EVENT_COUNTER_EVENT_L1_2 0x8 +#define EVENT_COUNTER_GROUP_SEL_SHIFT 24 +#define EVENT_COUNTER_GROUP_5 0x5 + +#define PORT_LOGIC_ACK_F_ASPM_CTRL 0x70C +#define ENTER_ASPM BIT(30) +#define L0S_ENTRANCE_LAT_SHIFT 24 +#define L0S_ENTRANCE_LAT_MASK GENMASK(26, 24) +#define L1_ENTRANCE_LAT_SHIFT 27 +#define L1_ENTRANCE_LAT_MASK GENMASK(29, 27) +#define N_FTS_SHIFT 8 +#define N_FTS_MASK GENMASK(7, 0) +#define N_FTS_VAL 52 + +#define PORT_LOGIC_GEN2_CTRL 0x80C +#define PORT_LOGIC_GEN2_CTRL_DIRECT_SPEED_CHANGE BIT(17) +#define FTS_MASK GENMASK(7, 0) +#define FTS_VAL 52 + +#define PORT_LOGIC_MSI_CTRL_INT_0_EN 0x828 + +#define GEN3_EQ_CONTROL_OFF 0x8a8 +#define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_SHIFT 8 +#define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_MASK GENMASK(23, 8) +#define GEN3_EQ_CONTROL_OFF_FB_MODE_MASK GENMASK(3, 0) + +#define GEN3_RELATED_OFF 0x890 +#define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0) +#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16) +#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24 +#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24) + +#define PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT 0x8D0 +#define AMBA_ERROR_RESPONSE_CRS_SHIFT 3 +#define AMBA_ERROR_RESPONSE_CRS_MASK GENMASK(1, 0) +#define AMBA_ERROR_RESPONSE_CRS_OKAY 0 +#define AMBA_ERROR_RESPONSE_CRS_OKAY_FFFFFFFF 1 +#define AMBA_ERROR_RESPONSE_CRS_OKAY_FFFF0001 2 + +#define PORT_LOGIC_MSIX_DOORBELL 0x948 + +#define CAP_SPCIE_CAP_OFF 0x154 +#define CAP_SPCIE_CAP_OFF_DSP_TX_PRESET0_MASK GENMASK(3, 0) +#define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_MASK GENMASK(11, 8) +#define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_SHIFT 8 + +#define PME_ACK_TIMEOUT 10000 + +#define LTSSM_TIMEOUT 50000 /* 50ms */ + +#define GEN3_GEN4_EQ_PRESET_INIT 5 + +#define GEN1_CORE_CLK_FREQ 62500000 +#define GEN2_CORE_CLK_FREQ 125000000 +#define GEN3_CORE_CLK_FREQ 250000000 +#define GEN4_CORE_CLK_FREQ 500000000 + +static const unsigned int pcie_gen_freq[] = { + GEN1_CORE_CLK_FREQ, + GEN2_CORE_CLK_FREQ, + GEN3_CORE_CLK_FREQ, + GEN4_CORE_CLK_FREQ +}; + +static const u32 event_cntr_ctrl_offset[] = { + 0x1d8, + 0x1a8, + 0x1a8, + 0x1a8, + 0x1c4, + 0x1d8 +}; + +static const u32 event_cntr_data_offset[] = { + 0x1dc, + 0x1ac, + 0x1ac, + 0x1ac, + 0x1c8, + 0x1dc +}; + +struct tegra_pcie_dw { + struct device *dev; + struct resource *appl_res; + struct resource *dbi_res; + struct resource *atu_dma_res; + void __iomem *appl_base; + struct clk *core_clk; + struct reset_control *core_apb_rst; + struct reset_control *core_rst; + struct dw_pcie pci; + struct tegra_bpmp *bpmp; + + bool supports_clkreq; + bool enable_cdm_check; + bool link_state; + bool update_fc_fixup; + u8 init_link_width; + u32 msi_ctrl_int; + u32 num_lanes; + u32 max_speed; + u32 cid; + u32 cfg_link_cap_l1sub; + u32 pcie_cap_base; + u32 aspm_cmrt; + u32 aspm_pwr_on_t; + u32 aspm_l0s_enter_lat; + + struct regulator *pex_ctl_supply; + struct regulator *slot_ctl_3v3; + struct regulator *slot_ctl_12v; + + unsigned int phy_count; + struct phy **phys; + + struct dentry *debugfs; +}; + +static inline struct tegra_pcie_dw *to_tegra_pcie(struct dw_pcie *pci) +{ + return container_of(pci, struct tegra_pcie_dw, pci); +} + +static inline void appl_writel(struct tegra_pcie_dw *pcie, const u32 value, + const u32 reg) +{ + writel_relaxed(value, pcie->appl_base + reg); +} + +static inline u32 appl_readl(struct tegra_pcie_dw *pcie, const u32 reg) +{ + return readl_relaxed(pcie->appl_base + reg); +} + +struct tegra_pcie_soc { + enum dw_pcie_device_mode mode; +}; + +static void apply_bad_link_workaround(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); + u32 current_link_width; + u16 val; + + /* + * NOTE:- Since this scenario is uncommon and link as such is not + * stable anyway, not waiting to confirm if link is really + * transitioning to Gen-2 speed + */ + val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA); + if (val & PCI_EXP_LNKSTA_LBMS) { + current_link_width = (val & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + if (pcie->init_link_width > current_link_width) { + dev_warn(pci->dev, "PCIe link is bad, width reduced\n"); + val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + + PCI_EXP_LNKCTL2); + val &= ~PCI_EXP_LNKCTL2_TLS; + val |= PCI_EXP_LNKCTL2_TLS_2_5GT; + dw_pcie_writew_dbi(pci, pcie->pcie_cap_base + + PCI_EXP_LNKCTL2, val); + + val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + + PCI_EXP_LNKCTL); + val |= PCI_EXP_LNKCTL_RL; + dw_pcie_writew_dbi(pci, pcie->pcie_cap_base + + PCI_EXP_LNKCTL, val); + } + } +} + +static irqreturn_t tegra_pcie_rp_irq_handler(struct tegra_pcie_dw *pcie) +{ + struct dw_pcie *pci = &pcie->pci; + struct pcie_port *pp = &pci->pp; + u32 val, tmp; + u16 val_w; + + val = appl_readl(pcie, APPL_INTR_STATUS_L0); + if (val & APPL_INTR_STATUS_L0_LINK_STATE_INT) { + val = appl_readl(pcie, APPL_INTR_STATUS_L1_0_0); + if (val & APPL_INTR_STATUS_L1_0_0_LINK_REQ_RST_NOT_CHGED) { + appl_writel(pcie, val, APPL_INTR_STATUS_L1_0_0); + + /* SBR & Surprise Link Down WAR */ + val = appl_readl(pcie, APPL_CAR_RESET_OVRD); + val &= ~APPL_CAR_RESET_OVRD_CYA_OVERRIDE_CORE_RST_N; + appl_writel(pcie, val, APPL_CAR_RESET_OVRD); + udelay(1); + val = appl_readl(pcie, APPL_CAR_RESET_OVRD); + val |= APPL_CAR_RESET_OVRD_CYA_OVERRIDE_CORE_RST_N; + appl_writel(pcie, val, APPL_CAR_RESET_OVRD); + + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_GEN2_CTRL); + val |= PORT_LOGIC_GEN2_CTRL_DIRECT_SPEED_CHANGE; + dw_pcie_writel_dbi(pci, PORT_LOGIC_GEN2_CTRL, val); + } + } + + if (val & APPL_INTR_STATUS_L0_INT_INT) { + val = appl_readl(pcie, APPL_INTR_STATUS_L1_8_0); + if (val & APPL_INTR_STATUS_L1_8_0_AUTO_BW_INT_STS) { + appl_writel(pcie, + APPL_INTR_STATUS_L1_8_0_AUTO_BW_INT_STS, + APPL_INTR_STATUS_L1_8_0); + apply_bad_link_workaround(pp); + } + if (val & APPL_INTR_STATUS_L1_8_0_BW_MGT_INT_STS) { + appl_writel(pcie, + APPL_INTR_STATUS_L1_8_0_BW_MGT_INT_STS, + APPL_INTR_STATUS_L1_8_0); + + val_w = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + + PCI_EXP_LNKSTA); + dev_dbg(pci->dev, "Link Speed : Gen-%u\n", val_w & + PCI_EXP_LNKSTA_CLS); + } + } + + val = appl_readl(pcie, APPL_INTR_STATUS_L0); + if (val & APPL_INTR_STATUS_L0_CDM_REG_CHK_INT) { + val = appl_readl(pcie, APPL_INTR_STATUS_L1_18); + tmp = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS); + if (val & APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMPLT) { + dev_info(pci->dev, "CDM check complete\n"); + tmp |= PCIE_PL_CHK_REG_CHK_REG_COMPLETE; + } + if (val & APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMP_ERR) { + dev_err(pci->dev, "CDM comparison mismatch\n"); + tmp |= PCIE_PL_CHK_REG_CHK_REG_COMPARISON_ERROR; + } + if (val & APPL_INTR_STATUS_L1_18_CDM_REG_CHK_LOGIC_ERR) { + dev_err(pci->dev, "CDM Logic error\n"); + tmp |= PCIE_PL_CHK_REG_CHK_REG_LOGIC_ERROR; + } + dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, tmp); + tmp = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_ERR_ADDR); + dev_err(pci->dev, "CDM Error Address Offset = 0x%08X\n", tmp); + } + + return IRQ_HANDLED; +} + +static irqreturn_t tegra_pcie_irq_handler(int irq, void *arg) +{ + struct tegra_pcie_dw *pcie = arg; + + return tegra_pcie_rp_irq_handler(pcie); +} + +static int tegra_pcie_dw_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + /* + * This is an endpoint mode specific register happen to appear even + * when controller is operating in root port mode and system hangs + * when it is accessed with link being in ASPM-L1 state. + * So skip accessing it altogether + */ + if (where == PORT_LOGIC_MSIX_DOORBELL) { + *val = 0x00000000; + return PCIBIOS_SUCCESSFUL; + } + + return dw_pcie_read(pci->dbi_base + where, size, val); +} + +static int tegra_pcie_dw_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + /* + * This is an endpoint mode specific register happen to appear even + * when controller is operating in root port mode and system hangs + * when it is accessed with link being in ASPM-L1 state. + * So skip accessing it altogether + */ + if (where == PORT_LOGIC_MSIX_DOORBELL) + return PCIBIOS_SUCCESSFUL; + + return dw_pcie_write(pci->dbi_base + where, size, val); +} + +#if defined(CONFIG_PCIEASPM) +static void disable_aspm_l11(struct tegra_pcie_dw *pcie) +{ + u32 val; + + val = dw_pcie_readl_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub); + val &= ~PCI_L1SS_CAP_ASPM_L1_1; + dw_pcie_writel_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub, val); +} + +static void disable_aspm_l12(struct tegra_pcie_dw *pcie) +{ + u32 val; + + val = dw_pcie_readl_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub); + val &= ~PCI_L1SS_CAP_ASPM_L1_2; + dw_pcie_writel_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub, val); +} + +static inline u32 event_counter_prog(struct tegra_pcie_dw *pcie, u32 event) +{ + u32 val; + + val = dw_pcie_readl_dbi(&pcie->pci, event_cntr_ctrl_offset[pcie->cid]); + val &= ~(EVENT_COUNTER_EVENT_SEL_MASK << EVENT_COUNTER_EVENT_SEL_SHIFT); + val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT; + val |= event << EVENT_COUNTER_EVENT_SEL_SHIFT; + val |= EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT; + dw_pcie_writel_dbi(&pcie->pci, event_cntr_ctrl_offset[pcie->cid], val); + val = dw_pcie_readl_dbi(&pcie->pci, event_cntr_data_offset[pcie->cid]); + + return val; +} + +static int aspm_state_cnt(struct seq_file *s, void *data) +{ + struct tegra_pcie_dw *pcie = (struct tegra_pcie_dw *) + dev_get_drvdata(s->private); + u32 val; + + seq_printf(s, "Tx L0s entry count : %u\n", + event_counter_prog(pcie, EVENT_COUNTER_EVENT_Tx_L0S)); + + seq_printf(s, "Rx L0s entry count : %u\n", + event_counter_prog(pcie, EVENT_COUNTER_EVENT_Rx_L0S)); + + seq_printf(s, "Link L1 entry count : %u\n", + event_counter_prog(pcie, EVENT_COUNTER_EVENT_L1)); + + seq_printf(s, "Link L1.1 entry count : %u\n", + event_counter_prog(pcie, EVENT_COUNTER_EVENT_L1_1)); + + seq_printf(s, "Link L1.2 entry count : %u\n", + event_counter_prog(pcie, EVENT_COUNTER_EVENT_L1_2)); + + /* Clear all counters */ + dw_pcie_writel_dbi(&pcie->pci, event_cntr_ctrl_offset[pcie->cid], + EVENT_COUNTER_ALL_CLEAR); + + /* Re-enable counting */ + val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT; + val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT; + dw_pcie_writel_dbi(&pcie->pci, event_cntr_ctrl_offset[pcie->cid], val); + + return 0; +} + +static void init_host_aspm(struct tegra_pcie_dw *pcie) +{ + struct dw_pcie *pci = &pcie->pci; + u32 val; + + val = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS); + pcie->cfg_link_cap_l1sub = val + PCI_L1SS_CAP; + + /* Enable ASPM counters */ + val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT; + val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT; + dw_pcie_writel_dbi(pci, event_cntr_ctrl_offset[pcie->cid], val); + + /* Program T_cmrt and T_pwr_on values */ + val = dw_pcie_readl_dbi(pci, pcie->cfg_link_cap_l1sub); + val &= ~(PCI_L1SS_CAP_CM_RESTORE_TIME | PCI_L1SS_CAP_P_PWR_ON_VALUE); + val |= (pcie->aspm_cmrt << 8); + val |= (pcie->aspm_pwr_on_t << 19); + dw_pcie_writel_dbi(pci, pcie->cfg_link_cap_l1sub, val); + + /* Program L0s and L1 entrance latencies */ + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL); + val &= ~L0S_ENTRANCE_LAT_MASK; + val |= (pcie->aspm_l0s_enter_lat << L0S_ENTRANCE_LAT_SHIFT); + val |= ENTER_ASPM; + dw_pcie_writel_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL, val); +} + +static int init_debugfs(struct tegra_pcie_dw *pcie) +{ + struct dentry *d; + + d = debugfs_create_devm_seqfile(pcie->dev, "aspm_state_cnt", + pcie->debugfs, aspm_state_cnt); + if (IS_ERR_OR_NULL(d)) + dev_err(pcie->dev, + "Failed to create debugfs file \"aspm_state_cnt\"\n"); + + return 0; +} +#else +static inline void disable_aspm_l12(struct tegra_pcie_dw *pcie) { return; } +static inline void disable_aspm_l11(struct tegra_pcie_dw *pcie) { return; } +static inline void init_host_aspm(struct tegra_pcie_dw *pcie) { return; } +static inline int init_debugfs(struct tegra_pcie_dw *pcie) { return 0; } +#endif + +static void tegra_pcie_enable_system_interrupts(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); + u32 val; + u16 val_w; + + val = appl_readl(pcie, APPL_INTR_EN_L0_0); + val |= APPL_INTR_EN_L0_0_LINK_STATE_INT_EN; + appl_writel(pcie, val, APPL_INTR_EN_L0_0); + + val = appl_readl(pcie, APPL_INTR_EN_L1_0_0); + val |= APPL_INTR_EN_L1_0_0_LINK_REQ_RST_NOT_INT_EN; + appl_writel(pcie, val, APPL_INTR_EN_L1_0_0); + + if (pcie->enable_cdm_check) { + val = appl_readl(pcie, APPL_INTR_EN_L0_0); + val |= APPL_INTR_EN_L0_0_CDM_REG_CHK_INT_EN; + appl_writel(pcie, val, APPL_INTR_EN_L0_0); + + val = appl_readl(pcie, APPL_INTR_EN_L1_18); + val |= APPL_INTR_EN_L1_18_CDM_REG_CHK_CMP_ERR; + val |= APPL_INTR_EN_L1_18_CDM_REG_CHK_LOGIC_ERR; + appl_writel(pcie, val, APPL_INTR_EN_L1_18); + } + + val_w = dw_pcie_readw_dbi(&pcie->pci, pcie->pcie_cap_base + + PCI_EXP_LNKSTA); + pcie->init_link_width = (val_w & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + + val_w = dw_pcie_readw_dbi(&pcie->pci, pcie->pcie_cap_base + + PCI_EXP_LNKCTL); + val_w |= PCI_EXP_LNKCTL_LBMIE; + dw_pcie_writew_dbi(&pcie->pci, pcie->pcie_cap_base + PCI_EXP_LNKCTL, + val_w); +} + +static void tegra_pcie_enable_legacy_interrupts(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); + u32 val; + + /* Enable legacy interrupt generation */ + val = appl_readl(pcie, APPL_INTR_EN_L0_0); + val |= APPL_INTR_EN_L0_0_SYS_INTR_EN; + val |= APPL_INTR_EN_L0_0_INT_INT_EN; + appl_writel(pcie, val, APPL_INTR_EN_L0_0); + + val = appl_readl(pcie, APPL_INTR_EN_L1_8_0); + val |= APPL_INTR_EN_L1_8_INTX_EN; + val |= APPL_INTR_EN_L1_8_AUTO_BW_INT_EN; + val |= APPL_INTR_EN_L1_8_BW_MGT_INT_EN; + if (IS_ENABLED(CONFIG_PCIEAER)) + val |= APPL_INTR_EN_L1_8_AER_INT_EN; + appl_writel(pcie, val, APPL_INTR_EN_L1_8_0); +} + +static void tegra_pcie_enable_msi_interrupts(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); + u32 val; + + dw_pcie_msi_init(pp); + + /* Enable MSI interrupt generation */ + val = appl_readl(pcie, APPL_INTR_EN_L0_0); + val |= APPL_INTR_EN_L0_0_SYS_MSI_INTR_EN; + val |= APPL_INTR_EN_L0_0_MSI_RCV_INT_EN; + appl_writel(pcie, val, APPL_INTR_EN_L0_0); +} + +static void tegra_pcie_enable_interrupts(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); + + /* Clear interrupt statuses before enabling interrupts */ + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L0); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_0_0); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_1); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_2); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_3); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_6); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_7); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_8_0); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_9); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_10); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_11); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_13); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_14); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_15); + appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_17); + + tegra_pcie_enable_system_interrupts(pp); + tegra_pcie_enable_legacy_interrupts(pp); + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_enable_msi_interrupts(pp); +} + +static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie) +{ + struct dw_pcie *pci = &pcie->pci; + u32 val, offset, i; + + /* Program init preset */ + for (i = 0; i < pcie->num_lanes; i++) { + dw_pcie_read(pci->dbi_base + CAP_SPCIE_CAP_OFF + + (i * 2), 2, &val); + val &= ~CAP_SPCIE_CAP_OFF_DSP_TX_PRESET0_MASK; + val |= GEN3_GEN4_EQ_PRESET_INIT; + val &= ~CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_MASK; + val |= (GEN3_GEN4_EQ_PRESET_INIT << + CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_SHIFT); + dw_pcie_write(pci->dbi_base + CAP_SPCIE_CAP_OFF + + (i * 2), 2, val); + + offset = dw_pcie_find_ext_capability(pci, + PCI_EXT_CAP_ID_PL_16GT) + + PCI_PL_16GT_LE_CTRL; + dw_pcie_read(pci->dbi_base + offset + i, 1, &val); + val &= ~PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK; + val |= GEN3_GEN4_EQ_PRESET_INIT; + val &= ~PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK; + val |= (GEN3_GEN4_EQ_PRESET_INIT << + PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT); + dw_pcie_write(pci->dbi_base + offset + i, 1, val); + } + + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); + val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); + + val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); + val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_MASK; + val |= (0x3ff << GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_SHIFT); + val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE_MASK; + dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val); + + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); + val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; + val |= (0x1 << GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT); + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); + + val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); + val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_MASK; + val |= (0x360 << GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_SHIFT); + val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE_MASK; + dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val); + + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); + val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); +} + +static void tegra_pcie_prepare_host(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); + u32 val; + + val = dw_pcie_readl_dbi(pci, PCI_IO_BASE); + val &= ~(IO_BASE_IO_DECODE | IO_BASE_IO_DECODE_BIT8); + dw_pcie_writel_dbi(pci, PCI_IO_BASE, val); + + val = dw_pcie_readl_dbi(pci, PCI_PREF_MEMORY_BASE); + val |= CFG_PREF_MEM_LIMIT_BASE_MEM_DECODE; + val |= CFG_PREF_MEM_LIMIT_BASE_MEM_LIMIT_DECODE; + dw_pcie_writel_dbi(pci, PCI_PREF_MEMORY_BASE, val); + + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); + + /* Configure FTS */ + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL); + val &= ~(N_FTS_MASK << N_FTS_SHIFT); + val |= N_FTS_VAL << N_FTS_SHIFT; + dw_pcie_writel_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL, val); + + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_GEN2_CTRL); + val &= ~FTS_MASK; + val |= FTS_VAL; + dw_pcie_writel_dbi(pci, PORT_LOGIC_GEN2_CTRL, val); + + /* Enable as 0xFFFF0001 response for CRS */ + val = dw_pcie_readl_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT); + val &= ~(AMBA_ERROR_RESPONSE_CRS_MASK << AMBA_ERROR_RESPONSE_CRS_SHIFT); + val |= (AMBA_ERROR_RESPONSE_CRS_OKAY_FFFF0001 << + AMBA_ERROR_RESPONSE_CRS_SHIFT); + dw_pcie_writel_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT, val); + + /* Configure Max Speed from DT */ + if (pcie->max_speed && pcie->max_speed != -EINVAL) { + val = dw_pcie_readl_dbi(pci, pcie->pcie_cap_base + + PCI_EXP_LNKCAP); + val &= ~PCI_EXP_LNKCAP_SLS; + val |= pcie->max_speed; + dw_pcie_writel_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP, + val); + } + + /* Configure Max lane width from DT */ + val = dw_pcie_readl_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP); + val &= ~PCI_EXP_LNKCAP_MLW; + val |= (pcie->num_lanes << PCI_EXP_LNKSTA_NLW_SHIFT); + dw_pcie_writel_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP, val); + + config_gen3_gen4_eq_presets(pcie); + + init_host_aspm(pcie); + + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); + val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); + + if (pcie->update_fc_fixup) { + val = dw_pcie_readl_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF); + val |= 0x1 << CFG_TIMER_CTRL_ACK_NAK_SHIFT; + dw_pcie_writel_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF, val); + } + + dw_pcie_setup_rc(pp); + + clk_set_rate(pcie->core_clk, GEN4_CORE_CLK_FREQ); + + /* Assert RST */ + val = appl_readl(pcie, APPL_PINMUX); + val &= ~APPL_PINMUX_PEX_RST; + appl_writel(pcie, val, APPL_PINMUX); + + usleep_range(100, 200); + + /* Enable LTSSM */ + val = appl_readl(pcie, APPL_CTRL); + val |= APPL_CTRL_LTSSM_EN; + appl_writel(pcie, val, APPL_CTRL); + + /* De-assert RST */ + val = appl_readl(pcie, APPL_PINMUX); + val |= APPL_PINMUX_PEX_RST; + appl_writel(pcie, val, APPL_PINMUX); + + msleep(100); +} + +static int tegra_pcie_dw_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); + u32 val, tmp, offset, speed; + + tegra_pcie_prepare_host(pp); + + if (dw_pcie_wait_for_link(pci)) { + /* + * There are some endpoints which can't get the link up if + * root port has Data Link Feature (DLF) enabled. + * Refer Spec rev 4.0 ver 1.0 sec 3.4.2 & 7.7.4 for more info + * on Scaled Flow Control and DLF. + * So, need to confirm that is indeed the case here and attempt + * link up once again with DLF disabled. + */ + val = appl_readl(pcie, APPL_DEBUG); + val &= APPL_DEBUG_LTSSM_STATE_MASK; + val >>= APPL_DEBUG_LTSSM_STATE_SHIFT; + tmp = appl_readl(pcie, APPL_LINK_STATUS); + tmp &= APPL_LINK_STATUS_RDLH_LINK_UP; + if (!(val == 0x11 && !tmp)) { + /* Link is down for all good reasons */ + return 0; + } + + dev_info(pci->dev, "Link is down in DLL"); + dev_info(pci->dev, "Trying again with DLFE disabled\n"); + /* Disable LTSSM */ + val = appl_readl(pcie, APPL_CTRL); + val &= ~APPL_CTRL_LTSSM_EN; + appl_writel(pcie, val, APPL_CTRL); + + reset_control_assert(pcie->core_rst); + reset_control_deassert(pcie->core_rst); + + offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_DLF); + val = dw_pcie_readl_dbi(pci, offset + PCI_DLF_CAP); + val &= ~PCI_DLF_EXCHANGE_ENABLE; + dw_pcie_writel_dbi(pci, offset, val); + + tegra_pcie_prepare_host(pp); + + if (dw_pcie_wait_for_link(pci)) + return 0; + } + + speed = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA) & + PCI_EXP_LNKSTA_CLS; + clk_set_rate(pcie->core_clk, pcie_gen_freq[speed - 1]); + + tegra_pcie_enable_interrupts(pp); + + return 0; +} + +static int tegra_pcie_dw_link_up(struct dw_pcie *pci) +{ + struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); + u32 val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA); + + return !!(val & PCI_EXP_LNKSTA_DLLLA); +} + +static void tegra_pcie_set_msi_vec_num(struct pcie_port *pp) +{ + pp->num_vectors = MAX_MSI_IRQS; +} + +static const struct dw_pcie_ops tegra_dw_pcie_ops = { + .link_up = tegra_pcie_dw_link_up, +}; + +static struct dw_pcie_host_ops tegra_pcie_dw_host_ops = { + .rd_own_conf = tegra_pcie_dw_rd_own_conf, + .wr_own_conf = tegra_pcie_dw_wr_own_conf, + .host_init = tegra_pcie_dw_host_init, + .set_num_vectors = tegra_pcie_set_msi_vec_num, +}; + +static void tegra_pcie_disable_phy(struct tegra_pcie_dw *pcie) +{ + unsigned int phy_count = pcie->phy_count; + + while (phy_count--) { + phy_power_off(pcie->phys[phy_count]); + phy_exit(pcie->phys[phy_count]); + } +} + +static int tegra_pcie_enable_phy(struct tegra_pcie_dw *pcie) +{ + unsigned int i; + int ret; + + for (i = 0; i < pcie->phy_count; i++) { + ret = phy_init(pcie->phys[i]); + if (ret < 0) + goto phy_power_off; + + ret = phy_power_on(pcie->phys[i]); + if (ret < 0) + goto phy_exit; + } + + return 0; + +phy_power_off: + while (i--) { + phy_power_off(pcie->phys[i]); +phy_exit: + phy_exit(pcie->phys[i]); + } + + return ret; +} + +static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie) +{ + struct device_node *np = pcie->dev->of_node; + int ret; + + ret = of_property_read_u32(np, "nvidia,aspm-cmrt-us", &pcie->aspm_cmrt); + if (ret < 0) { + dev_info(pcie->dev, "Failed to read ASPM T_cmrt: %d\n", ret); + return ret; + } + + ret = of_property_read_u32(np, "nvidia,aspm-pwr-on-t-us", + &pcie->aspm_pwr_on_t); + if (ret < 0) + dev_info(pcie->dev, "Failed to read ASPM Power On time: %d\n", + ret); + + ret = of_property_read_u32(np, "nvidia,aspm-l0s-entrance-latency-us", + &pcie->aspm_l0s_enter_lat); + if (ret < 0) + dev_info(pcie->dev, + "Failed to read ASPM L0s Entrance latency: %d\n", ret); + + ret = of_property_read_u32(np, "num-lanes", &pcie->num_lanes); + if (ret < 0) { + dev_err(pcie->dev, "Failed to read num-lanes: %d\n", ret); + return ret; + } + + pcie->max_speed = of_pci_get_max_link_speed(np); + + ret = of_property_read_u32_index(np, "nvidia,bpmp", 1, &pcie->cid); + if (ret) { + dev_err(pcie->dev, "Failed to read Controller-ID: %d\n", ret); + return ret; + } + + ret = of_property_count_strings(np, "phy-names"); + if (ret < 0) { + dev_err(pcie->dev, "Failed to find PHY entries: %d\n", + ret); + return ret; + } + pcie->phy_count = ret; + + if (of_property_read_bool(np, "nvidia,update-fc-fixup")) + pcie->update_fc_fixup = true; + + pcie->supports_clkreq = + of_property_read_bool(pcie->dev->of_node, "supports-clkreq"); + + pcie->enable_cdm_check = + of_property_read_bool(np, "snps,enable-cdm-check"); + + return 0; +} + +static int tegra_pcie_bpmp_set_ctrl_state(struct tegra_pcie_dw *pcie, + bool enable) +{ + struct mrq_uphy_response resp; + struct tegra_bpmp_message msg; + struct mrq_uphy_request req; + + /* Controller-5 doesn't need to have its state set by BPMP-FW */ + if (pcie->cid == 5) + return 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.cmd = CMD_UPHY_PCIE_CONTROLLER_STATE; + req.controller_state.pcie_controller = pcie->cid; + req.controller_state.enable = enable; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_UPHY; + msg.tx.data = &req; + msg.tx.size = sizeof(req); + msg.rx.data = &resp; + msg.rx.size = sizeof(resp); + + return tegra_bpmp_transfer(pcie->bpmp, &msg); +} + +static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie) +{ + struct pcie_port *pp = &pcie->pci.pp; + struct pci_bus *child, *root_bus = NULL; + struct pci_dev *pdev; + + /* + * link doesn't go into L2 state with some of the endpoints with Tegra + * if they are not in D0 state. So, need to make sure that immediate + * downstream devices are in D0 state before sending PME_TurnOff to put + * link into L2 state. + * This is as per PCI Express Base r4.0 v1.0 September 27-2017, + * 5.2 Link State Power Management (Page #428). + */ + + list_for_each_entry(child, &pp->root_bus->children, node) { + /* Bring downstream devices to D0 if they are not already in */ + if (child->parent == pp->root_bus) { + root_bus = child; + break; + } + } + + if (!root_bus) { + dev_err(pcie->dev, "Failed to find downstream devices\n"); + return; + } + + list_for_each_entry(pdev, &root_bus->devices, bus_list) { + if (PCI_SLOT(pdev->devfn) == 0) { + if (pci_set_power_state(pdev, PCI_D0)) + dev_err(pcie->dev, + "Failed to transition %s to D0 state\n", + dev_name(&pdev->dev)); + } + } +} + +static int tegra_pcie_get_slot_regulators(struct tegra_pcie_dw *pcie) +{ + pcie->slot_ctl_3v3 = devm_regulator_get_optional(pcie->dev, "vpcie3v3"); + if (IS_ERR(pcie->slot_ctl_3v3)) { + if (PTR_ERR(pcie->slot_ctl_3v3) != -ENODEV) + return PTR_ERR(pcie->slot_ctl_3v3); + + pcie->slot_ctl_3v3 = NULL; + } + + pcie->slot_ctl_12v = devm_regulator_get_optional(pcie->dev, "vpcie12v"); + if (IS_ERR(pcie->slot_ctl_12v)) { + if (PTR_ERR(pcie->slot_ctl_12v) != -ENODEV) + return PTR_ERR(pcie->slot_ctl_12v); + + pcie->slot_ctl_12v = NULL; + } + + return 0; +} + +static int tegra_pcie_enable_slot_regulators(struct tegra_pcie_dw *pcie) +{ + int ret; + + if (pcie->slot_ctl_3v3) { + ret = regulator_enable(pcie->slot_ctl_3v3); + if (ret < 0) { + dev_err(pcie->dev, + "Failed to enable 3.3V slot supply: %d\n", ret); + return ret; + } + } + + if (pcie->slot_ctl_12v) { + ret = regulator_enable(pcie->slot_ctl_12v); + if (ret < 0) { + dev_err(pcie->dev, + "Failed to enable 12V slot supply: %d\n", ret); + goto fail_12v_enable; + } + } + + /* + * According to PCI Express Card Electromechanical Specification + * Revision 1.1, Table-2.4, T_PVPERL (Power stable to PERST# inactive) + * should be a minimum of 100ms. + */ + if (pcie->slot_ctl_3v3 || pcie->slot_ctl_12v) + msleep(100); + + return 0; + +fail_12v_enable: + if (pcie->slot_ctl_3v3) + regulator_disable(pcie->slot_ctl_3v3); + return ret; +} + +static void tegra_pcie_disable_slot_regulators(struct tegra_pcie_dw *pcie) +{ + if (pcie->slot_ctl_12v) + regulator_disable(pcie->slot_ctl_12v); + if (pcie->slot_ctl_3v3) + regulator_disable(pcie->slot_ctl_3v3); +} + +static int tegra_pcie_config_controller(struct tegra_pcie_dw *pcie, + bool en_hw_hot_rst) +{ + int ret; + u32 val; + + ret = tegra_pcie_bpmp_set_ctrl_state(pcie, true); + if (ret) { + dev_err(pcie->dev, + "Failed to enable controller %u: %d\n", pcie->cid, ret); + return ret; + } + + ret = tegra_pcie_enable_slot_regulators(pcie); + if (ret < 0) + goto fail_slot_reg_en; + + ret = regulator_enable(pcie->pex_ctl_supply); + if (ret < 0) { + dev_err(pcie->dev, "Failed to enable regulator: %d\n", ret); + goto fail_reg_en; + } + + ret = clk_prepare_enable(pcie->core_clk); + if (ret) { + dev_err(pcie->dev, "Failed to enable core clock: %d\n", ret); + goto fail_core_clk; + } + + ret = reset_control_deassert(pcie->core_apb_rst); + if (ret) { + dev_err(pcie->dev, "Failed to deassert core APB reset: %d\n", + ret); + goto fail_core_apb_rst; + } + + if (en_hw_hot_rst) { + /* Enable HW_HOT_RST mode */ + val = appl_readl(pcie, APPL_CTRL); + val &= ~(APPL_CTRL_HW_HOT_RST_MODE_MASK << + APPL_CTRL_HW_HOT_RST_MODE_SHIFT); + val |= APPL_CTRL_HW_HOT_RST_EN; + appl_writel(pcie, val, APPL_CTRL); + } + + ret = tegra_pcie_enable_phy(pcie); + if (ret) { + dev_err(pcie->dev, "Failed to enable PHY: %d\n", ret); + goto fail_phy; + } + + /* Update CFG base address */ + appl_writel(pcie, pcie->dbi_res->start & APPL_CFG_BASE_ADDR_MASK, + APPL_CFG_BASE_ADDR); + + /* Configure this core for RP mode operation */ + appl_writel(pcie, APPL_DM_TYPE_RP, APPL_DM_TYPE); + + appl_writel(pcie, 0x0, APPL_CFG_SLCG_OVERRIDE); + + val = appl_readl(pcie, APPL_CTRL); + appl_writel(pcie, val | APPL_CTRL_SYS_PRE_DET_STATE, APPL_CTRL); + + val = appl_readl(pcie, APPL_CFG_MISC); + val |= (APPL_CFG_MISC_ARCACHE_VAL << APPL_CFG_MISC_ARCACHE_SHIFT); + appl_writel(pcie, val, APPL_CFG_MISC); + + if (!pcie->supports_clkreq) { + val = appl_readl(pcie, APPL_PINMUX); + val |= APPL_PINMUX_CLKREQ_OVERRIDE_EN; + val &= ~APPL_PINMUX_CLKREQ_OVERRIDE; + appl_writel(pcie, val, APPL_PINMUX); + } + + /* Update iATU_DMA base address */ + appl_writel(pcie, + pcie->atu_dma_res->start & APPL_CFG_IATU_DMA_BASE_ADDR_MASK, + APPL_CFG_IATU_DMA_BASE_ADDR); + + reset_control_deassert(pcie->core_rst); + + pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci, + PCI_CAP_ID_EXP); + + /* Disable ASPM-L1SS advertisement as there is no CLKREQ routing */ + if (!pcie->supports_clkreq) { + disable_aspm_l11(pcie); + disable_aspm_l12(pcie); + } + + return ret; + +fail_phy: + reset_control_assert(pcie->core_apb_rst); +fail_core_apb_rst: + clk_disable_unprepare(pcie->core_clk); +fail_core_clk: + regulator_disable(pcie->pex_ctl_supply); +fail_reg_en: + tegra_pcie_disable_slot_regulators(pcie); +fail_slot_reg_en: + tegra_pcie_bpmp_set_ctrl_state(pcie, false); + + return ret; +} + +static int __deinit_controller(struct tegra_pcie_dw *pcie) +{ + int ret; + + ret = reset_control_assert(pcie->core_rst); + if (ret) { + dev_err(pcie->dev, "Failed to assert \"core\" reset: %d\n", + ret); + return ret; + } + + tegra_pcie_disable_phy(pcie); + + ret = reset_control_assert(pcie->core_apb_rst); + if (ret) { + dev_err(pcie->dev, "Failed to assert APB reset: %d\n", ret); + return ret; + } + + clk_disable_unprepare(pcie->core_clk); + + ret = regulator_disable(pcie->pex_ctl_supply); + if (ret) { + dev_err(pcie->dev, "Failed to disable regulator: %d\n", ret); + return ret; + } + + tegra_pcie_disable_slot_regulators(pcie); + + ret = tegra_pcie_bpmp_set_ctrl_state(pcie, false); + if (ret) { + dev_err(pcie->dev, "Failed to disable controller %d: %d\n", + pcie->cid, ret); + return ret; + } + + return ret; +} + +static int tegra_pcie_init_controller(struct tegra_pcie_dw *pcie) +{ + struct dw_pcie *pci = &pcie->pci; + struct pcie_port *pp = &pci->pp; + int ret; + + ret = tegra_pcie_config_controller(pcie, false); + if (ret < 0) + return ret; + + pp->ops = &tegra_pcie_dw_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret < 0) { + dev_err(pcie->dev, "Failed to add PCIe port: %d\n", ret); + goto fail_host_init; + } + + return 0; + +fail_host_init: + return __deinit_controller(pcie); +} + +static int tegra_pcie_try_link_l2(struct tegra_pcie_dw *pcie) +{ + u32 val; + + if (!tegra_pcie_dw_link_up(&pcie->pci)) + return 0; + + val = appl_readl(pcie, APPL_RADM_STATUS); + val |= APPL_PM_XMT_TURNOFF_STATE; + appl_writel(pcie, val, APPL_RADM_STATUS); + + return readl_poll_timeout_atomic(pcie->appl_base + APPL_DEBUG, val, + val & APPL_DEBUG_PM_LINKST_IN_L2_LAT, + 1, PME_ACK_TIMEOUT); +} + +static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie) +{ + u32 data; + int err; + + if (!tegra_pcie_dw_link_up(&pcie->pci)) { + dev_dbg(pcie->dev, "PCIe link is not up...!\n"); + return; + } + + if (tegra_pcie_try_link_l2(pcie)) { + dev_info(pcie->dev, "Link didn't transition to L2 state\n"); + /* + * TX lane clock freq will reset to Gen1 only if link is in L2 + * or detect state. + * So apply pex_rst to end point to force RP to go into detect + * state + */ + data = appl_readl(pcie, APPL_PINMUX); + data &= ~APPL_PINMUX_PEX_RST; + appl_writel(pcie, data, APPL_PINMUX); + + err = readl_poll_timeout_atomic(pcie->appl_base + APPL_DEBUG, + data, + ((data & + APPL_DEBUG_LTSSM_STATE_MASK) >> + APPL_DEBUG_LTSSM_STATE_SHIFT) == + LTSSM_STATE_PRE_DETECT, + 1, LTSSM_TIMEOUT); + if (err) { + dev_info(pcie->dev, "Link didn't go to detect state\n"); + } else { + /* Disable LTSSM after link is in detect state */ + data = appl_readl(pcie, APPL_CTRL); + data &= ~APPL_CTRL_LTSSM_EN; + appl_writel(pcie, data, APPL_CTRL); + } + } + /* + * DBI registers may not be accessible after this as PLL-E would be + * down depending on how CLKREQ is pulled by end point + */ + data = appl_readl(pcie, APPL_PINMUX); + data |= (APPL_PINMUX_CLKREQ_OVERRIDE_EN | APPL_PINMUX_CLKREQ_OVERRIDE); + /* Cut REFCLK to slot */ + data |= APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE_EN; + data &= ~APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE; + appl_writel(pcie, data, APPL_PINMUX); +} + +static int tegra_pcie_deinit_controller(struct tegra_pcie_dw *pcie) +{ + tegra_pcie_downstream_dev_to_D0(pcie); + dw_pcie_host_deinit(&pcie->pci.pp); + tegra_pcie_dw_pme_turnoff(pcie); + + return __deinit_controller(pcie); +} + +static int tegra_pcie_config_rp(struct tegra_pcie_dw *pcie) +{ + struct pcie_port *pp = &pcie->pci.pp; + struct device *dev = pcie->dev; + char *name; + int ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = of_irq_get_byname(dev->of_node, "msi"); + if (!pp->msi_irq) { + dev_err(dev, "Failed to get MSI interrupt\n"); + return -ENODEV; + } + } + + pm_runtime_enable(dev); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to get runtime sync for PCIe dev: %d\n", + ret); + goto fail_pm_get_sync; + } + + ret = pinctrl_pm_select_default_state(dev); + if (ret < 0) { + dev_err(dev, "Failed to configure sideband pins: %d\n", ret); + goto fail_pinctrl; + } + + tegra_pcie_init_controller(pcie); + + pcie->link_state = tegra_pcie_dw_link_up(&pcie->pci); + if (!pcie->link_state) { + ret = -ENOMEDIUM; + goto fail_host_init; + } + + name = devm_kasprintf(dev, GFP_KERNEL, "%pOFP", dev->of_node); + if (!name) { + ret = -ENOMEM; + goto fail_host_init; + } + + pcie->debugfs = debugfs_create_dir(name, NULL); + if (!pcie->debugfs) + dev_err(dev, "Failed to create debugfs\n"); + else + init_debugfs(pcie); + + return ret; + +fail_host_init: + tegra_pcie_deinit_controller(pcie); +fail_pinctrl: + pm_runtime_put_sync(dev); +fail_pm_get_sync: + pm_runtime_disable(dev); + return ret; +} + +static int tegra_pcie_dw_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *atu_dma_res; + struct tegra_pcie_dw *pcie; + struct resource *dbi_res; + struct pcie_port *pp; + struct dw_pcie *pci; + struct phy **phys; + char *name; + int ret; + u32 i; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pci = &pcie->pci; + pci->dev = &pdev->dev; + pci->ops = &tegra_dw_pcie_ops; + pp = &pci->pp; + pcie->dev = &pdev->dev; + + ret = tegra_pcie_dw_parse_dt(pcie); + if (ret < 0) { + dev_err(dev, "Failed to parse device tree: %d\n", ret); + return ret; + } + + ret = tegra_pcie_get_slot_regulators(pcie); + if (ret < 0) { + dev_err(dev, "Failed to get slot regulators: %d\n", ret); + return ret; + } + + pcie->pex_ctl_supply = devm_regulator_get(dev, "vddio-pex-ctl"); + if (IS_ERR(pcie->pex_ctl_supply)) { + ret = PTR_ERR(pcie->pex_ctl_supply); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get regulator: %ld\n", + PTR_ERR(pcie->pex_ctl_supply)); + return ret; + } + + pcie->core_clk = devm_clk_get(dev, "core"); + if (IS_ERR(pcie->core_clk)) { + dev_err(dev, "Failed to get core clock: %ld\n", + PTR_ERR(pcie->core_clk)); + return PTR_ERR(pcie->core_clk); + } + + pcie->appl_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "appl"); + if (!pcie->appl_res) { + dev_err(dev, "Failed to find \"appl\" region\n"); + return -ENODEV; + } + + pcie->appl_base = devm_ioremap_resource(dev, pcie->appl_res); + if (IS_ERR(pcie->appl_base)) + return PTR_ERR(pcie->appl_base); + + pcie->core_apb_rst = devm_reset_control_get(dev, "apb"); + if (IS_ERR(pcie->core_apb_rst)) { + dev_err(dev, "Failed to get APB reset: %ld\n", + PTR_ERR(pcie->core_apb_rst)); + return PTR_ERR(pcie->core_apb_rst); + } + + phys = devm_kcalloc(dev, pcie->phy_count, sizeof(*phys), GFP_KERNEL); + if (!phys) + return -ENOMEM; + + for (i = 0; i < pcie->phy_count; i++) { + name = kasprintf(GFP_KERNEL, "p2u-%u", i); + if (!name) { + dev_err(dev, "Failed to create P2U string\n"); + return -ENOMEM; + } + phys[i] = devm_phy_get(dev, name); + kfree(name); + if (IS_ERR(phys[i])) { + ret = PTR_ERR(phys[i]); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get PHY: %d\n", ret); + return ret; + } + } + + pcie->phys = phys; + + dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + if (!dbi_res) { + dev_err(dev, "Failed to find \"dbi\" region\n"); + return -ENODEV; + } + pcie->dbi_res = dbi_res; + + pci->dbi_base = devm_ioremap_resource(dev, dbi_res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + /* Tegra HW locates DBI2 at a fixed offset from DBI */ + pci->dbi_base2 = pci->dbi_base + 0x1000; + + atu_dma_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "atu_dma"); + if (!atu_dma_res) { + dev_err(dev, "Failed to find \"atu_dma\" region\n"); + return -ENODEV; + } + pcie->atu_dma_res = atu_dma_res; + + pci->atu_base = devm_ioremap_resource(dev, atu_dma_res); + if (IS_ERR(pci->atu_base)) + return PTR_ERR(pci->atu_base); + + pcie->core_rst = devm_reset_control_get(dev, "core"); + if (IS_ERR(pcie->core_rst)) { + dev_err(dev, "Failed to get core reset: %ld\n", + PTR_ERR(pcie->core_rst)); + return PTR_ERR(pcie->core_rst); + } + + pp->irq = platform_get_irq_byname(pdev, "intr"); + if (!pp->irq) { + dev_err(dev, "Failed to get \"intr\" interrupt\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, pp->irq, tegra_pcie_irq_handler, + IRQF_SHARED, "tegra-pcie-intr", pcie); + if (ret) { + dev_err(dev, "Failed to request IRQ %d: %d\n", pp->irq, ret); + return ret; + } + + pcie->bpmp = tegra_bpmp_get(dev); + if (IS_ERR(pcie->bpmp)) + return PTR_ERR(pcie->bpmp); + + platform_set_drvdata(pdev, pcie); + + ret = tegra_pcie_config_rp(pcie); + if (ret && ret != -ENOMEDIUM) + goto fail; + else + return 0; + +fail: + tegra_bpmp_put(pcie->bpmp); + return ret; +} + +static int tegra_pcie_dw_remove(struct platform_device *pdev) +{ + struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev); + + if (!pcie->link_state) + return 0; + + debugfs_remove_recursive(pcie->debugfs); + tegra_pcie_deinit_controller(pcie); + pm_runtime_put_sync(pcie->dev); + pm_runtime_disable(pcie->dev); + tegra_bpmp_put(pcie->bpmp); + + return 0; +} + +static int tegra_pcie_dw_suspend_late(struct device *dev) +{ + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); + u32 val; + + if (!pcie->link_state) + return 0; + + /* Enable HW_HOT_RST mode */ + val = appl_readl(pcie, APPL_CTRL); + val &= ~(APPL_CTRL_HW_HOT_RST_MODE_MASK << + APPL_CTRL_HW_HOT_RST_MODE_SHIFT); + val |= APPL_CTRL_HW_HOT_RST_EN; + appl_writel(pcie, val, APPL_CTRL); + + return 0; +} + +static int tegra_pcie_dw_suspend_noirq(struct device *dev) +{ + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); + + if (!pcie->link_state) + return 0; + + /* Save MSI interrupt vector */ + pcie->msi_ctrl_int = dw_pcie_readl_dbi(&pcie->pci, + PORT_LOGIC_MSI_CTRL_INT_0_EN); + tegra_pcie_downstream_dev_to_D0(pcie); + tegra_pcie_dw_pme_turnoff(pcie); + + return __deinit_controller(pcie); +} + +static int tegra_pcie_dw_resume_noirq(struct device *dev) +{ + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); + int ret; + + if (!pcie->link_state) + return 0; + + ret = tegra_pcie_config_controller(pcie, true); + if (ret < 0) + return ret; + + ret = tegra_pcie_dw_host_init(&pcie->pci.pp); + if (ret < 0) { + dev_err(dev, "Failed to init host: %d\n", ret); + goto fail_host_init; + } + + /* Restore MSI interrupt vector */ + dw_pcie_writel_dbi(&pcie->pci, PORT_LOGIC_MSI_CTRL_INT_0_EN, + pcie->msi_ctrl_int); + + return 0; + +fail_host_init: + return __deinit_controller(pcie); +} + +static int tegra_pcie_dw_resume_early(struct device *dev) +{ + struct tegra_pcie_dw *pcie = dev_get_drvdata(dev); + u32 val; + + if (!pcie->link_state) + return 0; + + /* Disable HW_HOT_RST mode */ + val = appl_readl(pcie, APPL_CTRL); + val &= ~(APPL_CTRL_HW_HOT_RST_MODE_MASK << + APPL_CTRL_HW_HOT_RST_MODE_SHIFT); + val |= APPL_CTRL_HW_HOT_RST_MODE_IMDT_RST << + APPL_CTRL_HW_HOT_RST_MODE_SHIFT; + val &= ~APPL_CTRL_HW_HOT_RST_EN; + appl_writel(pcie, val, APPL_CTRL); + + return 0; +} + +static void tegra_pcie_dw_shutdown(struct platform_device *pdev) +{ + struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev); + + if (!pcie->link_state) + return; + + debugfs_remove_recursive(pcie->debugfs); + tegra_pcie_downstream_dev_to_D0(pcie); + + disable_irq(pcie->pci.pp.irq); + if (IS_ENABLED(CONFIG_PCI_MSI)) + disable_irq(pcie->pci.pp.msi_irq); + + tegra_pcie_dw_pme_turnoff(pcie); + __deinit_controller(pcie); +} + +static const struct of_device_id tegra_pcie_dw_of_match[] = { + { + .compatible = "nvidia,tegra194-pcie", + }, + {}, +}; + +static const struct dev_pm_ops tegra_pcie_dw_pm_ops = { + .suspend_late = tegra_pcie_dw_suspend_late, + .suspend_noirq = tegra_pcie_dw_suspend_noirq, + .resume_noirq = tegra_pcie_dw_resume_noirq, + .resume_early = tegra_pcie_dw_resume_early, +}; + +static struct platform_driver tegra_pcie_dw_driver = { + .probe = tegra_pcie_dw_probe, + .remove = tegra_pcie_dw_remove, + .shutdown = tegra_pcie_dw_shutdown, + .driver = { + .name = "tegra194-pcie", + .pm = &tegra_pcie_dw_pm_ops, + .of_match_table = tegra_pcie_dw_of_match, + }, +}; +module_platform_driver(tegra_pcie_dw_driver); + +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"); diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c index 3f30ee4a00b3..a5401a0b1e58 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier.c +++ b/drivers/pci/controller/dwc/pcie-uniphier.c @@ -9,11 +9,11 @@ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/init.h> #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> -#include <linux/module.h> #include <linux/of_irq.h> #include <linux/pci.h> #include <linux/phy/phy.h> @@ -33,6 +33,10 @@ #define PCL_PIPEMON 0x0044 #define PCL_PCLK_ALIVE BIT(15) +#define PCL_MODE 0x8000 +#define PCL_MODE_REGEN BIT(8) +#define PCL_MODE_REGVAL BIT(0) + #define PCL_APP_READY_CTRL 0x8008 #define PCL_APP_LTSSM_ENABLE BIT(0) @@ -85,6 +89,12 @@ static void uniphier_pcie_init_rc(struct uniphier_pcie_priv *priv) { u32 val; + /* set RC MODE */ + val = readl(priv->base + PCL_MODE); + val |= PCL_MODE_REGEN; + val &= ~PCL_MODE_REGVAL; + writel(val, priv->base + PCL_MODE); + /* use auxiliary power detection */ val = readl(priv->base + PCL_APP_PM0); val |= PCL_SYS_AUX_PWR_DET; @@ -161,12 +171,6 @@ static void uniphier_pcie_irq_enable(struct uniphier_pcie_priv *priv) writel(PCL_RCV_INTX_ALL_ENABLE, priv->base + PCL_RCV_INTX); } -static void uniphier_pcie_irq_disable(struct uniphier_pcie_priv *priv) -{ - writel(0, priv->base + PCL_RCV_INT); - writel(0, priv->base + PCL_RCV_INTX); -} - static void uniphier_pcie_irq_ack(struct irq_data *d) { struct pcie_port *pp = irq_data_get_irq_chip_data(d); @@ -387,14 +391,6 @@ out_clk_disable: return ret; } -static void uniphier_pcie_host_disable(struct uniphier_pcie_priv *priv) -{ - uniphier_pcie_irq_disable(priv); - phy_exit(priv->phy); - reset_control_assert(priv->rst); - clk_disable_unprepare(priv->clk); -} - static const struct dw_pcie_ops dw_pcie_ops = { .start_link = uniphier_pcie_establish_link, .stop_link = uniphier_pcie_stop_link, @@ -446,31 +442,16 @@ static int uniphier_pcie_probe(struct platform_device *pdev) return uniphier_add_pcie_port(priv, pdev); } -static int uniphier_pcie_remove(struct platform_device *pdev) -{ - struct uniphier_pcie_priv *priv = platform_get_drvdata(pdev); - - uniphier_pcie_host_disable(priv); - - return 0; -} - static const struct of_device_id uniphier_pcie_match[] = { { .compatible = "socionext,uniphier-pcie", }, { /* sentinel */ }, }; -MODULE_DEVICE_TABLE(of, uniphier_pcie_match); static struct platform_driver uniphier_pcie_driver = { .probe = uniphier_pcie_probe, - .remove = uniphier_pcie_remove, .driver = { .name = "uniphier-pcie", .of_match_table = uniphier_pcie_match, }, }; builtin_platform_driver(uniphier_pcie_driver); - -MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); -MODULE_DESCRIPTION("UniPhier PCIe host controller driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index fc0fe4d4de49..2a20b649f40c 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -16,6 +16,7 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/msi.h> #include <linux/of_address.h> #include <linux/of_pci.h> @@ -175,18 +176,20 @@ (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where)) -#define PIO_TIMEOUT_MS 1 +#define PIO_RETRY_CNT 500 +#define PIO_RETRY_DELAY 2 /* 2 us*/ #define LINK_WAIT_MAX_RETRIES 10 #define LINK_WAIT_USLEEP_MIN 90000 #define LINK_WAIT_USLEEP_MAX 100000 +#define RETRAIN_WAIT_MAX_RETRIES 10 +#define RETRAIN_WAIT_USLEEP_US 2000 #define MSI_IRQ_NUM 32 struct advk_pcie { struct platform_device *pdev; void __iomem *base; - struct list_head resources; struct irq_domain *irq_domain; struct irq_chip irq_chip; struct irq_domain *msi_domain; @@ -239,6 +242,17 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie) return -ETIMEDOUT; } +static void advk_pcie_wait_for_retrain(struct advk_pcie *pcie) +{ + size_t retries; + + for (retries = 0; retries < RETRAIN_WAIT_MAX_RETRIES; ++retries) { + if (!advk_pcie_link_up(pcie)) + break; + udelay(RETRAIN_WAIT_USLEEP_US); + } +} + static void advk_pcie_setup_hw(struct advk_pcie *pcie) { u32 reg; @@ -324,6 +338,14 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) reg |= PIO_CTRL_ADDR_WIN_DISABLE; advk_writel(pcie, reg, PIO_CTRL); + /* + * PERST# signal could have been asserted by pinctrl subsystem before + * probe() callback has been called, making the endpoint going into + * fundamental reset. As required by PCI Express spec a delay for at + * least 100ms after such a reset before link training is needed. + */ + msleep(PCI_PM_D3COLD_WAIT); + /* Start link training */ reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); reg |= PCIE_CORE_LINK_TRAINING; @@ -383,17 +405,16 @@ static void advk_pcie_check_pio_status(struct advk_pcie *pcie) static int advk_pcie_wait_pio(struct advk_pcie *pcie) { struct device *dev = &pcie->pdev->dev; - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS); + int i; - while (time_before(jiffies, timeout)) { + for (i = 0; i < PIO_RETRY_CNT; i++) { u32 start, isr; start = advk_readl(pcie, PIO_START); isr = advk_readl(pcie, PIO_ISR); if (!start && isr) return 0; + udelay(PIO_RETRY_DELAY); } dev_err(dev, "config read/write timed out\n"); @@ -415,7 +436,7 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, case PCI_EXP_RTCTL: { u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG); - *value = (val & PCIE_MSG_PM_PME_MASK) ? PCI_EXP_RTCTL_PMEIE : 0; + *value = (val & PCIE_MSG_PM_PME_MASK) ? 0 : PCI_EXP_RTCTL_PMEIE; return PCI_BRIDGE_EMUL_HANDLED; } @@ -426,11 +447,20 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, return PCI_BRIDGE_EMUL_HANDLED; } + case PCI_EXP_LNKCTL: { + /* u32 contains both PCI_EXP_LNKCTL and PCI_EXP_LNKSTA */ + u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg) & + ~(PCI_EXP_LNKSTA_LT << 16); + if (!advk_pcie_link_up(pcie)) + val |= (PCI_EXP_LNKSTA_LT << 16); + *value = val; + return PCI_BRIDGE_EMUL_HANDLED; + } + case PCI_CAP_LIST_ID: case PCI_EXP_DEVCAP: case PCI_EXP_DEVCTL: case PCI_EXP_LNKCAP: - case PCI_EXP_LNKCTL: *value = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg); return PCI_BRIDGE_EMUL_HANDLED; default: @@ -447,14 +477,24 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, switch (reg) { case PCI_EXP_DEVCTL: + advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg); + break; + case PCI_EXP_LNKCTL: advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg); + if (new & PCI_EXP_LNKCTL_RL) + advk_pcie_wait_for_retrain(pcie); break; - case PCI_EXP_RTCTL: - new = (new & PCI_EXP_RTCTL_PMEIE) << 3; - advk_writel(pcie, new, PCIE_ISR0_MASK_REG); + case PCI_EXP_RTCTL: { + /* Only mask/unmask PME interrupt */ + u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG) & + ~PCIE_MSG_PM_PME_MASK; + if ((new & PCI_EXP_RTCTL_PMEIE) == 0) + val |= PCIE_MSG_PM_PME_MASK; + advk_writel(pcie, val, PCIE_ISR0_MASK_REG); break; + } case PCI_EXP_RTSTA: new = (new & PCI_EXP_RTSTA_PME) >> 9; @@ -479,18 +519,20 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie) { struct pci_bridge_emul *bridge = &pcie->bridge; - bridge->conf.vendor = advk_readl(pcie, PCIE_CORE_DEV_ID_REG) & 0xffff; - bridge->conf.device = advk_readl(pcie, PCIE_CORE_DEV_ID_REG) >> 16; + bridge->conf.vendor = + cpu_to_le16(advk_readl(pcie, PCIE_CORE_DEV_ID_REG) & 0xffff); + bridge->conf.device = + cpu_to_le16(advk_readl(pcie, PCIE_CORE_DEV_ID_REG) >> 16); bridge->conf.class_revision = - advk_readl(pcie, PCIE_CORE_DEV_REV_REG) & 0xff; + cpu_to_le32(advk_readl(pcie, PCIE_CORE_DEV_REV_REG) & 0xff); /* Support 32 bits I/O addressing */ bridge->conf.iobase = PCI_IO_RANGE_TYPE_32; bridge->conf.iolimit = PCI_IO_RANGE_TYPE_32; /* Support 64 bits memory pref */ - bridge->conf.pref_mem_base = PCI_PREF_RANGE_TYPE_64; - bridge->conf.pref_mem_limit = PCI_PREF_RANGE_TYPE_64; + bridge->conf.pref_mem_base = cpu_to_le16(PCI_PREF_RANGE_TYPE_64); + bridge->conf.pref_mem_limit = cpu_to_le16(PCI_PREF_RANGE_TYPE_64); /* Support interrupt A for MSI feature */ bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE; @@ -910,63 +952,11 @@ static irqreturn_t advk_pcie_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie) -{ - int err, res_valid = 0; - struct device *dev = &pcie->pdev->dev; - struct resource_entry *win, *tmp; - resource_size_t iobase; - - INIT_LIST_HEAD(&pcie->resources); - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &pcie->resources, &iobase); - if (err) - return err; - - err = devm_request_pci_bus_resources(dev, &pcie->resources); - if (err) - goto out_release_res; - - resource_list_for_each_entry_safe(win, tmp, &pcie->resources) { - struct resource *res = win->res; - - switch (resource_type(res)) { - case IORESOURCE_IO: - err = devm_pci_remap_iospace(dev, res, iobase); - if (err) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - err, res); - resource_list_destroy_entry(win); - } - break; - case IORESOURCE_MEM: - res_valid |= !(res->flags & IORESOURCE_PREFETCH); - break; - case IORESOURCE_BUS: - pcie->root_bus_nr = res->start; - break; - } - } - - if (!res_valid) { - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; - goto out_release_res; - } - - return 0; - -out_release_res: - pci_free_resource_list(&pcie->resources); - return err; -} - static int advk_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct advk_pcie *pcie; - struct resource *res; + struct resource *res, *bus; struct pci_host_bridge *bridge; int ret, irq; @@ -991,11 +981,13 @@ static int advk_pcie_probe(struct platform_device *pdev) return ret; } - ret = advk_pcie_parse_request_of_pci_ranges(pcie); + ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, &bus); if (ret) { dev_err(dev, "Failed to parse resources\n"); return ret; } + pcie->root_bus_nr = bus->start; advk_pcie_setup_hw(pcie); @@ -1014,7 +1006,6 @@ static int advk_pcie_probe(struct platform_device *pdev) return ret; } - list_splice_init(&pcie->resources, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = 0; diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c index bf5ece5d9291..1b67564de7af 100644 --- a/drivers/pci/controller/pci-ftpci100.c +++ b/drivers/pci/controller/pci-ftpci100.c @@ -375,12 +375,11 @@ static int faraday_pci_setup_cascaded_irq(struct faraday_pci *p) return 0; } -static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, - struct device_node *np) +static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p) { - struct of_pci_range range; - struct of_pci_range_parser parser; struct device *dev = p->dev; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(p); + struct resource_entry *entry; u32 confreg[3] = { FARADAY_PCI_MEM1_BASE_SIZE, FARADAY_PCI_MEM2_BASE_SIZE, @@ -389,19 +388,13 @@ static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, int i = 0; u32 val; - if (of_pci_dma_range_parser_init(&parser, np)) { - dev_err(dev, "missing dma-ranges property\n"); - return -EINVAL; - } - - /* - * Get the dma-ranges from the device tree - */ - for_each_of_pci_range(&parser, &range) { - u64 end = range.pci_addr + range.size - 1; + resource_list_for_each_entry(entry, &bridge->dma_ranges) { + u64 pci_addr = entry->res->start - entry->offset; + u64 end = entry->res->end - entry->offset; int ret; - ret = faraday_res_to_memcfg(range.pci_addr, range.size, &val); + ret = faraday_res_to_memcfg(pci_addr, + resource_size(entry->res), &val); if (ret) { dev_err(dev, "DMA range %d: illegal MEM resource size\n", i); @@ -409,7 +402,7 @@ static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, } dev_info(dev, "DMA MEM%d BASE: 0x%016llx -> 0x%016llx config %08x\n", - i + 1, range.pci_addr, end, val); + i + 1, pci_addr, end, val); if (i <= 2) { faraday_raw_pci_write_config(p, 0, 0, confreg[i], 4, val); @@ -430,10 +423,8 @@ static int faraday_pci_probe(struct platform_device *pdev) const struct faraday_pci_variant *variant = of_device_get_match_data(dev); struct resource *regs; - resource_size_t io_base; struct resource_entry *win; struct faraday_pci *p; - struct resource *mem; struct resource *io; struct pci_host_bridge *host; struct clk *clk; @@ -441,7 +432,6 @@ static int faraday_pci_probe(struct platform_device *pdev) unsigned char cur_bus_speed = PCI_SPEED_33MHz; int ret; u32 val; - LIST_HEAD(res); host = devm_pci_alloc_host_bridge(dev, sizeof(*p)); if (!host) @@ -480,44 +470,21 @@ static int faraday_pci_probe(struct platform_device *pdev) if (IS_ERR(p->base)) return PTR_ERR(p->base); - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &res, &io_base); - if (ret) - return ret; - - ret = devm_request_pci_bus_resources(dev, &res); + ret = pci_parse_request_of_pci_ranges(dev, &host->windows, + &host->dma_ranges, NULL); if (ret) return ret; - /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry(win, &res) { - switch (resource_type(win->res)) { - case IORESOURCE_IO: - io = win->res; - io->name = "Gemini PCI I/O"; - if (!faraday_res_to_memcfg(io->start - win->offset, - resource_size(io), &val)) { - /* setup I/O space size */ - writel(val, p->base + PCI_IOSIZE); - } else { - dev_err(dev, "illegal IO mem size\n"); - return -EINVAL; - } - ret = devm_pci_remap_iospace(dev, io, io_base); - if (ret) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - ret, io); - continue; - } - break; - case IORESOURCE_MEM: - mem = win->res; - mem->name = "Gemini PCI MEM"; - break; - case IORESOURCE_BUS: - break; - default: - break; + win = resource_list_first_type(&host->windows, IORESOURCE_IO); + if (win) { + io = win->res; + if (!faraday_res_to_memcfg(io->start - win->offset, + resource_size(io), &val)) { + /* setup I/O space size */ + writel(val, p->base + PCI_IOSIZE); + } else { + dev_err(dev, "illegal IO mem size\n"); + return -EINVAL; } } @@ -565,11 +532,10 @@ static int faraday_pci_probe(struct platform_device *pdev) cur_bus_speed = PCI_SPEED_66MHz; } - ret = faraday_pci_parse_map_dma_ranges(p, dev->of_node); + ret = faraday_pci_parse_map_dma_ranges(p); if (ret) return ret; - list_splice_init(&res, &host->windows); ret = pci_scan_root_bus_bridge(host); if (ret) { dev_err(dev, "failed to scan host: %d\n", ret); @@ -581,7 +547,6 @@ static int faraday_pci_probe(struct platform_device *pdev) pci_bus_assign_resources(p->bus); pci_bus_add_devices(p->bus); - pci_free_resource_list(&res); return 0; } diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index c742881b5061..250a3fc80ec6 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -27,7 +27,7 @@ static struct pci_config_window *gen_pci_init(struct device *dev, struct pci_config_window *cfg; /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); + err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range); if (err) return ERR_PTR(err); @@ -43,9 +43,8 @@ static struct pci_config_window *gen_pci_init(struct device *dev, goto err_out; } - err = devm_add_action(dev, gen_pci_unmap_cfg, cfg); + err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg); if (err) { - gen_pci_unmap_cfg(cfg); goto err_out; } return cfg; diff --git a/drivers/pci/controller/pci-hyperv-intf.c b/drivers/pci/controller/pci-hyperv-intf.c new file mode 100644 index 000000000000..cc96be450360 --- /dev/null +++ b/drivers/pci/controller/pci-hyperv-intf.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Microsoft Corporation. + * + * Author: + * Haiyang Zhang <haiyangz@microsoft.com> + * + * This small module is a helper driver allows other drivers to + * have a common interface with the Hyper-V PCI frontend driver. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/hyperv.h> + +struct hyperv_pci_block_ops hvpci_block_ops; +EXPORT_SYMBOL_GPL(hvpci_block_ops); + +int hyperv_read_cfg_blk(struct pci_dev *dev, void *buf, unsigned int buf_len, + unsigned int block_id, unsigned int *bytes_returned) +{ + if (!hvpci_block_ops.read_block) + return -EOPNOTSUPP; + + return hvpci_block_ops.read_block(dev, buf, buf_len, block_id, + bytes_returned); +} +EXPORT_SYMBOL_GPL(hyperv_read_cfg_blk); + +int hyperv_write_cfg_blk(struct pci_dev *dev, void *buf, unsigned int len, + unsigned int block_id) +{ + if (!hvpci_block_ops.write_block) + return -EOPNOTSUPP; + + return hvpci_block_ops.write_block(dev, buf, len, block_id); +} +EXPORT_SYMBOL_GPL(hyperv_write_cfg_blk); + +int hyperv_reg_block_invalidate(struct pci_dev *dev, void *context, + void (*block_invalidate)(void *context, + u64 block_mask)) +{ + if (!hvpci_block_ops.reg_blk_invalidate) + return -EOPNOTSUPP; + + return hvpci_block_ops.reg_blk_invalidate(dev, context, + block_invalidate); +} +EXPORT_SYMBOL_GPL(hyperv_reg_block_invalidate); + +static void __exit exit_hv_pci_intf(void) +{ +} + +static int __init init_hv_pci_intf(void) +{ + return 0; +} + +module_init(init_hv_pci_intf); +module_exit(exit_hv_pci_intf); + +MODULE_DESCRIPTION("Hyper-V PCI Interface"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 40b625458afa..9977abff92fc 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -76,11 +76,6 @@ static enum pci_protocol_version_t pci_protocol_versions[] = { PCI_PROTOCOL_VERSION_1_1, }; -/* - * Protocol version negotiated by hv_pci_protocol_negotiation(). - */ -static enum pci_protocol_version_t pci_protocol_version; - #define PCI_CONFIG_MMIO_LENGTH 0x2000 #define CFG_PAGE_OFFSET 0x1000 #define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET) @@ -307,7 +302,7 @@ struct pci_bus_relations { struct pci_q_res_req_response { struct vmpacket_descriptor hdr; s32 status; /* negative values are failures */ - u32 probed_bar[6]; + u32 probed_bar[PCI_STD_NUM_BARS]; } __packed; struct pci_set_power { @@ -365,6 +360,39 @@ struct pci_delete_interrupt { struct tran_int_desc int_desc; } __packed; +/* + * Note: the VM must pass a valid block id, wslot and bytes_requested. + */ +struct pci_read_block { + struct pci_message message_type; + u32 block_id; + union win_slot_encoding wslot; + u32 bytes_requested; +} __packed; + +struct pci_read_block_response { + struct vmpacket_descriptor hdr; + u32 status; + u8 bytes[HV_CONFIG_BLOCK_SIZE_MAX]; +} __packed; + +/* + * Note: the VM must pass a valid block id, wslot and byte_count. + */ +struct pci_write_block { + struct pci_message message_type; + u32 block_id; + union win_slot_encoding wslot; + u32 byte_count; + u8 bytes[HV_CONFIG_BLOCK_SIZE_MAX]; +} __packed; + +struct pci_dev_inval_block { + struct pci_incoming_message incoming; + union win_slot_encoding wslot; + u64 block_mask; +} __packed; + struct pci_dev_incoming { struct pci_incoming_message incoming; union win_slot_encoding wslot; @@ -422,12 +450,15 @@ enum hv_pcibus_state { hv_pcibus_init = 0, hv_pcibus_probed, hv_pcibus_installed, + hv_pcibus_removing, hv_pcibus_removed, hv_pcibus_maximum }; struct hv_pcibus_device { struct pci_sysdata sysdata; + /* Protocol version negotiated with the host */ + enum pci_protocol_version_t protocol_version; enum hv_pcibus_state state; refcount_t remove_lock; struct hv_device *hdev; @@ -499,11 +530,14 @@ struct hv_pci_dev { struct hv_pcibus_device *hbus; struct work_struct wrk; + void (*block_invalidate)(void *context, u64 block_mask); + void *invalidate_context; + /* * What would be observed if one wrote 0xFFFFFFFF to a BAR and then * read it back, for each of the BAR offsets within config space. */ - u32 probed_bar[6]; + u32 probed_bar[PCI_STD_NUM_BARS]; }; struct hv_pci_compl { @@ -817,6 +851,253 @@ static struct pci_ops hv_pcifront_ops = { .write = hv_pcifront_write_config, }; +/* + * Paravirtual backchannel + * + * Hyper-V SR-IOV provides a backchannel mechanism in software for + * communication between a VF driver and a PF driver. These + * "configuration blocks" are similar in concept to PCI configuration space, + * but instead of doing reads and writes in 32-bit chunks through a very slow + * path, packets of up to 128 bytes can be sent or received asynchronously. + * + * Nearly every SR-IOV device contains just such a communications channel in + * hardware, so using this one in software is usually optional. Using the + * software channel, however, allows driver implementers to leverage software + * tools that fuzz the communications channel looking for vulnerabilities. + * + * The usage model for these packets puts the responsibility for reading or + * writing on the VF driver. The VF driver sends a read or a write packet, + * indicating which "block" is being referred to by number. + * + * If the PF driver wishes to initiate communication, it can "invalidate" one or + * more of the first 64 blocks. This invalidation is delivered via a callback + * supplied by the VF driver by this driver. + * + * No protocol is implied, except that supplied by the PF and VF drivers. + */ + +struct hv_read_config_compl { + struct hv_pci_compl comp_pkt; + void *buf; + unsigned int len; + unsigned int bytes_returned; +}; + +/** + * hv_pci_read_config_compl() - Invoked when a response packet + * for a read config block operation arrives. + * @context: Identifies the read config operation + * @resp: The response packet itself + * @resp_packet_size: Size in bytes of the response packet + */ +static void hv_pci_read_config_compl(void *context, struct pci_response *resp, + int resp_packet_size) +{ + struct hv_read_config_compl *comp = context; + struct pci_read_block_response *read_resp = + (struct pci_read_block_response *)resp; + unsigned int data_len, hdr_len; + + hdr_len = offsetof(struct pci_read_block_response, bytes); + if (resp_packet_size < hdr_len) { + comp->comp_pkt.completion_status = -1; + goto out; + } + + data_len = resp_packet_size - hdr_len; + if (data_len > 0 && read_resp->status == 0) { + comp->bytes_returned = min(comp->len, data_len); + memcpy(comp->buf, read_resp->bytes, comp->bytes_returned); + } else { + comp->bytes_returned = 0; + } + + comp->comp_pkt.completion_status = read_resp->status; +out: + complete(&comp->comp_pkt.host_event); +} + +/** + * hv_read_config_block() - Sends a read config block request to + * the back-end driver running in the Hyper-V parent partition. + * @pdev: The PCI driver's representation for this device. + * @buf: Buffer into which the config block will be copied. + * @len: Size in bytes of buf. + * @block_id: Identifies the config block which has been requested. + * @bytes_returned: Size which came back from the back-end driver. + * + * Return: 0 on success, -errno on failure + */ +int hv_read_config_block(struct pci_dev *pdev, void *buf, unsigned int len, + unsigned int block_id, unsigned int *bytes_returned) +{ + struct hv_pcibus_device *hbus = + container_of(pdev->bus->sysdata, struct hv_pcibus_device, + sysdata); + struct { + struct pci_packet pkt; + char buf[sizeof(struct pci_read_block)]; + } pkt; + struct hv_read_config_compl comp_pkt; + struct pci_read_block *read_blk; + int ret; + + if (len == 0 || len > HV_CONFIG_BLOCK_SIZE_MAX) + return -EINVAL; + + init_completion(&comp_pkt.comp_pkt.host_event); + comp_pkt.buf = buf; + comp_pkt.len = len; + + memset(&pkt, 0, sizeof(pkt)); + pkt.pkt.completion_func = hv_pci_read_config_compl; + pkt.pkt.compl_ctxt = &comp_pkt; + read_blk = (struct pci_read_block *)&pkt.pkt.message; + read_blk->message_type.type = PCI_READ_BLOCK; + read_blk->wslot.slot = devfn_to_wslot(pdev->devfn); + read_blk->block_id = block_id; + read_blk->bytes_requested = len; + + ret = vmbus_sendpacket(hbus->hdev->channel, read_blk, + sizeof(*read_blk), (unsigned long)&pkt.pkt, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret) + return ret; + + ret = wait_for_response(hbus->hdev, &comp_pkt.comp_pkt.host_event); + if (ret) + return ret; + + if (comp_pkt.comp_pkt.completion_status != 0 || + comp_pkt.bytes_returned == 0) { + dev_err(&hbus->hdev->device, + "Read Config Block failed: 0x%x, bytes_returned=%d\n", + comp_pkt.comp_pkt.completion_status, + comp_pkt.bytes_returned); + return -EIO; + } + + *bytes_returned = comp_pkt.bytes_returned; + return 0; +} + +/** + * hv_pci_write_config_compl() - Invoked when a response packet for a write + * config block operation arrives. + * @context: Identifies the write config operation + * @resp: The response packet itself + * @resp_packet_size: Size in bytes of the response packet + */ +static void hv_pci_write_config_compl(void *context, struct pci_response *resp, + int resp_packet_size) +{ + struct hv_pci_compl *comp_pkt = context; + + comp_pkt->completion_status = resp->status; + complete(&comp_pkt->host_event); +} + +/** + * hv_write_config_block() - Sends a write config block request to the + * back-end driver running in the Hyper-V parent partition. + * @pdev: The PCI driver's representation for this device. + * @buf: Buffer from which the config block will be copied. + * @len: Size in bytes of buf. + * @block_id: Identifies the config block which is being written. + * + * Return: 0 on success, -errno on failure + */ +int hv_write_config_block(struct pci_dev *pdev, void *buf, unsigned int len, + unsigned int block_id) +{ + struct hv_pcibus_device *hbus = + container_of(pdev->bus->sysdata, struct hv_pcibus_device, + sysdata); + struct { + struct pci_packet pkt; + char buf[sizeof(struct pci_write_block)]; + u32 reserved; + } pkt; + struct hv_pci_compl comp_pkt; + struct pci_write_block *write_blk; + u32 pkt_size; + int ret; + + if (len == 0 || len > HV_CONFIG_BLOCK_SIZE_MAX) + return -EINVAL; + + init_completion(&comp_pkt.host_event); + + memset(&pkt, 0, sizeof(pkt)); + pkt.pkt.completion_func = hv_pci_write_config_compl; + pkt.pkt.compl_ctxt = &comp_pkt; + write_blk = (struct pci_write_block *)&pkt.pkt.message; + write_blk->message_type.type = PCI_WRITE_BLOCK; + write_blk->wslot.slot = devfn_to_wslot(pdev->devfn); + write_blk->block_id = block_id; + write_blk->byte_count = len; + memcpy(write_blk->bytes, buf, len); + pkt_size = offsetof(struct pci_write_block, bytes) + len; + /* + * This quirk is required on some hosts shipped around 2018, because + * these hosts don't check the pkt_size correctly (new hosts have been + * fixed since early 2019). The quirk is also safe on very old hosts + * and new hosts, because, on them, what really matters is the length + * specified in write_blk->byte_count. + */ + pkt_size += sizeof(pkt.reserved); + + ret = vmbus_sendpacket(hbus->hdev->channel, write_blk, pkt_size, + (unsigned long)&pkt.pkt, VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret) + return ret; + + ret = wait_for_response(hbus->hdev, &comp_pkt.host_event); + if (ret) + return ret; + + if (comp_pkt.completion_status != 0) { + dev_err(&hbus->hdev->device, + "Write Config Block failed: 0x%x\n", + comp_pkt.completion_status); + return -EIO; + } + + return 0; +} + +/** + * hv_register_block_invalidate() - Invoked when a config block invalidation + * arrives from the back-end driver. + * @pdev: The PCI driver's representation for this device. + * @context: Identifies the device. + * @block_invalidate: Identifies all of the blocks being invalidated. + * + * Return: 0 on success, -errno on failure + */ +int hv_register_block_invalidate(struct pci_dev *pdev, void *context, + void (*block_invalidate)(void *context, + u64 block_mask)) +{ + struct hv_pcibus_device *hbus = + container_of(pdev->bus->sysdata, struct hv_pcibus_device, + sysdata); + struct hv_pci_dev *hpdev; + + hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn)); + if (!hpdev) + return -ENODEV; + + hpdev->block_invalidate = block_invalidate; + hpdev->invalidate_context = context; + + put_pcichild(hpdev); + return 0; + +} + /* Interrupt management hooks */ static void hv_int_desc_free(struct hv_pci_dev *hpdev, struct tran_int_desc *int_desc) @@ -941,7 +1222,7 @@ static void hv_irq_unmask(struct irq_data *data) * negative effect (yet?). */ - if (pci_protocol_version >= PCI_PROTOCOL_VERSION_1_2) { + if (hbus->protocol_version >= PCI_PROTOCOL_VERSION_1_2) { /* * PCI_PROTOCOL_VERSION_1_2 supports the VP_SET version of the * HVCALL_RETARGET_INTERRUPT hypercall, which also coincides @@ -1111,7 +1392,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ctxt.pci_pkt.completion_func = hv_pci_compose_compl; ctxt.pci_pkt.compl_ctxt = ∁ - switch (pci_protocol_version) { + switch (hbus->protocol_version) { case PCI_PROTOCOL_VERSION_1_1: size = hv_compose_msi_req_v1(&ctxt.int_pkts.v1, dest, @@ -1327,7 +1608,7 @@ static void survey_child_resources(struct hv_pcibus_device *hbus) * so it's sufficient to just add them up without tracking alignment. */ list_for_each_entry(hpdev, &hbus->children, list_entry) { - for (i = 0; i < 6; i++) { + for (i = 0; i < PCI_STD_NUM_BARS; i++) { if (hpdev->probed_bar[i] & PCI_BASE_ADDRESS_SPACE_IO) dev_err(&hbus->hdev->device, "There's an I/O BAR in this list!\n"); @@ -1398,10 +1679,27 @@ static void prepopulate_bars(struct hv_pcibus_device *hbus) spin_lock_irqsave(&hbus->device_list_lock, flags); + /* + * Clear the memory enable bit, in case it's already set. This occurs + * in the suspend path of hibernation, where the device is suspended, + * resumed and suspended again: see hibernation_snapshot() and + * hibernation_platform_enter(). + * + * If the memory enable bit is already set, Hyper-V sliently ignores + * the below BAR updates, and the related PCI device driver can not + * work, because reading from the device register(s) always returns + * 0xFFFFFFFF. + */ + list_for_each_entry(hpdev, &hbus->children, list_entry) { + _hv_pcifront_read_config(hpdev, PCI_COMMAND, 2, &command); + command &= ~PCI_COMMAND_MEMORY; + _hv_pcifront_write_config(hpdev, PCI_COMMAND, 2, command); + } + /* Pick addresses for the BARs. */ do { list_for_each_entry(hpdev, &hbus->children, list_entry) { - for (i = 0; i < 6; i++) { + for (i = 0; i < PCI_STD_NUM_BARS; i++) { bar_val = hpdev->probed_bar[i]; if (bar_val == 0) continue; @@ -1558,7 +1856,7 @@ static void q_resource_requirements(void *context, struct pci_response *resp, "query resource requirements failed: %x\n", resp->status); } else { - for (i = 0; i < 6; i++) { + for (i = 0; i < PCI_STD_NUM_BARS; i++) { completion->hpdev->probed_bar[i] = q_res_req->probed_bar[i]; } @@ -1824,6 +2122,12 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus, unsigned long flags; bool pending_dr; + if (hbus->state == hv_pcibus_removing) { + dev_info(&hbus->hdev->device, + "PCI VMBus BUS_RELATIONS: ignored\n"); + return; + } + dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT); if (!dr_wrk) return; @@ -1940,11 +2244,19 @@ static void hv_eject_device_work(struct work_struct *work) */ static void hv_pci_eject_device(struct hv_pci_dev *hpdev) { + struct hv_pcibus_device *hbus = hpdev->hbus; + struct hv_device *hdev = hbus->hdev; + + if (hbus->state == hv_pcibus_removing) { + dev_info(&hdev->device, "PCI VMBus EJECT: ignored\n"); + return; + } + hpdev->state = hv_pcichild_ejecting; get_pcichild(hpdev); INIT_WORK(&hpdev->wrk, hv_eject_device_work); - get_hvpcibus(hpdev->hbus); - queue_work(hpdev->hbus->wq, &hpdev->wrk); + get_hvpcibus(hbus); + queue_work(hbus->wq, &hpdev->wrk); } /** @@ -1968,6 +2280,7 @@ static void hv_pci_onchannelcallback(void *context) struct pci_response *response; struct pci_incoming_message *new_message; struct pci_bus_relations *bus_rel; + struct pci_dev_inval_block *inval; struct pci_dev_incoming *dev_message; struct hv_pci_dev *hpdev; @@ -2045,6 +2358,21 @@ static void hv_pci_onchannelcallback(void *context) } break; + case PCI_INVALIDATE_BLOCK: + + inval = (struct pci_dev_inval_block *)buffer; + hpdev = get_pcichild_wslot(hbus, + inval->wslot.slot); + if (hpdev) { + if (hpdev->block_invalidate) { + hpdev->block_invalidate( + hpdev->invalidate_context, + inval->block_mask); + } + put_pcichild(hpdev); + } + break; + default: dev_warn(&hbus->hdev->device, "Unimplemented protocol message %x\n", @@ -2080,8 +2408,11 @@ static void hv_pci_onchannelcallback(void *context) * failing if the host doesn't support the necessary protocol * level. */ -static int hv_pci_protocol_negotiation(struct hv_device *hdev) +static int hv_pci_protocol_negotiation(struct hv_device *hdev, + enum pci_protocol_version_t version[], + int num_version) { + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); struct pci_version_request *version_req; struct hv_pci_compl comp_pkt; struct pci_packet *pkt; @@ -2104,8 +2435,8 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev) version_req = (struct pci_version_request *)&pkt->message; version_req->message_type.type = PCI_QUERY_PROTOCOL_VERSION; - for (i = 0; i < ARRAY_SIZE(pci_protocol_versions); i++) { - version_req->protocol_version = pci_protocol_versions[i]; + for (i = 0; i < num_version; i++) { + version_req->protocol_version = version[i]; ret = vmbus_sendpacket(hdev->channel, version_req, sizeof(struct pci_version_request), (unsigned long)pkt, VM_PKT_DATA_INBAND, @@ -2121,10 +2452,10 @@ static int hv_pci_protocol_negotiation(struct hv_device *hdev) } if (comp_pkt.completion_status >= 0) { - pci_protocol_version = pci_protocol_versions[i]; + hbus->protocol_version = version[i]; dev_info(&hdev->device, "PCI VMBus probing: Using version %#x\n", - pci_protocol_version); + hbus->protocol_version); goto exit; } @@ -2408,7 +2739,7 @@ static int hv_send_resources_allocated(struct hv_device *hdev) u32 wslot; int ret; - size_res = (pci_protocol_version < PCI_PROTOCOL_VERSION_1_2) + size_res = (hbus->protocol_version < PCI_PROTOCOL_VERSION_1_2) ? sizeof(*res_assigned) : sizeof(*res_assigned2); pkt = kmalloc(sizeof(*pkt) + size_res, GFP_KERNEL); @@ -2427,7 +2758,7 @@ static int hv_send_resources_allocated(struct hv_device *hdev) pkt->completion_func = hv_pci_generic_compl; pkt->compl_ctxt = &comp_pkt; - if (pci_protocol_version < PCI_PROTOCOL_VERSION_1_2) { + if (hbus->protocol_version < PCI_PROTOCOL_VERSION_1_2) { res_assigned = (struct pci_resources_assigned *)&pkt->message; res_assigned->message_type.type = @@ -2510,6 +2841,48 @@ static void put_hvpcibus(struct hv_pcibus_device *hbus) complete(&hbus->remove_event); } +#define HVPCI_DOM_MAP_SIZE (64 * 1024) +static DECLARE_BITMAP(hvpci_dom_map, HVPCI_DOM_MAP_SIZE); + +/* + * PCI domain number 0 is used by emulated devices on Gen1 VMs, so define 0 + * as invalid for passthrough PCI devices of this driver. + */ +#define HVPCI_DOM_INVALID 0 + +/** + * hv_get_dom_num() - Get a valid PCI domain number + * Check if the PCI domain number is in use, and return another number if + * it is in use. + * + * @dom: Requested domain number + * + * return: domain number on success, HVPCI_DOM_INVALID on failure + */ +static u16 hv_get_dom_num(u16 dom) +{ + unsigned int i; + + if (test_and_set_bit(dom, hvpci_dom_map) == 0) + return dom; + + for_each_clear_bit(i, hvpci_dom_map, HVPCI_DOM_MAP_SIZE) { + if (test_and_set_bit(i, hvpci_dom_map) == 0) + return i; + } + + return HVPCI_DOM_INVALID; +} + +/** + * hv_put_dom_num() - Mark the PCI domain number as free + * @dom: Domain number to be freed + */ +static void hv_put_dom_num(u16 dom) +{ + clear_bit(dom, hvpci_dom_map); +} + /** * hv_pci_probe() - New VMBus channel probe, for a root PCI bus * @hdev: VMBus's tracking struct for this root PCI bus @@ -2521,33 +2894,68 @@ static int hv_pci_probe(struct hv_device *hdev, const struct hv_vmbus_device_id *dev_id) { struct hv_pcibus_device *hbus; + u16 dom_req, dom; + char *name; int ret; /* * hv_pcibus_device contains the hypercall arguments for retargeting in * hv_irq_unmask(). Those must not cross a page boundary. */ - BUILD_BUG_ON(sizeof(*hbus) > PAGE_SIZE); + BUILD_BUG_ON(sizeof(*hbus) > HV_HYP_PAGE_SIZE); - hbus = (struct hv_pcibus_device *)get_zeroed_page(GFP_KERNEL); + /* + * With the recent 59bb47985c1d ("mm, sl[aou]b: guarantee natural + * alignment for kmalloc(power-of-two)"), kzalloc() is able to allocate + * a 4KB buffer that is guaranteed to be 4KB-aligned. Here the size and + * alignment of hbus is important because hbus's field + * retarget_msi_interrupt_params must not cross a 4KB page boundary. + * + * Here we prefer kzalloc to get_zeroed_page(), because a buffer + * allocated by the latter is not tracked and scanned by kmemleak, and + * hence kmemleak reports the pointer contained in the hbus buffer + * (i.e. the hpdev struct, which is created in new_pcichild_device() and + * is tracked by hbus->children) as memory leak (false positive). + * + * If the kernel doesn't have 59bb47985c1d, get_zeroed_page() *must* be + * used to allocate the hbus buffer and we can avoid the kmemleak false + * positive by using kmemleak_alloc() and kmemleak_free() to ask + * kmemleak to track and scan the hbus buffer. + */ + hbus = (struct hv_pcibus_device *)kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL); if (!hbus) return -ENOMEM; hbus->state = hv_pcibus_init; /* - * The PCI bus "domain" is what is called "segment" in ACPI and - * other specs. Pull it from the instance ID, to get something - * unique. Bytes 8 and 9 are what is used in Windows guests, so - * do the same thing for consistency. Note that, since this code - * only runs in a Hyper-V VM, Hyper-V can (and does) guarantee - * that (1) the only domain in use for something that looks like - * a physical PCI bus (which is actually emulated by the - * hypervisor) is domain 0 and (2) there will be no overlap - * between domains derived from these instance IDs in the same - * VM. + * The PCI bus "domain" is what is called "segment" in ACPI and other + * specs. Pull it from the instance ID, to get something usually + * unique. In rare cases of collision, we will find out another number + * not in use. + * + * Note that, since this code only runs in a Hyper-V VM, Hyper-V + * together with this guest driver can guarantee that (1) The only + * domain used by Gen1 VMs for something that looks like a physical + * PCI bus (which is actually emulated by the hypervisor) is domain 0. + * (2) There will be no overlap between domains (after fixing possible + * collisions) in the same VM. */ - hbus->sysdata.domain = hdev->dev_instance.b[9] | - hdev->dev_instance.b[8] << 8; + dom_req = hdev->dev_instance.b[5] << 8 | hdev->dev_instance.b[4]; + dom = hv_get_dom_num(dom_req); + + if (dom == HVPCI_DOM_INVALID) { + dev_err(&hdev->device, + "Unable to use dom# 0x%hx or other numbers", dom_req); + ret = -EINVAL; + goto free_bus; + } + + if (dom != dom_req) + dev_info(&hdev->device, + "PCI dom# 0x%hx has collision, using 0x%hx", + dom_req, dom); + + hbus->sysdata.domain = dom; hbus->hdev = hdev; refcount_set(&hbus->remove_lock, 1); @@ -2562,7 +2970,7 @@ static int hv_pci_probe(struct hv_device *hdev, hbus->sysdata.domain); if (!hbus->wq) { ret = -ENOMEM; - goto free_bus; + goto free_dom; } ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0, @@ -2572,7 +2980,8 @@ static int hv_pci_probe(struct hv_device *hdev, hv_set_drvdata(hdev, hbus); - ret = hv_pci_protocol_negotiation(hdev); + ret = hv_pci_protocol_negotiation(hdev, pci_protocol_versions, + ARRAY_SIZE(pci_protocol_versions)); if (ret) goto close; @@ -2589,7 +2998,14 @@ static int hv_pci_probe(struct hv_device *hdev, goto free_config; } - hbus->sysdata.fwnode = irq_domain_alloc_fwnode(hbus); + name = kasprintf(GFP_KERNEL, "%pUL", &hdev->dev_instance); + if (!name) { + ret = -ENOMEM; + goto unmap; + } + + hbus->sysdata.fwnode = irq_domain_alloc_named_fwnode(name); + kfree(name); if (!hbus->sysdata.fwnode) { ret = -ENOMEM; goto unmap; @@ -2639,12 +3055,14 @@ close: vmbus_close(hdev->channel); destroy_wq: destroy_workqueue(hbus->wq); +free_dom: + hv_put_dom_num(hbus->sysdata.domain); free_bus: free_page((unsigned long)hbus); return ret; } -static void hv_pci_bus_exit(struct hv_device *hdev) +static int hv_pci_bus_exit(struct hv_device *hdev, bool hibernating) { struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); struct { @@ -2660,16 +3078,20 @@ static void hv_pci_bus_exit(struct hv_device *hdev) * access the per-channel ringbuffer any longer. */ if (hdev->channel->rescind) - return; + return 0; - /* Delete any children which might still exist. */ - memset(&relations, 0, sizeof(relations)); - hv_pci_devices_present(hbus, &relations); + if (!hibernating) { + /* Delete any children which might still exist. */ + memset(&relations, 0, sizeof(relations)); + hv_pci_devices_present(hbus, &relations); + } ret = hv_send_resources_released(hdev); - if (ret) + if (ret) { dev_err(&hdev->device, "Couldn't send resources released packet(s)\n"); + return ret; + } memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet)); init_completion(&comp_pkt.host_event); @@ -2682,8 +3104,13 @@ static void hv_pci_bus_exit(struct hv_device *hdev) (unsigned long)&pkt.teardown_packet, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (!ret) - wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ); + if (ret) + return ret; + + if (wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ) == 0) + return -ETIMEDOUT; + + return 0; } /** @@ -2695,19 +3122,20 @@ static void hv_pci_bus_exit(struct hv_device *hdev) static int hv_pci_remove(struct hv_device *hdev) { struct hv_pcibus_device *hbus; + int ret; hbus = hv_get_drvdata(hdev); if (hbus->state == hv_pcibus_installed) { /* Remove the bus from PCI's point of view. */ pci_lock_rescan_remove(); pci_stop_root_bus(hbus->pci_bus); - pci_remove_root_bus(hbus->pci_bus); hv_pci_remove_slots(hbus); + pci_remove_root_bus(hbus->pci_bus); pci_unlock_rescan_remove(); hbus->state = hv_pcibus_removed; } - hv_pci_bus_exit(hdev); + ret = hv_pci_bus_exit(hdev, false); vmbus_close(hdev->channel); @@ -2720,8 +3148,98 @@ static int hv_pci_remove(struct hv_device *hdev) put_hvpcibus(hbus); wait_for_completion(&hbus->remove_event); destroy_workqueue(hbus->wq); - free_page((unsigned long)hbus); + + hv_put_dom_num(hbus->sysdata.domain); + + kfree(hbus); + return ret; +} + +static int hv_pci_suspend(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); + enum hv_pcibus_state old_state; + int ret; + + /* + * hv_pci_suspend() must make sure there are no pending work items + * before calling vmbus_close(), since it runs in a process context + * as a callback in dpm_suspend(). When it starts to run, the channel + * callback hv_pci_onchannelcallback(), which runs in a tasklet + * context, can be still running concurrently and scheduling new work + * items onto hbus->wq in hv_pci_devices_present() and + * hv_pci_eject_device(), and the work item handlers can access the + * vmbus channel, which can be being closed by hv_pci_suspend(), e.g. + * the work item handler pci_devices_present_work() -> + * new_pcichild_device() writes to the vmbus channel. + * + * To eliminate the race, hv_pci_suspend() disables the channel + * callback tasklet, sets hbus->state to hv_pcibus_removing, and + * re-enables the tasklet. This way, when hv_pci_suspend() proceeds, + * it knows that no new work item can be scheduled, and then it flushes + * hbus->wq and safely closes the vmbus channel. + */ + tasklet_disable(&hdev->channel->callback_event); + + /* Change the hbus state to prevent new work items. */ + old_state = hbus->state; + if (hbus->state == hv_pcibus_installed) + hbus->state = hv_pcibus_removing; + + tasklet_enable(&hdev->channel->callback_event); + + if (old_state != hv_pcibus_installed) + return -EINVAL; + + flush_workqueue(hbus->wq); + + ret = hv_pci_bus_exit(hdev, true); + if (ret) + return ret; + + vmbus_close(hdev->channel); + + return 0; +} + +static int hv_pci_resume(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); + enum pci_protocol_version_t version[1]; + int ret; + + hbus->state = hv_pcibus_init; + + ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0, + hv_pci_onchannelcallback, hbus); + if (ret) + return ret; + + /* Only use the version that was in use before hibernation. */ + version[0] = hbus->protocol_version; + ret = hv_pci_protocol_negotiation(hdev, version, 1); + if (ret) + goto out; + + ret = hv_pci_query_relations(hdev); + if (ret) + goto out; + + ret = hv_pci_enter_d0(hdev); + if (ret) + goto out; + + ret = hv_send_resources_allocated(hdev); + if (ret) + goto out; + + prepopulate_bars(hbus); + + hbus->state = hv_pcibus_installed; return 0; +out: + vmbus_close(hdev->channel); + return ret; } static const struct hv_vmbus_device_id hv_pci_id_table[] = { @@ -2738,15 +3256,29 @@ static struct hv_driver hv_pci_drv = { .id_table = hv_pci_id_table, .probe = hv_pci_probe, .remove = hv_pci_remove, + .suspend = hv_pci_suspend, + .resume = hv_pci_resume, }; static void __exit exit_hv_pci_drv(void) { vmbus_driver_unregister(&hv_pci_drv); + + hvpci_block_ops.read_block = NULL; + hvpci_block_ops.write_block = NULL; + hvpci_block_ops.reg_blk_invalidate = NULL; } static int __init init_hv_pci_drv(void) { + /* Set the invalid domain number's bit, so it will not be used */ + set_bit(HVPCI_DOM_INVALID, hvpci_dom_map); + + /* Initialize PCI block r/w interface */ + hvpci_block_ops.read_block = hv_read_config_block; + hvpci_block_ops.write_block = hv_write_config_block; + hvpci_block_ops.reg_blk_invalidate = hv_register_block_invalidate; + return vmbus_driver_register(&hv_pci_drv); } diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index d3a0419e42f2..153a64676bc9 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -554,7 +554,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, } } -struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { +static struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { .write_base = mvebu_pci_bridge_emul_base_conf_write, .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read, .write_pcie = mvebu_pci_bridge_emul_pcie_conf_write, @@ -713,7 +713,7 @@ static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev, ret = of_address_to_resource(np, 0, ®s); if (ret) - return ERR_PTR(ret); + return (void __iomem *)ERR_PTR(ret); return devm_ioremap_resource(&pdev->dev, ®s); } diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 9a917b2456f6..0e03cef72840 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -1406,7 +1406,7 @@ static struct phy *devm_of_phy_optional_get_index(struct device *dev, phy = devm_of_phy_get(dev, np, name); kfree(name); - if (IS_ERR(phy) && PTR_ERR(phy) == -ENODEV) + if (PTR_ERR(phy) == -ENODEV) phy = NULL; return phy; @@ -2237,14 +2237,15 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) err = of_pci_get_devfn(port); if (err < 0) { dev_err(dev, "failed to parse address: %d\n", err); - return err; + goto err_node_put; } index = PCI_SLOT(err); if (index < 1 || index > soc->num_ports) { dev_err(dev, "invalid port number: %d\n", index); - return -EINVAL; + err = -EINVAL; + goto err_node_put; } index--; @@ -2253,12 +2254,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) if (err < 0) { dev_err(dev, "failed to parse # of lanes: %d\n", err); - return err; + goto err_node_put; } if (value > 16) { dev_err(dev, "invalid # of lanes: %u\n", value); - return -EINVAL; + err = -EINVAL; + goto err_node_put; } lanes |= value << (index << 3); @@ -2272,13 +2274,15 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) lane += value; rp = devm_kzalloc(dev, sizeof(*rp), GFP_KERNEL); - if (!rp) - return -ENOMEM; + if (!rp) { + err = -ENOMEM; + goto err_node_put; + } err = of_address_to_resource(port, 0, &rp->regs); if (err < 0) { dev_err(dev, "failed to parse address: %d\n", err); - return err; + goto err_node_put; } INIT_LIST_HEAD(&rp->list); @@ -2330,6 +2334,10 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) return err; return 0; + +err_node_put: + of_node_put(port); + return err; } /* @@ -2491,7 +2499,6 @@ static const struct tegra_pcie_soc tegra20_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 0, - .afi_pex2_ctrl = 0x128, .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, .pads_refclk_cfg0 = 0xfa5cfa5c, @@ -2520,6 +2527,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .num_ports = 3, .ports = tegra30_pcie_ports, .msi_base_shift = 8, + .afi_pex2_ctrl = 0x128, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, .pads_refclk_cfg0 = 0xfa5cfa5c, @@ -2790,7 +2798,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) pm_runtime_enable(pcie->dev); err = pm_runtime_get_sync(pcie->dev); - if (err) { + if (err < 0) { dev_err(dev, "fail to enable pcie controller: %d\n", err); goto teardown_msi; } diff --git a/drivers/pci/controller/pci-thunder-pem.c b/drivers/pci/controller/pci-thunder-pem.c index f127ce8bd4ef..9491e266b1ea 100644 --- a/drivers/pci/controller/pci-thunder-pem.c +++ b/drivers/pci/controller/pci-thunder-pem.c @@ -6,6 +6,7 @@ #include <linux/bitfield.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/pci.h> #include <linux/of_address.h> #include <linux/of_pci.h> #include <linux/pci-acpi.h> diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index d219404bad92..bd05221f5a22 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -241,10 +241,8 @@ struct v3_pci { void __iomem *config_base; struct pci_bus *bus; u32 config_mem; - u32 io_mem; u32 non_pre_mem; u32 pre_mem; - phys_addr_t io_bus_addr; phys_addr_t non_pre_bus_addr; phys_addr_t pre_bus_addr; struct regmap *map; @@ -520,35 +518,22 @@ static int v3_integrator_init(struct v3_pci *v3) } static int v3_pci_setup_resource(struct v3_pci *v3, - resource_size_t io_base, struct pci_host_bridge *host, struct resource_entry *win) { struct device *dev = v3->dev; struct resource *mem; struct resource *io; - int ret; switch (resource_type(win->res)) { case IORESOURCE_IO: io = win->res; - io->name = "V3 PCI I/O"; - v3->io_mem = io_base; - v3->io_bus_addr = io->start - win->offset; - dev_dbg(dev, "I/O window %pR, bus addr %pap\n", - io, &v3->io_bus_addr); - ret = devm_pci_remap_iospace(dev, io, io_base); - if (ret) { - dev_warn(dev, - "error %d: failed to map resource %pR\n", - ret, io); - return ret; - } + /* Setup window 2 - PCI I/O */ - writel(v3_addr_to_lb_base2(v3->io_mem) | + writel(v3_addr_to_lb_base2(pci_pio_to_address(io->start)) | V3_LB_BASE2_ENABLE, v3->base + V3_LB_BASE2); - writew(v3_addr_to_lb_map2(v3->io_bus_addr), + writew(v3_addr_to_lb_map2(io->start - win->offset), v3->base + V3_LB_MAP2); break; case IORESOURCE_MEM: @@ -613,28 +598,30 @@ static int v3_pci_setup_resource(struct v3_pci *v3, } static int v3_get_dma_range_config(struct v3_pci *v3, - struct of_pci_range *range, + struct resource_entry *entry, u32 *pci_base, u32 *pci_map) { struct device *dev = v3->dev; - u64 cpu_end = range->cpu_addr + range->size - 1; - u64 pci_end = range->pci_addr + range->size - 1; + u64 cpu_addr = entry->res->start; + u64 cpu_end = entry->res->end; + u64 pci_end = cpu_end - entry->offset; + u64 pci_addr = entry->res->start - entry->offset; u32 val; - if (range->pci_addr & ~V3_PCI_BASE_M_ADR_BASE) { + if (pci_addr & ~V3_PCI_BASE_M_ADR_BASE) { dev_err(dev, "illegal range, only PCI bits 31..20 allowed\n"); return -EINVAL; } - val = ((u32)range->pci_addr) & V3_PCI_BASE_M_ADR_BASE; + val = ((u32)pci_addr) & V3_PCI_BASE_M_ADR_BASE; *pci_base = val; - if (range->cpu_addr & ~V3_PCI_MAP_M_MAP_ADR) { + if (cpu_addr & ~V3_PCI_MAP_M_MAP_ADR) { dev_err(dev, "illegal range, only CPU bits 31..20 allowed\n"); return -EINVAL; } - val = ((u32)range->cpu_addr) & V3_PCI_MAP_M_MAP_ADR; + val = ((u32)cpu_addr) & V3_PCI_MAP_M_MAP_ADR; - switch (range->size) { + switch (resource_size(entry->res)) { case SZ_1M: val |= V3_LB_BASE_ADR_SIZE_1MB; break; @@ -682,8 +669,8 @@ static int v3_get_dma_range_config(struct v3_pci *v3, dev_dbg(dev, "DMA MEM CPU: 0x%016llx -> 0x%016llx => " "PCI: 0x%016llx -> 0x%016llx base %08x map %08x\n", - range->cpu_addr, cpu_end, - range->pci_addr, pci_end, + cpu_addr, cpu_end, + pci_addr, pci_end, *pci_base, *pci_map); return 0; @@ -692,24 +679,16 @@ static int v3_get_dma_range_config(struct v3_pci *v3, static int v3_pci_parse_map_dma_ranges(struct v3_pci *v3, struct device_node *np) { - struct of_pci_range range; - struct of_pci_range_parser parser; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(v3); struct device *dev = v3->dev; + struct resource_entry *entry; int i = 0; - if (of_pci_dma_range_parser_init(&parser, np)) { - dev_err(dev, "missing dma-ranges property\n"); - return -EINVAL; - } - - /* - * Get the dma-ranges from the device tree - */ - for_each_of_pci_range(&parser, &range) { + resource_list_for_each_entry(entry, &bridge->dma_ranges) { int ret; u32 pci_base, pci_map; - ret = v3_get_dma_range_config(v3, &range, &pci_base, &pci_map); + ret = v3_get_dma_range_config(v3, entry, &pci_base, &pci_map); if (ret) return ret; @@ -732,7 +711,6 @@ static int v3_pci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - resource_size_t io_base; struct resource *regs; struct resource_entry *win; struct v3_pci *v3; @@ -741,7 +719,6 @@ static int v3_pci_probe(struct platform_device *pdev) u16 val; int irq; int ret; - LIST_HEAD(res); host = pci_alloc_host_bridge(sizeof(*v3)); if (!host) @@ -793,12 +770,8 @@ static int v3_pci_probe(struct platform_device *pdev) if (IS_ERR(v3->config_base)) return PTR_ERR(v3->config_base); - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, - &io_base); - if (ret) - return ret; - - ret = devm_request_pci_bus_resources(dev, &res); + ret = pci_parse_request_of_pci_ranges(dev, &host->windows, + &host->dma_ranges, NULL); if (ret) return ret; @@ -852,8 +825,8 @@ static int v3_pci_probe(struct platform_device *pdev) writew(val, v3->base + V3_PCI_CMD); /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry(win, &res) { - ret = v3_pci_setup_resource(v3, io_base, host, win); + resource_list_for_each_entry(win, &host->windows) { + ret = v3_pci_setup_resource(v3, host, win); if (ret) { dev_err(dev, "error setting up resources\n"); return ret; @@ -931,7 +904,6 @@ static int v3_pci_probe(struct platform_device *pdev) val |= V3_SYSTEM_M_LOCK; writew(val, v3->base + V3_SYSTEM); - list_splice_init(&res, &host->windows); ret = pci_scan_root_bus_bridge(host); if (ret) { dev_err(dev, "failed to register host: %d\n", ret); diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c index f59ad2728c0b..b911359b6d81 100644 --- a/drivers/pci/controller/pci-versatile.c +++ b/drivers/pci/controller/pci-versatile.c @@ -62,65 +62,16 @@ static struct pci_ops pci_versatile_ops = { .write = pci_generic_config_write, }; -static int versatile_pci_parse_request_of_pci_ranges(struct device *dev, - struct list_head *res) -{ - int err, mem = 1, res_valid = 0; - resource_size_t iobase; - struct resource_entry *win, *tmp; - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, res, &iobase); - if (err) - return err; - - err = devm_request_pci_bus_resources(dev, res); - if (err) - goto out_release_res; - - resource_list_for_each_entry_safe(win, tmp, res) { - struct resource *res = win->res; - - switch (resource_type(res)) { - case IORESOURCE_IO: - err = devm_pci_remap_iospace(dev, res, iobase); - if (err) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - err, res); - resource_list_destroy_entry(win); - } - break; - case IORESOURCE_MEM: - res_valid |= !(res->flags & IORESOURCE_PREFETCH); - - writel(res->start >> 28, PCI_IMAP(mem)); - writel(PHYS_OFFSET >> 28, PCI_SMAP(mem)); - mem++; - - break; - } - } - - if (res_valid) - return 0; - - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; - -out_release_res: - pci_free_resource_list(res); - return err; -} - static int versatile_pci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; - int ret, i, myslot = -1; + struct resource_entry *entry; + int ret, i, myslot = -1, mem = 1; u32 val; void __iomem *local_pci_cfg_base; struct pci_bus *bus, *child; struct pci_host_bridge *bridge; - LIST_HEAD(pci_res); bridge = devm_pci_alloc_host_bridge(dev, 0); if (!bridge) @@ -141,10 +92,19 @@ static int versatile_pci_probe(struct platform_device *pdev) if (IS_ERR(versatile_cfg_base[1])) return PTR_ERR(versatile_cfg_base[1]); - ret = versatile_pci_parse_request_of_pci_ranges(dev, &pci_res); + ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + NULL, NULL); if (ret) return ret; + resource_list_for_each_entry(entry, &bridge->windows) { + if (resource_type(entry->res) == IORESOURCE_MEM) { + writel(entry->res->start >> 28, PCI_IMAP(mem)); + writel(__pa(PAGE_OFFSET) >> 28, PCI_SMAP(mem)); + mem++; + } + } + /* * We need to discover the PCI core first to configure itself * before the main PCI probing is performed @@ -177,9 +137,9 @@ static int versatile_pci_probe(struct platform_device *pdev) /* * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM */ - writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0); - writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1); - writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2); + writel(__pa(PAGE_OFFSET), local_pci_cfg_base + PCI_BASE_ADDRESS_0); + writel(__pa(PAGE_OFFSET), local_pci_cfg_base + PCI_BASE_ADDRESS_1); + writel(__pa(PAGE_OFFSET), local_pci_cfg_base + PCI_BASE_ADDRESS_2); /* * For many years the kernel and QEMU were symbiotically buggy @@ -197,7 +157,6 @@ static int versatile_pci_probe(struct platform_device *pdev) pci_add_flags(PCI_ENABLE_PROC_DOMAINS); pci_add_flags(PCI_REASSIGN_ALL_BUS); - list_splice_init(&pci_res, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = NULL; bridge->busnr = 0; diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index ffda3e8b4742..de195fd430dc 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -405,15 +405,13 @@ static void xgene_pcie_setup_cfg_reg(struct xgene_pcie_port *port) xgene_pcie_writel(port, CFGCTL, EN_REG); } -static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, - struct list_head *res, - resource_size_t io_base) +static int xgene_pcie_map_ranges(struct xgene_pcie_port *port) { + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(port); struct resource_entry *window; struct device *dev = port->dev; - int ret; - resource_list_for_each_entry(window, res) { + resource_list_for_each_entry(window, &bridge->windows) { struct resource *res = window->res; u64 restype = resource_type(res); @@ -421,11 +419,9 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, switch (restype) { case IORESOURCE_IO: - xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base, + xgene_pcie_setup_ob_reg(port, res, OMR3BARL, + pci_pio_to_address(res->start), res->start - window->offset); - ret = devm_pci_remap_iospace(dev, res, io_base); - if (ret < 0) - return ret; break; case IORESOURCE_MEM: if (res->flags & IORESOURCE_PREFETCH) @@ -485,27 +481,28 @@ static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size) } static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, - struct of_pci_range *range, u8 *ib_reg_mask) + struct resource_entry *entry, + u8 *ib_reg_mask) { void __iomem *cfg_base = port->cfg_base; struct device *dev = port->dev; void *bar_addr; u32 pim_reg; - u64 cpu_addr = range->cpu_addr; - u64 pci_addr = range->pci_addr; - u64 size = range->size; + u64 cpu_addr = entry->res->start; + u64 pci_addr = cpu_addr - entry->offset; + u64 size = resource_size(entry->res); u64 mask = ~(size - 1) | EN_REG; u32 flags = PCI_BASE_ADDRESS_MEM_TYPE_64; u32 bar_low; int region; - region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size); + region = xgene_pcie_select_ib_reg(ib_reg_mask, size); if (region < 0) { dev_warn(dev, "invalid pcie dma-range config\n"); return; } - if (range->flags & IORESOURCE_PREFETCH) + if (entry->res->flags & IORESOURCE_PREFETCH) flags |= PCI_BASE_ADDRESS_MEM_PREFETCH; bar_low = pcie_bar_low_val((u32)cpu_addr, flags); @@ -536,25 +533,13 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port) { - struct device_node *np = port->node; - struct of_pci_range range; - struct of_pci_range_parser parser; - struct device *dev = port->dev; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(port); + struct resource_entry *entry; u8 ib_reg_mask = 0; - if (of_pci_dma_range_parser_init(&parser, np)) { - dev_err(dev, "missing dma-ranges property\n"); - return -EINVAL; - } - - /* Get the dma-ranges from DT */ - for_each_of_pci_range(&parser, &range) { - u64 end = range.cpu_addr + range.size - 1; + resource_list_for_each_entry(entry, &bridge->dma_ranges) + xgene_pcie_setup_ib_reg(port, entry, &ib_reg_mask); - dev_dbg(dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", - range.flags, range.cpu_addr, end, range.pci_addr); - xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask); - } return 0; } @@ -567,8 +552,7 @@ static void xgene_pcie_clear_config(struct xgene_pcie_port *port) xgene_pcie_writel(port, i, 0); } -static int xgene_pcie_setup(struct xgene_pcie_port *port, struct list_head *res, - resource_size_t io_base) +static int xgene_pcie_setup(struct xgene_pcie_port *port) { struct device *dev = port->dev; u32 val, lanes = 0, speed = 0; @@ -580,7 +564,7 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port, struct list_head *res, val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID; xgene_pcie_writel(port, BRIDGE_CFG_0, val); - ret = xgene_pcie_map_ranges(port, res, io_base); + ret = xgene_pcie_map_ranges(port); if (ret) return ret; @@ -607,11 +591,9 @@ static int xgene_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node; struct xgene_pcie_port *port; - resource_size_t iobase = 0; struct pci_bus *bus, *child; struct pci_host_bridge *bridge; int ret; - LIST_HEAD(res); bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port)); if (!bridge) @@ -634,20 +616,15 @@ static int xgene_pcie_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, - &iobase); + ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, NULL); if (ret) return ret; - ret = devm_request_pci_bus_resources(dev, &res); - if (ret) - goto error; - - ret = xgene_pcie_setup(port, &res, iobase); + ret = xgene_pcie_setup(port); if (ret) - goto error; + return ret; - list_splice_init(&res, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = port; bridge->busnr = 0; @@ -657,7 +634,7 @@ static int xgene_pcie_probe(struct platform_device *pdev) ret = pci_scan_root_bus_bridge(bridge); if (ret < 0) - goto error; + return ret; bus = bridge->bus; @@ -666,10 +643,6 @@ static int xgene_pcie_probe(struct platform_device *pdev) pcie_bus_configure_settings(child); pci_bus_add_devices(bus); return 0; - -error: - pci_free_resource_list(&res); - return ret; } static const struct of_device_id xgene_pcie_match_table[] = { diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index d2497ca43828..b447c3e4abad 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -92,7 +92,6 @@ struct altera_pcie { u8 root_bus_nr; struct irq_domain *irq_domain; struct resource bus_range; - struct list_head resources; const struct altera_pcie_data *pcie_data; }; @@ -670,39 +669,6 @@ static void altera_pcie_isr(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static int altera_pcie_parse_request_of_pci_ranges(struct altera_pcie *pcie) -{ - int err, res_valid = 0; - struct device *dev = &pcie->pdev->dev; - struct resource_entry *win; - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &pcie->resources, NULL); - if (err) - return err; - - err = devm_request_pci_bus_resources(dev, &pcie->resources); - if (err) - goto out_release_res; - - resource_list_for_each_entry(win, &pcie->resources) { - struct resource *res = win->res; - - if (resource_type(res) == IORESOURCE_MEM) - res_valid |= !(res->flags & IORESOURCE_PREFETCH); - } - - if (res_valid) - return 0; - - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; - -out_release_res: - pci_free_resource_list(&pcie->resources); - return err; -} - static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) { struct device *dev = &pcie->pdev->dev; @@ -833,9 +799,8 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } - INIT_LIST_HEAD(&pcie->resources); - - ret = altera_pcie_parse_request_of_pci_ranges(pcie); + ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, NULL); if (ret) { dev_err(dev, "Failed add resources\n"); return ret; @@ -853,7 +818,6 @@ static int altera_pcie_probe(struct platform_device *pdev) cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); altera_pcie_host_init(pcie); - list_splice_init(&pcie->resources, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = pcie->root_bus_nr; @@ -884,7 +848,6 @@ static int altera_pcie_remove(struct platform_device *pdev) pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); - pci_free_resource_list(&pcie->resources); altera_pcie_irq_teardown(pcie); return 0; diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c new file mode 100644 index 000000000000..d20aabc26273 --- /dev/null +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -0,0 +1,1015 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2009 - 2019 Broadcom */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/log2.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_pci.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/printk.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "../pci.h" + +/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */ +#define BRCM_PCIE_CAP_REGS 0x00ac + +/* Broadcom STB PCIe Register Offsets */ +#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1 0x0188 +#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc +#define PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN 0x0 + +#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c +#define PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff + +#define PCIE_RC_DL_MDIO_ADDR 0x1100 +#define PCIE_RC_DL_MDIO_WR_DATA 0x1104 +#define PCIE_RC_DL_MDIO_RD_DATA 0x1108 + +#define PCIE_MISC_MISC_CTRL 0x4008 +#define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000 +#define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000 +#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000 +#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128 0x0 +#define PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK 0xf8000000 + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c +#define PCIE_MEM_WIN0_LO(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4) + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010 +#define PCIE_MEM_WIN0_HI(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4) + +#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c +#define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f + +#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034 +#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f +#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038 + +#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c +#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f + +#define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044 +#define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048 + +#define PCIE_MISC_MSI_DATA_CONFIG 0x404c +#define PCIE_MISC_MSI_DATA_CONFIG_VAL 0xffe06540 + +#define PCIE_MISC_PCIE_CTRL 0x4064 +#define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1 + +#define PCIE_MISC_PCIE_STATUS 0x4068 +#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80 +#define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20 +#define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10 +#define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40 + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070 +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000 +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK 0xfff0 +#define PCIE_MEM_WIN0_BASE_LIMIT(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4) + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI 0x4080 +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK 0xff +#define PCIE_MEM_WIN0_BASE_HI(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8) + +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI 0x4084 +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK 0xff +#define PCIE_MEM_WIN0_LIMIT_HI(win) \ + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8) + +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000 + +#define PCIE_MSI_INTR2_STATUS 0x4500 +#define PCIE_MSI_INTR2_CLR 0x4508 +#define PCIE_MSI_INTR2_MASK_SET 0x4510 +#define PCIE_MSI_INTR2_MASK_CLR 0x4514 + +#define PCIE_EXT_CFG_DATA 0x8000 + +#define PCIE_EXT_CFG_INDEX 0x9000 +#define PCIE_EXT_BUSNUM_SHIFT 20 +#define PCIE_EXT_SLOT_SHIFT 15 +#define PCIE_EXT_FUNC_SHIFT 12 + +#define PCIE_RGR1_SW_INIT_1 0x9210 +#define PCIE_RGR1_SW_INIT_1_PERST_MASK 0x1 +#define PCIE_RGR1_SW_INIT_1_INIT_MASK 0x2 + +/* PCIe parameters */ +#define BRCM_NUM_PCIE_OUT_WINS 0x4 +#define BRCM_INT_PCI_MSI_NR 32 + +/* MSI target adresses */ +#define BRCM_MSI_TARGET_ADDR_LT_4GB 0x0fffffffcULL +#define BRCM_MSI_TARGET_ADDR_GT_4GB 0xffffffffcULL + +/* MDIO registers */ +#define MDIO_PORT0 0x0 +#define MDIO_DATA_MASK 0x7fffffff +#define MDIO_PORT_MASK 0xf0000 +#define MDIO_REGAD_MASK 0xffff +#define MDIO_CMD_MASK 0xfff00000 +#define MDIO_CMD_READ 0x1 +#define MDIO_CMD_WRITE 0x0 +#define MDIO_DATA_DONE_MASK 0x80000000 +#define MDIO_RD_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 1 : 0) +#define MDIO_WT_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 0 : 1) +#define SSC_REGS_ADDR 0x1100 +#define SET_ADDR_OFFSET 0x1f +#define SSC_CNTL_OFFSET 0x2 +#define SSC_CNTL_OVRD_EN_MASK 0x8000 +#define SSC_CNTL_OVRD_VAL_MASK 0x4000 +#define SSC_STATUS_OFFSET 0x1 +#define SSC_STATUS_SSC_MASK 0x400 +#define SSC_STATUS_PLL_LOCK_MASK 0x800 + +struct brcm_msi { + struct device *dev; + void __iomem *base; + struct device_node *np; + struct irq_domain *msi_domain; + struct irq_domain *inner_domain; + struct mutex lock; /* guards the alloc/free operations */ + u64 target_addr; + int irq; + /* used indicates which MSI interrupts have been alloc'd */ + unsigned long used; +}; + +/* Internal PCIe Host Controller Information.*/ +struct brcm_pcie { + struct device *dev; + void __iomem *base; + struct clk *clk; + struct pci_bus *root_bus; + struct device_node *np; + bool ssc; + int gen; + u64 msi_target_addr; + struct brcm_msi *msi; +}; + +/* + * This is to convert the size of the inbound "BAR" region to the + * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE + */ +static int brcm_pcie_encode_ibar_size(u64 size) +{ + int log2_in = ilog2(size); + + if (log2_in >= 12 && log2_in <= 15) + /* Covers 4KB to 32KB (inclusive) */ + return (log2_in - 12) + 0x1c; + else if (log2_in >= 16 && log2_in <= 35) + /* Covers 64KB to 32GB, (inclusive) */ + return log2_in - 15; + /* Something is awry so disable */ + return 0; +} + +static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd) +{ + u32 pkt = 0; + + pkt |= FIELD_PREP(MDIO_PORT_MASK, port); + pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad); + pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd); + + return pkt; +} + +/* negative return value indicates error */ +static int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val) +{ + int tries; + u32 data; + + writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ), + base + PCIE_RC_DL_MDIO_ADDR); + readl(base + PCIE_RC_DL_MDIO_ADDR); + + data = readl(base + PCIE_RC_DL_MDIO_RD_DATA); + for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) { + udelay(10); + data = readl(base + PCIE_RC_DL_MDIO_RD_DATA); + } + + *val = FIELD_GET(MDIO_DATA_MASK, data); + return MDIO_RD_DONE(data) ? 0 : -EIO; +} + +/* negative return value indicates error */ +static int brcm_pcie_mdio_write(void __iomem *base, u8 port, + u8 regad, u16 wrdata) +{ + int tries; + u32 data; + + writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE), + base + PCIE_RC_DL_MDIO_ADDR); + readl(base + PCIE_RC_DL_MDIO_ADDR); + writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA); + + data = readl(base + PCIE_RC_DL_MDIO_WR_DATA); + for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) { + udelay(10); + data = readl(base + PCIE_RC_DL_MDIO_WR_DATA); + } + + return MDIO_WT_DONE(data) ? 0 : -EIO; +} + +/* + * Configures device for Spread Spectrum Clocking (SSC) mode; a negative + * return value indicates error. + */ +static int brcm_pcie_set_ssc(struct brcm_pcie *pcie) +{ + int pll, ssc; + int ret; + u32 tmp; + + ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, + SSC_REGS_ADDR); + if (ret < 0) + return ret; + + ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, + SSC_CNTL_OFFSET, &tmp); + if (ret < 0) + return ret; + + u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_EN_MASK); + u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_VAL_MASK); + ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, + SSC_CNTL_OFFSET, tmp); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, + SSC_STATUS_OFFSET, &tmp); + if (ret < 0) + return ret; + + ssc = FIELD_GET(SSC_STATUS_SSC_MASK, tmp); + pll = FIELD_GET(SSC_STATUS_PLL_LOCK_MASK, tmp); + + return ssc && pll ? 0 : -EIO; +} + +/* Limits operation to a specific generation (1, 2, or 3) */ +static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen) +{ + u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); + u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); + + lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen; + writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); + + lnkctl2 = (lnkctl2 & ~0xf) | gen; + writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); +} + +static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie, + unsigned int win, u64 cpu_addr, + u64 pcie_addr, u64 size) +{ + u32 cpu_addr_mb_high, limit_addr_mb_high; + phys_addr_t cpu_addr_mb, limit_addr_mb; + int high_addr_shift; + u32 tmp; + + /* Set the base of the pcie_addr window */ + writel(lower_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_LO(win)); + writel(upper_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_HI(win)); + + /* Write the addr base & limit lower bits (in MBs) */ + cpu_addr_mb = cpu_addr / SZ_1M; + limit_addr_mb = (cpu_addr + size - 1) / SZ_1M; + + tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win)); + u32p_replace_bits(&tmp, cpu_addr_mb, + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK); + u32p_replace_bits(&tmp, limit_addr_mb, + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK); + writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win)); + + /* Write the cpu & limit addr upper bits */ + high_addr_shift = + HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK); + + cpu_addr_mb_high = cpu_addr_mb >> high_addr_shift; + tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_HI(win)); + u32p_replace_bits(&tmp, cpu_addr_mb_high, + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK); + writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_HI(win)); + + limit_addr_mb_high = limit_addr_mb >> high_addr_shift; + tmp = readl(pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); + u32p_replace_bits(&tmp, limit_addr_mb_high, + PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK); + writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); +} + +static struct irq_chip brcm_msi_irq_chip = { + .name = "BRCM STB PCIe MSI", + .irq_ack = irq_chip_ack_parent, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info brcm_msi_domain_info = { + /* Multi MSI is supported by the controller, but not by this driver */ + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), + .chip = &brcm_msi_irq_chip, +}; + +static void brcm_pcie_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long status, virq; + struct brcm_msi *msi; + struct device *dev; + u32 bit; + + chained_irq_enter(chip, desc); + msi = irq_desc_get_handler_data(desc); + dev = msi->dev; + + status = readl(msi->base + PCIE_MSI_INTR2_STATUS); + for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) { + virq = irq_find_mapping(msi->inner_domain, bit); + if (virq) + generic_handle_irq(virq); + else + dev_dbg(dev, "unexpected MSI\n"); + } + + chained_irq_exit(chip, desc); +} + +static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct brcm_msi *msi = irq_data_get_irq_chip_data(data); + + msg->address_lo = lower_32_bits(msi->target_addr); + msg->address_hi = upper_32_bits(msi->target_addr); + msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq; +} + +static int brcm_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static void brcm_msi_ack_irq(struct irq_data *data) +{ + struct brcm_msi *msi = irq_data_get_irq_chip_data(data); + + writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR); +} + + +static struct irq_chip brcm_msi_bottom_irq_chip = { + .name = "BRCM STB MSI", + .irq_compose_msi_msg = brcm_msi_compose_msi_msg, + .irq_set_affinity = brcm_msi_set_affinity, + .irq_ack = brcm_msi_ack_irq, +}; + +static int brcm_msi_alloc(struct brcm_msi *msi) +{ + int hwirq; + + mutex_lock(&msi->lock); + hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0); + mutex_unlock(&msi->lock); + + return hwirq; +} + +static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq) +{ + mutex_lock(&msi->lock); + bitmap_release_region(&msi->used, hwirq, 0); + mutex_unlock(&msi->lock); +} + +static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct brcm_msi *msi = domain->host_data; + int hwirq; + + hwirq = brcm_msi_alloc(msi); + + if (hwirq < 0) + return hwirq; + + irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq, + &brcm_msi_bottom_irq_chip, domain->host_data, + handle_edge_irq, NULL, NULL); + return 0; +} + +static void brcm_irq_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 brcm_msi *msi = irq_data_get_irq_chip_data(d); + + brcm_msi_free(msi, d->hwirq); +} + +static const struct irq_domain_ops msi_domain_ops = { + .alloc = brcm_irq_domain_alloc, + .free = brcm_irq_domain_free, +}; + +static int brcm_allocate_domains(struct brcm_msi *msi) +{ + struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np); + struct device *dev = msi->dev; + + msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR, + &msi_domain_ops, msi); + if (!msi->inner_domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + msi->msi_domain = pci_msi_create_irq_domain(fwnode, + &brcm_msi_domain_info, + msi->inner_domain); + if (!msi->msi_domain) { + dev_err(dev, "failed to create MSI domain\n"); + irq_domain_remove(msi->inner_domain); + return -ENOMEM; + } + + return 0; +} + +static void brcm_free_domains(struct brcm_msi *msi) +{ + irq_domain_remove(msi->msi_domain); + irq_domain_remove(msi->inner_domain); +} + +static void brcm_msi_remove(struct brcm_pcie *pcie) +{ + struct brcm_msi *msi = pcie->msi; + + if (!msi) + return; + irq_set_chained_handler(msi->irq, NULL); + irq_set_handler_data(msi->irq, NULL); + brcm_free_domains(msi); +} + +static void brcm_msi_set_regs(struct brcm_msi *msi) +{ + writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR); + + /* + * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI + * enable, which we set to 1. + */ + writel(lower_32_bits(msi->target_addr) | 0x1, + msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO); + writel(upper_32_bits(msi->target_addr), + msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI); + + writel(PCIE_MISC_MSI_DATA_CONFIG_VAL, + msi->base + PCIE_MISC_MSI_DATA_CONFIG); +} + +static int brcm_pcie_enable_msi(struct brcm_pcie *pcie) +{ + struct brcm_msi *msi; + int irq, ret; + struct device *dev = pcie->dev; + + irq = irq_of_parse_and_map(dev->of_node, 1); + if (irq <= 0) { + dev_err(dev, "cannot map MSI interrupt\n"); + return -ENODEV; + } + + msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL); + if (!msi) + return -ENOMEM; + + mutex_init(&msi->lock); + msi->dev = dev; + msi->base = pcie->base; + msi->np = pcie->np; + msi->target_addr = pcie->msi_target_addr; + msi->irq = irq; + + ret = brcm_allocate_domains(msi); + if (ret) + return ret; + + irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi); + + brcm_msi_set_regs(msi); + pcie->msi = msi; + + return 0; +} + +/* The controller is capable of serving in both RC and EP roles */ +static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie) +{ + void __iomem *base = pcie->base; + u32 val = readl(base + PCIE_MISC_PCIE_STATUS); + + return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val); +} + +static bool brcm_pcie_link_up(struct brcm_pcie *pcie) +{ + u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS); + u32 dla = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK, val); + u32 plu = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK, val); + + return dla && plu; +} + +/* Configuration space read/write support */ +static inline int brcm_pcie_cfg_index(int busnr, int devfn, int reg) +{ + return ((PCI_SLOT(devfn) & 0x1f) << PCIE_EXT_SLOT_SHIFT) + | ((PCI_FUNC(devfn) & 0x07) << PCIE_EXT_FUNC_SHIFT) + | (busnr << PCIE_EXT_BUSNUM_SHIFT) + | (reg & ~3); +} + +static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct brcm_pcie *pcie = bus->sysdata; + void __iomem *base = pcie->base; + int idx; + + /* Accesses to the RC go right to the RC registers if slot==0 */ + if (pci_is_root_bus(bus)) + return PCI_SLOT(devfn) ? NULL : base + where; + + /* For devices, write to the config space index register */ + idx = brcm_pcie_cfg_index(bus->number, devfn, 0); + writel(idx, pcie->base + PCIE_EXT_CFG_INDEX); + return base + PCIE_EXT_CFG_DATA + where; +} + +static struct pci_ops brcm_pcie_ops = { + .map_bus = brcm_pcie_map_conf, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static inline void brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val) +{ + u32 tmp; + + tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1); + u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_INIT_MASK); + writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1); +} + +static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie, u32 val) +{ + u32 tmp; + + tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1); + u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK); + writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1); +} + +static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie, + u64 *rc_bar2_size, + u64 *rc_bar2_offset) +{ + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + struct device *dev = pcie->dev; + struct resource_entry *entry; + + entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM); + if (!entry) + return -ENODEV; + + + /* + * The controller expects the inbound window offset to be calculated as + * the difference between PCIe's address space and CPU's. The offset + * provided by the firmware is calculated the opposite way, so we + * negate it. + */ + *rc_bar2_offset = -entry->offset; + *rc_bar2_size = 1ULL << fls64(entry->res->end - entry->res->start); + + /* + * We validate the inbound memory view even though we should trust + * whatever the device-tree provides. This is because of an HW issue on + * early Raspberry Pi 4's revisions (bcm2711). It turns out its + * firmware has to dynamically edit dma-ranges due to a bug on the + * PCIe controller integration, which prohibits any access above the + * lower 3GB of memory. Given this, we decided to keep the dma-ranges + * in check, avoiding hard to debug device-tree related issues in the + * future: + * + * The PCIe host controller by design must set the inbound viewport to + * be a contiguous arrangement of all of the system's memory. In + * addition, its size mut be a power of two. To further complicate + * matters, the viewport must start on a pcie-address that is aligned + * on a multiple of its size. If a portion of the viewport does not + * represent system memory -- e.g. 3GB of memory requires a 4GB + * viewport -- we can map the outbound memory in or after 3GB and even + * though the viewport will overlap the outbound memory the controller + * will know to send outbound memory downstream and everything else + * upstream. + * + * For example: + * + * - The best-case scenario, memory up to 3GB, is to place the inbound + * region in the first 4GB of pcie-space, as some legacy devices can + * only address 32bits. We would also like to put the MSI under 4GB + * as well, since some devices require a 32bit MSI target address. + * + * - If the system memory is 4GB or larger we cannot start the inbound + * region at location 0 (since we have to allow some space for + * outbound memory @ 3GB). So instead it will start at the 1x + * multiple of its size + */ + if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size || + (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) { + dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n", + *rc_bar2_size, *rc_bar2_offset); + return -EINVAL; + } + + return 0; +} + +static int brcm_pcie_setup(struct brcm_pcie *pcie) +{ + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + u64 rc_bar2_offset, rc_bar2_size; + void __iomem *base = pcie->base; + struct device *dev = pcie->dev; + struct resource_entry *entry; + unsigned int scb_size_val; + bool ssc_good = false; + struct resource *res; + int num_out_wins = 0; + u16 nlw, cls, lnksta; + int i, ret; + u32 tmp; + + /* Reset the bridge */ + brcm_pcie_bridge_sw_init_set(pcie, 1); + + usleep_range(100, 200); + + /* Take the bridge out of reset */ + brcm_pcie_bridge_sw_init_set(pcie, 0); + + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK; + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + /* Wait for SerDes to be stable */ + usleep_range(100, 200); + + /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */ + u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK); + u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK); + u32p_replace_bits(&tmp, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128, + PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK); + writel(tmp, base + PCIE_MISC_MISC_CTRL); + + ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size, + &rc_bar2_offset); + if (ret) + return ret; + + tmp = lower_32_bits(rc_bar2_offset); + u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size), + PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK); + writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO); + writel(upper_32_bits(rc_bar2_offset), + base + PCIE_MISC_RC_BAR2_CONFIG_HI); + + scb_size_val = rc_bar2_size ? + ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */ + tmp = readl(base + PCIE_MISC_MISC_CTRL); + u32p_replace_bits(&tmp, scb_size_val, + PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK); + writel(tmp, base + PCIE_MISC_MISC_CTRL); + + /* + * We ideally want the MSI target address to be located in the 32bit + * addressable memory area. Some devices might depend on it. This is + * possible either when the inbound window is located above the lower + * 4GB or when the inbound area is smaller than 4GB (taking into + * account the rounding-up we're forced to perform). + */ + if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G) + pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB; + else + pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB; + + /* disable the PCIe->GISB memory window (RC_BAR1) */ + tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO); + tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK; + writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO); + + /* disable the PCIe->SCB memory window (RC_BAR3) */ + tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO); + tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK; + writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO); + + /* Mask all interrupts since we are not handling any yet */ + writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_MASK_SET); + + /* clear any interrupts we find on boot */ + writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_CLR); + + if (pcie->gen) + brcm_pcie_set_gen(pcie, pcie->gen); + + /* Unassert the fundamental reset */ + brcm_pcie_perst_set(pcie, 0); + + /* + * Give the RC/EP time to wake up, before trying to configure RC. + * Intermittently check status for link-up, up to a total of 100ms. + */ + for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5) + msleep(5); + + if (!brcm_pcie_link_up(pcie)) { + dev_err(dev, "link down\n"); + return -ENODEV; + } + + if (!brcm_pcie_rc_mode(pcie)) { + dev_err(dev, "PCIe misconfigured; is in EP mode\n"); + return -EINVAL; + } + + resource_list_for_each_entry(entry, &bridge->windows) { + res = entry->res; + + if (resource_type(res) != IORESOURCE_MEM) + continue; + + if (num_out_wins >= BRCM_NUM_PCIE_OUT_WINS) { + dev_err(pcie->dev, "too many outbound wins\n"); + return -EINVAL; + } + + brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start, + res->start - entry->offset, + resource_size(res)); + num_out_wins++; + } + + /* + * For config space accesses on the RC, show the right class for + * a PCIe-PCIe bridge (the default setting is to be EP mode). + */ + tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3); + u32p_replace_bits(&tmp, 0x060400, + PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK); + writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3); + + if (pcie->ssc) { + ret = brcm_pcie_set_ssc(pcie); + if (ret == 0) + ssc_good = true; + else + dev_err(dev, "failed attempt to enter ssc mode\n"); + } + + lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA); + cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta); + nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); + dev_info(dev, "link up, %s x%u %s\n", + PCIE_SPEED2STR(cls + PCI_SPEED_133MHz_PCIX_533), + nlw, ssc_good ? "(SSC)" : "(!SSC)"); + + /* PCIe->SCB endian mode for BAR */ + tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); + u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN, + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK); + writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); + + /* + * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1 + * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1. + */ + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + + return 0; +} + +/* L23 is a low-power PCIe link state */ +static void brcm_pcie_enter_l23(struct brcm_pcie *pcie) +{ + void __iomem *base = pcie->base; + int l23, i; + u32 tmp; + + /* Assert request for L23 */ + tmp = readl(base + PCIE_MISC_PCIE_CTRL); + u32p_replace_bits(&tmp, 1, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK); + writel(tmp, base + PCIE_MISC_PCIE_CTRL); + + /* Wait up to 36 msec for L23 */ + tmp = readl(base + PCIE_MISC_PCIE_STATUS); + l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, tmp); + for (i = 0; i < 15 && !l23; i++) { + usleep_range(2000, 2400); + tmp = readl(base + PCIE_MISC_PCIE_STATUS); + l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, + tmp); + } + + if (!l23) + dev_err(pcie->dev, "failed to enter low-power link state\n"); +} + +static void brcm_pcie_turn_off(struct brcm_pcie *pcie) +{ + void __iomem *base = pcie->base; + int tmp; + + if (brcm_pcie_link_up(pcie)) + brcm_pcie_enter_l23(pcie); + /* Assert fundamental reset */ + brcm_pcie_perst_set(pcie, 1); + + /* Deassert request for L23 in case it was asserted */ + tmp = readl(base + PCIE_MISC_PCIE_CTRL); + u32p_replace_bits(&tmp, 0, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK); + writel(tmp, base + PCIE_MISC_PCIE_CTRL); + + /* Turn off SerDes */ + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + + /* Shutdown PCIe bridge */ + brcm_pcie_bridge_sw_init_set(pcie, 1); +} + +static void __brcm_pcie_remove(struct brcm_pcie *pcie) +{ + brcm_msi_remove(pcie); + brcm_pcie_turn_off(pcie); + clk_disable_unprepare(pcie->clk); + clk_put(pcie->clk); +} + +static int brcm_pcie_remove(struct platform_device *pdev) +{ + struct brcm_pcie *pcie = platform_get_drvdata(pdev); + + pci_stop_root_bus(pcie->root_bus); + pci_remove_root_bus(pcie->root_bus); + __brcm_pcie_remove(pcie); + + return 0; +} + +static int brcm_pcie_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node, *msi_np; + struct pci_host_bridge *bridge; + struct brcm_pcie *pcie; + struct pci_bus *child; + struct resource *res; + int ret; + + bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie)); + if (!bridge) + return -ENOMEM; + + pcie = pci_host_bridge_priv(bridge); + pcie->dev = &pdev->dev; + pcie->np = np; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcie->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pcie->base)) + return PTR_ERR(pcie->base); + + pcie->clk = devm_clk_get_optional(&pdev->dev, "sw_pcie"); + if (IS_ERR(pcie->clk)) + return PTR_ERR(pcie->clk); + + ret = of_pci_get_max_link_speed(np); + pcie->gen = (ret < 0) ? 0 : ret; + + pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); + + ret = pci_parse_request_of_pci_ranges(pcie->dev, &bridge->windows, + &bridge->dma_ranges, NULL); + if (ret) + return ret; + + ret = clk_prepare_enable(pcie->clk); + if (ret) { + dev_err(&pdev->dev, "could not enable clock\n"); + return ret; + } + + ret = brcm_pcie_setup(pcie); + if (ret) + goto fail; + + msi_np = of_parse_phandle(pcie->np, "msi-parent", 0); + if (pci_msi_enabled() && msi_np == pcie->np) { + ret = brcm_pcie_enable_msi(pcie); + if (ret) { + dev_err(pcie->dev, "probe of internal MSI failed"); + goto fail; + } + } + + bridge->dev.parent = &pdev->dev; + bridge->busnr = 0; + bridge->ops = &brcm_pcie_ops; + bridge->sysdata = pcie; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret < 0) { + dev_err(pcie->dev, "Scanning root bridge failed\n"); + goto fail; + } + + pci_assign_unassigned_bus_resources(bridge->bus); + list_for_each_entry(child, &bridge->bus->children, node) + pcie_bus_configure_settings(child); + pci_bus_add_devices(bridge->bus); + platform_set_drvdata(pdev, pcie); + pcie->root_bus = bridge->bus; + + return 0; +fail: + __brcm_pcie_remove(pcie); + return ret; +} + +static const struct of_device_id brcm_pcie_match[] = { + { .compatible = "brcm,bcm2711-pcie" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcm_pcie_match); + +static struct platform_driver brcm_pcie_driver = { + .probe = brcm_pcie_probe, + .remove = brcm_pcie_remove, + .driver = { + .name = "brcm-pcie", + .of_match_table = brcm_pcie_match, + }, +}; +module_platform_driver(brcm_pcie_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Broadcom STB PCIe RC driver"); +MODULE_AUTHOR("Broadcom"); diff --git a/drivers/pci/controller/pcie-iproc-msi.c b/drivers/pci/controller/pcie-iproc-msi.c index 0a3f61be5625..3176ad3ab0e5 100644 --- a/drivers/pci/controller/pcie-iproc-msi.c +++ b/drivers/pci/controller/pcie-iproc-msi.c @@ -293,11 +293,12 @@ static const struct irq_domain_ops msi_domain_ops = { static inline u32 decode_msi_hwirq(struct iproc_msi *msi, u32 eq, u32 head) { - u32 *msg, hwirq; + u32 __iomem *msg; + u32 hwirq; unsigned int offs; offs = iproc_msi_eq_offset(msi, eq) + head * sizeof(u32); - msg = (u32 *)(msi->eq_cpu + offs); + msg = (u32 __iomem *)(msi->eq_cpu + offs); hwirq = readl(msg); hwirq = (hwirq >> 5) + (hwirq & 0x1f); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index 5a3550b6bb29..ff0a81a632a1 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -43,8 +43,6 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) struct iproc_pcie *pcie; struct device_node *np = dev->of_node; struct resource reg; - resource_size_t iobase = 0; - LIST_HEAD(resources); struct pci_host_bridge *bridge; int ret; @@ -93,15 +91,12 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) pcie->need_ib_cfg = of_property_read_bool(np, "dma-ranges"); /* PHY use is optional */ - pcie->phy = devm_phy_get(dev, "pcie-phy"); - if (IS_ERR(pcie->phy)) { - if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) - return -EPROBE_DEFER; - pcie->phy = NULL; - } + pcie->phy = devm_phy_optional_get(dev, "pcie-phy"); + if (IS_ERR(pcie->phy)) + return PTR_ERR(pcie->phy); - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &resources, - &iobase); + ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, NULL); if (ret) { dev_err(dev, "unable to get PCI host bridge resources\n"); return ret; @@ -116,10 +111,9 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) pcie->map_irq = of_irq_parse_and_map_pci; } - ret = iproc_pcie_setup(pcie, &resources); + ret = iproc_pcie_setup(pcie, &bridge->windows); if (ret) { dev_err(dev, "PCIe controller setup failed\n"); - pci_free_resource_list(&resources); return ret; } diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index 2d457bfdaf66..8c7f875acf7f 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1122,15 +1122,16 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx, } static int iproc_pcie_setup_ib(struct iproc_pcie *pcie, - struct of_pci_range *range, + struct resource_entry *entry, enum iproc_pcie_ib_map_type type) { struct device *dev = pcie->dev; struct iproc_pcie_ib *ib = &pcie->ib; int ret; unsigned int region_idx, size_idx; - u64 axi_addr = range->cpu_addr, pci_addr = range->pci_addr; - resource_size_t size = range->size; + u64 axi_addr = entry->res->start; + u64 pci_addr = entry->res->start - entry->offset; + resource_size_t size = resource_size(entry->res); /* iterate through all IARR mapping regions */ for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) { @@ -1182,67 +1183,46 @@ err_ib: return ret; } -static int iproc_pcie_add_dma_range(struct device *dev, - struct list_head *resources, - struct of_pci_range *range) +static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) { - struct resource *res; - struct resource_entry *entry, *tmp; - struct list_head *head = resources; - - res = devm_kzalloc(dev, sizeof(struct resource), GFP_KERNEL); - if (!res) - return -ENOMEM; + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct resource_entry *entry; + int ret = 0; - resource_list_for_each_entry(tmp, resources) { - if (tmp->res->start < range->cpu_addr) - head = &tmp->node; + resource_list_for_each_entry(entry, &host->dma_ranges) { + /* Each range entry corresponds to an inbound mapping region */ + ret = iproc_pcie_setup_ib(pcie, entry, IPROC_PCIE_IB_MAP_MEM); + if (ret) + break; } - res->start = range->cpu_addr; - res->end = res->start + range->size - 1; - - entry = resource_list_create_entry(res, 0); - if (!entry) - return -ENOMEM; - - entry->offset = res->start - range->cpu_addr; - resource_list_add(entry, head); - - return 0; + return ret; } -static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) +static void iproc_pcie_invalidate_mapping(struct iproc_pcie *pcie) { - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct of_pci_range range; - struct of_pci_range_parser parser; - int ret; - LIST_HEAD(resources); + struct iproc_pcie_ib *ib = &pcie->ib; + struct iproc_pcie_ob *ob = &pcie->ob; + int idx; - /* Get the dma-ranges from DT */ - ret = of_pci_dma_range_parser_init(&parser, pcie->dev->of_node); - if (ret) - return ret; + if (pcie->ep_is_internal) + return; - for_each_of_pci_range(&parser, &range) { - ret = iproc_pcie_add_dma_range(pcie->dev, - &resources, - &range); - if (ret) - goto out; - /* Each range entry corresponds to an inbound mapping region */ - ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_MEM); - if (ret) - goto out; + if (pcie->need_ob_cfg) { + /* iterate through all OARR mapping regions */ + for (idx = ob->nr_windows - 1; idx >= 0; idx--) { + iproc_pcie_write_reg(pcie, + MAP_REG(IPROC_PCIE_OARR0, idx), 0); + } } - list_splice_init(&resources, &host->dma_ranges); - - return 0; -out: - pci_free_resource_list(&resources); - return ret; + if (pcie->need_ib_cfg) { + /* iterate through all IARR mapping regions */ + for (idx = 0; idx < ib->nr_regions; idx++) { + iproc_pcie_write_reg(pcie, + MAP_REG(IPROC_PCIE_IARR0, idx), 0); + } + } } static int iproce_pcie_get_msi(struct iproc_pcie *pcie, @@ -1276,13 +1256,16 @@ static int iproce_pcie_get_msi(struct iproc_pcie *pcie, static int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr) { int ret; - struct of_pci_range range; + struct resource_entry entry; - memset(&range, 0, sizeof(range)); - range.size = SZ_32K; - range.pci_addr = range.cpu_addr = msi_addr & ~(range.size - 1); + memset(&entry, 0, sizeof(entry)); + entry.res = &entry.__res; - ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_IO); + msi_addr &= ~(SZ_32K - 1); + entry.res->start = msi_addr; + entry.res->end = msi_addr + SZ_32K - 1; + + ret = iproc_pcie_setup_ib(pcie, &entry, IPROC_PCIE_IB_MAP_IO); return ret; } @@ -1498,10 +1481,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) return ret; } - ret = devm_request_pci_bus_resources(dev, res); - if (ret) - return ret; - ret = phy_init(pcie->phy); if (ret) { dev_err(dev, "unable to initialize PCIe PHY\n"); @@ -1517,6 +1496,8 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) iproc_pcie_perst_ctrl(pcie, true); iproc_pcie_perst_ctrl(pcie, false); + iproc_pcie_invalidate_mapping(pcie); + if (pcie->need_ob_cfg) { ret = iproc_pcie_map_ranges(pcie, res); if (ret) { @@ -1543,7 +1524,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) if (iproc_pcie_msi_enable(pcie)) dev_info(dev, "not using iProc MSI\n"); - list_splice_init(res, &host->windows); host->busnr = 0; host->dev.parent = dev; host->ops = &iproc_pcie_ops; @@ -1608,6 +1588,30 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_disable_msi_parsing); +static void quirk_paxc_bridge(struct pci_dev *pdev) +{ + /* + * The PCI config space is shared with the PAXC root port and the first + * Ethernet device. So, we need to workaround this by telling the PCI + * code that the bridge is not an Ethernet device. + */ + if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + pdev->class = PCI_CLASS_BRIDGE_PCI << 8; + + /* + * MPSS is not being set properly (as it is currently 0). This is + * because that area of the PCI config space is hard coded to zero, and + * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS) + * so that the MPS can be set to the real max value. + */ + pdev->pcie_mpss = 2; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge); + MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 80601e1b939e..cb982891b22b 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -73,6 +73,7 @@ #define PCIE_MSI_VECTOR 0x0c0 #define PCIE_CONF_VEND_ID 0x100 +#define PCIE_CONF_DEVICE_ID 0x102 #define PCIE_CONF_CLASS_ID 0x106 #define PCIE_INT_MASK 0x420 @@ -141,12 +142,16 @@ 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 + * @device_id: device ID which this host need to be fixed * @ops: pointer to configuration access functions * @startup: pointer to controller setting functions * @setup_irq: pointer to initialize IRQ functions */ struct mtk_pcie_soc { bool need_fix_class_id; + bool need_fix_device_id; + unsigned int device_id; struct pci_ops *ops; int (*startup)(struct mtk_pcie_port *port); int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node); @@ -211,7 +216,6 @@ struct mtk_pcie { void __iomem *base; struct clk *free_ck; - struct resource mem; struct list_head ports; const struct mtk_pcie_soc *soc; unsigned int busnr; @@ -630,8 +634,6 @@ static void mtk_pcie_intr_handler(struct irq_desc *desc) } chained_irq_exit(irqchip, desc); - - return; } static int mtk_pcie_setup_irq(struct mtk_pcie_port *port, @@ -658,11 +660,19 @@ static int mtk_pcie_setup_irq(struct mtk_pcie_port *port, static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port) { struct mtk_pcie *pcie = port->pcie; - struct resource *mem = &pcie->mem; + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct resource *mem = NULL; + struct resource_entry *entry; const struct mtk_pcie_soc *soc = port->pcie->soc; u32 val; int err; + entry = resource_list_first_type(&host->windows, IORESOURCE_MEM); + if (entry) + mem = entry->res; + if (!mem) + return -EINVAL; + /* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */ if (pcie->base) { val = readl(pcie->base + PCIE_SYS_CFG_V2); @@ -696,6 +706,9 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port) writew(val, port->base + PCIE_CONF_CLASS_ID); } + if (soc->need_fix_device_id) + writew(soc->device_id, port->base + PCIE_CONF_DEVICE_ID); + /* 100ms timeout value should be enough for Gen1/2 training */ err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_V2, val, !!(val & PCIE_PORT_LINKUP_V2), 20, @@ -1017,39 +1030,15 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) struct mtk_pcie_port *port, *tmp; struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct list_head *windows = &host->windows; - struct resource_entry *win, *tmp_win; - resource_size_t io_base; + struct resource *bus; int err; - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - windows, &io_base); + err = pci_parse_request_of_pci_ranges(dev, windows, + &host->dma_ranges, &bus); if (err) return err; - err = devm_request_pci_bus_resources(dev, windows); - if (err < 0) - return err; - - /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry_safe(win, tmp_win, windows) { - switch (resource_type(win->res)) { - case IORESOURCE_IO: - err = devm_pci_remap_iospace(dev, win->res, io_base); - if (err) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - err, win->res); - resource_list_destroy_entry(win); - } - break; - case IORESOURCE_MEM: - memcpy(&pcie->mem, win->res, sizeof(*win->res)); - pcie->mem.name = "non-prefetchable"; - break; - case IORESOURCE_BUS: - pcie->busnr = win->res->start; - break; - } - } + pcie->busnr = bus->start; for_each_available_child_of_node(node, child) { int slot; @@ -1216,11 +1205,21 @@ static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = { .setup_irq = mtk_pcie_setup_irq, }; +static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = { + .need_fix_class_id = true, + .need_fix_device_id = true, + .device_id = PCI_DEVICE_ID_MEDIATEK_7629, + .ops = &mtk_pcie_ops_v2, + .startup = mtk_pcie_startup_port_v2, + .setup_irq = mtk_pcie_setup_irq, +}; + static const struct of_device_id mtk_pcie_ids[] = { { .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 }, { .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 }, { .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_mt2712 }, { .compatible = "mediatek,mt7622-pcie", .data = &mtk_pcie_soc_mt7622 }, + { .compatible = "mediatek,mt7629-pcie", .data = &mtk_pcie_soc_mt7629 }, {}, }; diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 672e633601c7..3a696ca45bfa 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -88,6 +88,7 @@ #define AMAP_CTRL_TYPE_MASK 3 #define PAB_EXT_PEX_AMAP_SIZEN(win) PAB_EXT_REG_ADDR(0xbef0, win) +#define PAB_EXT_PEX_AMAP_AXI_WIN(win) PAB_EXT_REG_ADDR(0xb4a0, win) #define PAB_PEX_AMAP_AXI_WIN(win) PAB_REG_ADDR(0x4ba4, win) #define PAB_PEX_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x4ba8, win) #define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win) @@ -139,7 +140,6 @@ struct mobiveil_msi { /* MSI information */ struct mobiveil_pcie { struct platform_device *pdev; - struct list_head resources; void __iomem *config_axi_slave_base; /* endpoint config base */ void __iomem *csr_axi_slave_base; /* root port config base */ void __iomem *apb_csr_base; /* MSI register base */ @@ -234,7 +234,7 @@ static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val) return PCIBIOS_SUCCESSFUL; } -static u32 csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size) +static u32 mobiveil_csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size) { void *addr; u32 val; @@ -249,7 +249,8 @@ static u32 csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size) return val; } -static void csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, size_t size) +static void mobiveil_csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, + size_t size) { void *addr; int ret; @@ -261,19 +262,19 @@ static void csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, size_t size) dev_err(&pcie->pdev->dev, "write CSR address failed\n"); } -static u32 csr_readl(struct mobiveil_pcie *pcie, u32 off) +static u32 mobiveil_csr_readl(struct mobiveil_pcie *pcie, u32 off) { - return csr_read(pcie, off, 0x4); + return mobiveil_csr_read(pcie, off, 0x4); } -static void csr_writel(struct mobiveil_pcie *pcie, u32 val, u32 off) +static void mobiveil_csr_writel(struct mobiveil_pcie *pcie, u32 val, u32 off) { - csr_write(pcie, val, off, 0x4); + mobiveil_csr_write(pcie, val, off, 0x4); } static bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie) { - return (csr_readl(pcie, LTSSM_STATUS) & + return (mobiveil_csr_readl(pcie, LTSSM_STATUS) & LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0; } @@ -322,7 +323,7 @@ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, PCI_SLOT(devfn) << PAB_DEVICE_SHIFT | PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT; - csr_writel(pcie, value, PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0)); + mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0)); return pcie->config_axi_slave_base + where; } @@ -352,13 +353,14 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) chained_irq_enter(chip, desc); /* read INTx status */ - val = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT); - mask = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); + val = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT); + mask = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); intr_status = val & mask; /* Handle INTx */ if (intr_status & PAB_INTP_INTX_MASK) { - shifted_status = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT); + shifted_status = mobiveil_csr_readl(pcie, + PAB_INTP_AMBA_MISC_STAT); shifted_status &= PAB_INTP_INTX_MASK; shifted_status >>= PAB_INTX_START; do { @@ -372,12 +374,13 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) bit); /* clear interrupt handled */ - csr_writel(pcie, 1 << (PAB_INTX_START + bit), - PAB_INTP_AMBA_MISC_STAT); + mobiveil_csr_writel(pcie, + 1 << (PAB_INTX_START + bit), + PAB_INTP_AMBA_MISC_STAT); } - shifted_status = csr_readl(pcie, - PAB_INTP_AMBA_MISC_STAT); + shifted_status = mobiveil_csr_readl(pcie, + PAB_INTP_AMBA_MISC_STAT); shifted_status &= PAB_INTP_INTX_MASK; shifted_status >>= PAB_INTX_START; } while (shifted_status != 0); @@ -412,7 +415,7 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) } /* Clear the interrupt status */ - csr_writel(pcie, intr_status, PAB_INTP_AMBA_MISC_STAT); + mobiveil_csr_writel(pcie, intr_status, PAB_INTP_AMBA_MISC_STAT); chained_irq_exit(chip, desc); } @@ -462,7 +465,7 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) } static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, - u64 pci_addr, u32 type, u64 size) + u64 cpu_addr, u64 pci_addr, u32 type, u64 size) { u32 value; u64 size64 = ~(size - 1); @@ -473,21 +476,24 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, return; } - value = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); + value = mobiveil_csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK); value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT | (lower_32_bits(size64) & WIN_SIZE_MASK); - csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); + mobiveil_csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); - csr_writel(pcie, upper_32_bits(size64), - PAB_EXT_PEX_AMAP_SIZEN(win_num)); + mobiveil_csr_writel(pcie, upper_32_bits(size64), + PAB_EXT_PEX_AMAP_SIZEN(win_num)); - csr_writel(pcie, pci_addr, PAB_PEX_AMAP_AXI_WIN(win_num)); + mobiveil_csr_writel(pcie, lower_32_bits(cpu_addr), + PAB_PEX_AMAP_AXI_WIN(win_num)); + mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr), + PAB_EXT_PEX_AMAP_AXI_WIN(win_num)); - csr_writel(pcie, lower_32_bits(pci_addr), - PAB_PEX_AMAP_PEX_WIN_L(win_num)); - csr_writel(pcie, upper_32_bits(pci_addr), - PAB_PEX_AMAP_PEX_WIN_H(win_num)); + mobiveil_csr_writel(pcie, lower_32_bits(pci_addr), + PAB_PEX_AMAP_PEX_WIN_L(win_num)); + mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), + PAB_PEX_AMAP_PEX_WIN_H(win_num)); pcie->ib_wins_configured++; } @@ -511,27 +517,29 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit * to 4 KB in PAB_AXI_AMAP_CTRL register */ - value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); + value = mobiveil_csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK); value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | (lower_32_bits(size64) & WIN_SIZE_MASK); - csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num)); + mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num)); - csr_writel(pcie, upper_32_bits(size64), PAB_EXT_AXI_AMAP_SIZE(win_num)); + mobiveil_csr_writel(pcie, upper_32_bits(size64), + PAB_EXT_AXI_AMAP_SIZE(win_num)); /* * program AXI window base with appropriate value in * PAB_AXI_AMAP_AXI_WIN0 register */ - csr_writel(pcie, lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK), - PAB_AXI_AMAP_AXI_WIN(win_num)); - csr_writel(pcie, upper_32_bits(cpu_addr), - PAB_EXT_AXI_AMAP_AXI_WIN(win_num)); + mobiveil_csr_writel(pcie, + lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK), + PAB_AXI_AMAP_AXI_WIN(win_num)); + mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr), + PAB_EXT_AXI_AMAP_AXI_WIN(win_num)); - csr_writel(pcie, lower_32_bits(pci_addr), - PAB_AXI_AMAP_PEX_WIN_L(win_num)); - csr_writel(pcie, upper_32_bits(pci_addr), - PAB_AXI_AMAP_PEX_WIN_H(win_num)); + mobiveil_csr_writel(pcie, lower_32_bits(pci_addr), + PAB_AXI_AMAP_PEX_WIN_L(win_num)); + mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), + PAB_AXI_AMAP_PEX_WIN_H(win_num)); pcie->ob_wins_configured++; } @@ -571,46 +579,47 @@ static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie) static int mobiveil_host_init(struct mobiveil_pcie *pcie) { + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); u32 value, pab_ctrl, type; struct resource_entry *win; /* setup bus numbers */ - value = csr_readl(pcie, PCI_PRIMARY_BUS); + value = mobiveil_csr_readl(pcie, PCI_PRIMARY_BUS); value &= 0xff000000; value |= 0x00ff0100; - csr_writel(pcie, value, PCI_PRIMARY_BUS); + mobiveil_csr_writel(pcie, value, PCI_PRIMARY_BUS); /* * program Bus Master Enable Bit in Command Register in PAB Config * Space */ - value = csr_readl(pcie, PCI_COMMAND); + value = mobiveil_csr_readl(pcie, PCI_COMMAND); value |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; - csr_writel(pcie, value, PCI_COMMAND); + mobiveil_csr_writel(pcie, value, PCI_COMMAND); /* * program PIO Enable Bit to 1 (and PEX PIO Enable to 1) in PAB_CTRL * register */ - pab_ctrl = csr_readl(pcie, PAB_CTRL); + pab_ctrl = mobiveil_csr_readl(pcie, PAB_CTRL); pab_ctrl |= (1 << AMBA_PIO_ENABLE_SHIFT) | (1 << PEX_PIO_ENABLE_SHIFT); - csr_writel(pcie, pab_ctrl, PAB_CTRL); + mobiveil_csr_writel(pcie, pab_ctrl, PAB_CTRL); - csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK), - PAB_INTP_AMBA_MISC_ENB); + mobiveil_csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK), + PAB_INTP_AMBA_MISC_ENB); /* * program PIO Enable Bit to 1 and Config Window Enable Bit to 1 in * PAB_AXI_PIO_CTRL Register */ - value = csr_readl(pcie, PAB_AXI_PIO_CTRL); + value = mobiveil_csr_readl(pcie, PAB_AXI_PIO_CTRL); value |= APIO_EN_MASK; - csr_writel(pcie, value, PAB_AXI_PIO_CTRL); + mobiveil_csr_writel(pcie, value, PAB_AXI_PIO_CTRL); /* Enable PCIe PIO master */ - value = csr_readl(pcie, PAB_PEX_PIO_CTRL); + value = mobiveil_csr_readl(pcie, PAB_PEX_PIO_CTRL); value |= 1 << PIO_ENABLE_SHIFT; - csr_writel(pcie, value, PAB_PEX_PIO_CTRL); + mobiveil_csr_writel(pcie, value, PAB_PEX_PIO_CTRL); /* * we'll program one outbound window for config reads and @@ -624,10 +633,10 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) CFG_WINDOW_TYPE, resource_size(pcie->ob_io_res)); /* memory inbound translation window */ - program_ib_windows(pcie, WIN_NUM_0, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE); + program_ib_windows(pcie, WIN_NUM_0, 0, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE); /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry(win, &pcie->resources) { + resource_list_for_each_entry(win, &bridge->windows) { if (resource_type(win->res) == IORESOURCE_MEM) type = MEM_WINDOW_TYPE; else if (resource_type(win->res) == IORESOURCE_IO) @@ -643,10 +652,10 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) } /* fixup for PCIe class register */ - value = csr_readl(pcie, PAB_INTP_AXI_PIO_CLASS); + value = mobiveil_csr_readl(pcie, PAB_INTP_AXI_PIO_CLASS); value &= 0xff; value |= (PCI_CLASS_BRIDGE_PCI << 16); - csr_writel(pcie, value, PAB_INTP_AXI_PIO_CLASS); + mobiveil_csr_writel(pcie, value, PAB_INTP_AXI_PIO_CLASS); /* setup MSI hardware registers */ mobiveil_pcie_enable_msi(pcie); @@ -664,9 +673,9 @@ static void mobiveil_mask_intx_irq(struct irq_data *data) pcie = irq_desc_get_chip_data(desc); mask = 1 << ((data->hwirq + PAB_INTX_START) - 1); raw_spin_lock_irqsave(&pcie->intx_mask_lock, flags); - shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); + shifted_val = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); shifted_val &= ~mask; - csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB); + mobiveil_csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB); raw_spin_unlock_irqrestore(&pcie->intx_mask_lock, flags); } @@ -680,9 +689,9 @@ static void mobiveil_unmask_intx_irq(struct irq_data *data) pcie = irq_desc_get_chip_data(desc); mask = 1 << ((data->hwirq + PAB_INTX_START) - 1); raw_spin_lock_irqsave(&pcie->intx_mask_lock, flags); - shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); + shifted_val = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); shifted_val |= mask; - csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB); + mobiveil_csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB); raw_spin_unlock_irqrestore(&pcie->intx_mask_lock, flags); } @@ -853,7 +862,6 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) struct pci_bus *child; struct pci_host_bridge *bridge; struct device *dev = &pdev->dev; - resource_size_t iobase; int ret; /* allocate the PCIe port */ @@ -871,11 +879,9 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) return ret; } - INIT_LIST_HEAD(&pcie->resources); - /* parse the host bridge base addresses from the device tree file */ - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &pcie->resources, &iobase); + ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, NULL); if (ret) { dev_err(dev, "Getting bridge resources failed\n"); return ret; @@ -888,24 +894,19 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) ret = mobiveil_host_init(pcie); if (ret) { dev_err(dev, "Failed to initialize host\n"); - goto error; + return ret; } /* initialize the IRQ domains */ ret = mobiveil_pcie_init_irq_domain(pcie); if (ret) { dev_err(dev, "Failed creating IRQ Domain\n"); - goto error; + return ret; } irq_set_chained_handler_and_data(pcie->irq, mobiveil_pcie_isr, pcie); - ret = devm_request_pci_bus_resources(dev, &pcie->resources); - if (ret) - goto error; - /* Initialize bridge */ - list_splice_init(&pcie->resources, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = pcie->root_bus_nr; @@ -916,13 +917,13 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) ret = mobiveil_bringup_link(pcie); if (ret) { dev_info(dev, "link bring-up failed\n"); - goto error; + return ret; } /* setup the kernel resources for the newly added PCIe root bus */ ret = pci_scan_root_bus_bridge(bridge); if (ret) - goto error; + return ret; bus = bridge->bus; @@ -932,9 +933,6 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) pci_bus_add_devices(bus); return 0; -error: - pci_free_resource_list(&pcie->resources); - return ret; } static const struct of_device_id mobiveil_pcie_of_match[] = { diff --git a/drivers/pci/controller/pcie-rcar.c b/drivers/pci/controller/pcie-rcar.c index f6a669a9af41..759c6542c5c8 100644 --- a/drivers/pci/controller/pcie-rcar.c +++ b/drivers/pci/controller/pcie-rcar.c @@ -30,8 +30,6 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> -#include "../pci.h" - #define PCIECAR 0x000010 #define PCIECCTLR 0x000018 #define CONFIG_SEND_ENABLE BIT(31) @@ -93,8 +91,11 @@ #define LINK_SPEED_2_5GTS (1 << 16) #define LINK_SPEED_5_0GTS (2 << 16) #define MACCTLR 0x011058 +#define MACCTLR_NFTS_MASK GENMASK(23, 16) /* The name is from SH7786 */ #define SPEED_CHANGE BIT(24) #define SCRAMBLE_DISABLE BIT(27) +#define LTSMDIS BIT(31) +#define MACCTLR_INIT_VAL (LTSMDIS | MACCTLR_NFTS_MASK) #define PMSR 0x01105c #define MACS2R 0x011078 #define MACCGSPSETR 0x011084 @@ -615,6 +616,8 @@ static int rcar_pcie_hw_init(struct rcar_pcie *pcie) if (IS_ENABLED(CONFIG_PCI_MSI)) rcar_pci_write_reg(pcie, 0x801f0000, PCIEMSITXR); + rcar_pci_write_reg(pcie, MACCTLR_INIT_VAL, MACCTLR); + /* Finish initialization - establish a PCI Express link */ rcar_pci_write_reg(pcie, CFINIT, PCIETCTLR); @@ -1014,40 +1017,43 @@ err_irq1: } static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie, - struct of_pci_range *range, + struct resource_entry *entry, int *index) { - u64 restype = range->flags; - u64 cpu_addr = range->cpu_addr; - u64 cpu_end = range->cpu_addr + range->size; - u64 pci_addr = range->pci_addr; + u64 restype = entry->res->flags; + u64 cpu_addr = entry->res->start; + u64 cpu_end = entry->res->end; + u64 pci_addr = entry->res->start - entry->offset; u32 flags = LAM_64BIT | LAR_ENABLE; u64 mask; - u64 size; + u64 size = resource_size(entry->res); int idx = *index; if (restype & IORESOURCE_PREFETCH) flags |= LAM_PREFETCH; - /* - * If the size of the range is larger than the alignment of the start - * address, we have to use multiple entries to perform the mapping. - */ - if (cpu_addr > 0) { - unsigned long nr_zeros = __ffs64(cpu_addr); - u64 alignment = 1ULL << nr_zeros; + while (cpu_addr < cpu_end) { + if (idx >= MAX_NR_INBOUND_MAPS - 1) { + dev_err(pcie->dev, "Failed to map inbound regions!\n"); + return -EINVAL; + } + /* + * If the size of the range is larger than the alignment of + * the start address, we have to use multiple entries to + * perform the mapping. + */ + if (cpu_addr > 0) { + unsigned long nr_zeros = __ffs64(cpu_addr); + u64 alignment = 1ULL << nr_zeros; - size = min(range->size, alignment); - } else { - size = range->size; - } - /* Hardware supports max 4GiB inbound region */ - size = min(size, 1ULL << 32); + size = min(size, alignment); + } + /* Hardware supports max 4GiB inbound region */ + size = min(size, 1ULL << 32); - mask = roundup_pow_of_two(size) - 1; - mask &= ~0xf; + mask = roundup_pow_of_two(size) - 1; + mask &= ~0xf; - while (cpu_addr < cpu_end) { /* * Set up 64-bit inbound regions as the range parser doesn't * distinguish between 32 and 64-bit types. @@ -1067,41 +1073,25 @@ static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie, pci_addr += size; cpu_addr += size; idx += 2; - - if (idx > MAX_NR_INBOUND_MAPS) { - dev_err(pcie->dev, "Failed to map inbound regions!\n"); - return -EINVAL; - } } *index = idx; return 0; } -static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie, - struct device_node *np) +static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie) { - struct of_pci_range range; - struct of_pci_range_parser parser; - int index = 0; - int err; - - if (of_pci_dma_range_parser_init(&parser, np)) - return -EINVAL; - - /* Get the dma-ranges from DT */ - for_each_of_pci_range(&parser, &range) { - u64 end = range.cpu_addr + range.size - 1; - - dev_dbg(pcie->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", - range.flags, range.cpu_addr, end, range.pci_addr); + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + struct resource_entry *entry; + int index = 0, err = 0; - err = rcar_pcie_inbound_ranges(pcie, &range, &index); + resource_list_for_each_entry(entry, &bridge->dma_ranges) { + err = rcar_pcie_inbound_ranges(pcie, entry, &index); if (err) - return err; + break; } - return 0; + return err; } static const struct of_device_id rcar_pcie_of_match[] = { @@ -1138,7 +1128,8 @@ static int rcar_pcie_probe(struct platform_device *pdev) pcie->dev = dev; platform_set_drvdata(pdev, pcie); - err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, NULL); + err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, + &bridge->dma_ranges, NULL); if (err) goto err_free_bridge; @@ -1161,7 +1152,7 @@ static int rcar_pcie_probe(struct platform_device *pdev) goto err_unmap_msi_irqs; } - err = rcar_pcie_parse_map_dma_ranges(pcie, dev->of_node); + err = rcar_pcie_parse_map_dma_ranges(pcie); if (err) goto err_clk_disable; @@ -1237,6 +1228,7 @@ static int rcar_pcie_resume_noirq(struct device *dev) return 0; /* Re-establish the PCIe link */ + rcar_pci_write_reg(pcie, MACCTLR_INIT_VAL, MACCTLR); rcar_pci_write_reg(pcie, CFINIT, PCIETCTLR); return rcar_pcie_wait_for_dl(pcie); } diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index 8d20f1793a61..94af6f5828a3 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -608,31 +608,25 @@ static int rockchip_pcie_parse_host_dt(struct rockchip_pcie *rockchip) rockchip->vpcie12v = devm_regulator_get_optional(dev, "vpcie12v"); if (IS_ERR(rockchip->vpcie12v)) { - if (PTR_ERR(rockchip->vpcie12v) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (PTR_ERR(rockchip->vpcie12v) != -ENODEV) + return PTR_ERR(rockchip->vpcie12v); dev_info(dev, "no vpcie12v regulator found\n"); } rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3"); if (IS_ERR(rockchip->vpcie3v3)) { - if (PTR_ERR(rockchip->vpcie3v3) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV) + return PTR_ERR(rockchip->vpcie3v3); dev_info(dev, "no vpcie3v3 regulator found\n"); } - rockchip->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8"); - if (IS_ERR(rockchip->vpcie1v8)) { - if (PTR_ERR(rockchip->vpcie1v8) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_info(dev, "no vpcie1v8 regulator found\n"); - } + rockchip->vpcie1v8 = devm_regulator_get(dev, "vpcie1v8"); + if (IS_ERR(rockchip->vpcie1v8)) + return PTR_ERR(rockchip->vpcie1v8); - rockchip->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9"); - if (IS_ERR(rockchip->vpcie0v9)) { - if (PTR_ERR(rockchip->vpcie0v9) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_info(dev, "no vpcie0v9 regulator found\n"); - } + rockchip->vpcie0v9 = devm_regulator_get(dev, "vpcie0v9"); + if (IS_ERR(rockchip->vpcie0v9)) + return PTR_ERR(rockchip->vpcie0v9); return 0; } @@ -658,27 +652,22 @@ static int rockchip_pcie_set_vpcie(struct rockchip_pcie *rockchip) } } - if (!IS_ERR(rockchip->vpcie1v8)) { - err = regulator_enable(rockchip->vpcie1v8); - if (err) { - dev_err(dev, "fail to enable vpcie1v8 regulator\n"); - goto err_disable_3v3; - } + err = regulator_enable(rockchip->vpcie1v8); + if (err) { + dev_err(dev, "fail to enable vpcie1v8 regulator\n"); + goto err_disable_3v3; } - if (!IS_ERR(rockchip->vpcie0v9)) { - err = regulator_enable(rockchip->vpcie0v9); - if (err) { - dev_err(dev, "fail to enable vpcie0v9 regulator\n"); - goto err_disable_1v8; - } + err = regulator_enable(rockchip->vpcie0v9); + if (err) { + dev_err(dev, "fail to enable vpcie0v9 regulator\n"); + goto err_disable_1v8; } return 0; err_disable_1v8: - if (!IS_ERR(rockchip->vpcie1v8)) - regulator_disable(rockchip->vpcie1v8); + regulator_disable(rockchip->vpcie1v8); err_disable_3v3: if (!IS_ERR(rockchip->vpcie3v3)) regulator_disable(rockchip->vpcie3v3); @@ -806,19 +795,28 @@ static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie *rockchip, static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip) { struct device *dev = rockchip->dev; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rockchip); + struct resource_entry *entry; + u64 pci_addr, size; int offset; int err; int reg_no; rockchip_pcie_cfg_configuration_accesses(rockchip, AXI_WRAPPER_TYPE0_CFG); + entry = resource_list_first_type(&bridge->windows, IORESOURCE_MEM); + if (!entry) + return -ENODEV; + + size = resource_size(entry->res); + pci_addr = entry->res->start - entry->offset; + rockchip->msg_bus_addr = pci_addr; - for (reg_no = 0; reg_no < (rockchip->mem_size >> 20); reg_no++) { + for (reg_no = 0; reg_no < (size >> 20); reg_no++) { err = rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1, AXI_WRAPPER_MEM_WRITE, 20 - 1, - rockchip->mem_bus_addr + - (reg_no << 20), + pci_addr + (reg_no << 20), 0); if (err) { dev_err(dev, "program RC mem outbound ATU failed\n"); @@ -832,14 +830,22 @@ static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip) return err; } - offset = rockchip->mem_size >> 20; - for (reg_no = 0; reg_no < (rockchip->io_size >> 20); reg_no++) { + entry = resource_list_first_type(&bridge->windows, IORESOURCE_IO); + if (!entry) + return -ENODEV; + + /* store the register number offset to program RC io outbound ATU */ + offset = size >> 20; + + size = resource_size(entry->res); + pci_addr = entry->res->start - entry->offset; + + for (reg_no = 0; reg_no < (size >> 20); reg_no++) { err = rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset, AXI_WRAPPER_IO_WRITE, 20 - 1, - rockchip->io_bus_addr + - (reg_no << 20), + pci_addr + (reg_no << 20), 0); if (err) { dev_err(dev, "program RC io outbound ATU failed\n"); @@ -852,8 +858,7 @@ static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip) AXI_WRAPPER_NOR_MSG, 20 - 1, 0, 0); - rockchip->msg_bus_addr = rockchip->mem_bus_addr + - ((reg_no + offset) << 20); + rockchip->msg_bus_addr += ((reg_no + offset) << 20); return err; } @@ -897,8 +902,7 @@ static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) rockchip_pcie_disable_clocks(rockchip); - if (!IS_ERR(rockchip->vpcie0v9)) - regulator_disable(rockchip->vpcie0v9); + regulator_disable(rockchip->vpcie0v9); return ret; } @@ -908,12 +912,10 @@ static int __maybe_unused rockchip_pcie_resume_noirq(struct device *dev) struct rockchip_pcie *rockchip = dev_get_drvdata(dev); int err; - if (!IS_ERR(rockchip->vpcie0v9)) { - err = regulator_enable(rockchip->vpcie0v9); - if (err) { - dev_err(dev, "fail to enable vpcie0v9 regulator\n"); - return err; - } + err = regulator_enable(rockchip->vpcie0v9); + if (err) { + dev_err(dev, "fail to enable vpcie0v9 regulator\n"); + return err; } err = rockchip_pcie_enable_clocks(rockchip); @@ -939,8 +941,7 @@ err_err_deinit_port: err_pcie_resume: rockchip_pcie_disable_clocks(rockchip); err_disable_0v9: - if (!IS_ERR(rockchip->vpcie0v9)) - regulator_disable(rockchip->vpcie0v9); + regulator_disable(rockchip->vpcie0v9); return err; } @@ -950,14 +951,9 @@ static int rockchip_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct pci_bus *bus, *child; struct pci_host_bridge *bridge; - struct resource_entry *win; - resource_size_t io_base; - struct resource *mem; - struct resource *io; + struct resource *bus_res; int err; - LIST_HEAD(res); - if (!dev->of_node) return -ENODEV; @@ -995,56 +991,23 @@ static int rockchip_pcie_probe(struct platform_device *pdev) if (err < 0) goto err_deinit_port; - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &res, &io_base); + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, &bus_res); if (err) goto err_remove_irq_domain; - err = devm_request_pci_bus_resources(dev, &res); - if (err) - goto err_free_res; - - /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry(win, &res) { - switch (resource_type(win->res)) { - case IORESOURCE_IO: - io = win->res; - io->name = "I/O"; - rockchip->io_size = resource_size(io); - rockchip->io_bus_addr = io->start - win->offset; - err = pci_remap_iospace(io, io_base); - if (err) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - err, io); - continue; - } - rockchip->io = io; - break; - case IORESOURCE_MEM: - mem = win->res; - mem->name = "MEM"; - rockchip->mem_size = resource_size(mem); - rockchip->mem_bus_addr = mem->start - win->offset; - break; - case IORESOURCE_BUS: - rockchip->root_bus_nr = win->res->start; - break; - default: - continue; - } - } + rockchip->root_bus_nr = bus_res->start; err = rockchip_pcie_cfg_atu(rockchip); if (err) - goto err_unmap_iospace; + goto err_remove_irq_domain; rockchip->msg_region = devm_ioremap(dev, rockchip->msg_bus_addr, SZ_1M); if (!rockchip->msg_region) { err = -ENOMEM; - goto err_unmap_iospace; + goto err_remove_irq_domain; } - list_splice_init(&res, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = rockchip; bridge->busnr = 0; @@ -1054,7 +1017,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) err = pci_scan_root_bus_bridge(bridge); if (err < 0) - goto err_unmap_iospace; + goto err_remove_irq_domain; bus = bridge->bus; @@ -1068,10 +1031,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev) pci_bus_add_devices(bus); return 0; -err_unmap_iospace: - pci_unmap_iospace(rockchip->io); -err_free_res: - pci_free_resource_list(&res); err_remove_irq_domain: irq_domain_remove(rockchip->irq_domain); err_deinit_port: @@ -1081,10 +1040,8 @@ err_vpcie: regulator_disable(rockchip->vpcie12v); if (!IS_ERR(rockchip->vpcie3v3)) regulator_disable(rockchip->vpcie3v3); - if (!IS_ERR(rockchip->vpcie1v8)) - regulator_disable(rockchip->vpcie1v8); - if (!IS_ERR(rockchip->vpcie0v9)) - regulator_disable(rockchip->vpcie0v9); + regulator_disable(rockchip->vpcie1v8); + regulator_disable(rockchip->vpcie0v9); err_set_vpcie: rockchip_pcie_disable_clocks(rockchip); return err; @@ -1097,7 +1054,6 @@ static int rockchip_pcie_remove(struct platform_device *pdev) pci_stop_root_bus(rockchip->root_bus); pci_remove_root_bus(rockchip->root_bus); - pci_unmap_iospace(rockchip->io); irq_domain_remove(rockchip->irq_domain); rockchip_pcie_deinit_phys(rockchip); @@ -1108,10 +1064,8 @@ static int rockchip_pcie_remove(struct platform_device *pdev) regulator_disable(rockchip->vpcie12v); if (!IS_ERR(rockchip->vpcie3v3)) regulator_disable(rockchip->vpcie3v3); - if (!IS_ERR(rockchip->vpcie1v8)) - regulator_disable(rockchip->vpcie1v8); - if (!IS_ERR(rockchip->vpcie0v9)) - regulator_disable(rockchip->vpcie0v9); + regulator_disable(rockchip->vpcie1v8); + regulator_disable(rockchip->vpcie0v9); return 0; } diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index 8e87a059ce73..d90dfb354573 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Rockchip AXI PCIe controller driver * @@ -304,13 +304,8 @@ struct rockchip_pcie { struct irq_domain *irq_domain; int offset; struct pci_bus *root_bus; - struct resource *io; - phys_addr_t io_bus_addr; - u32 io_size; void __iomem *msg_region; - u32 mem_size; phys_addr_t msg_bus_addr; - phys_addr_t mem_bus_addr; bool is_rc; struct resource *mem_res; }; diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 45c0f344ccd1..9bd1427f2fd6 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -821,8 +821,6 @@ static int nwl_pcie_probe(struct platform_device *pdev) struct pci_bus *child; struct pci_host_bridge *bridge; int err; - resource_size_t iobase = 0; - LIST_HEAD(res); bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); if (!bridge) @@ -845,24 +843,19 @@ static int nwl_pcie_probe(struct platform_device *pdev) return err; } - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, - &iobase); + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, NULL); if (err) { dev_err(dev, "Getting bridge resources failed\n"); return err; } - err = devm_request_pci_bus_resources(dev, &res); - if (err) - goto error; - err = nwl_pcie_init_irq_domain(pcie); if (err) { dev_err(dev, "Failed creating IRQ Domain\n"); - goto error; + return err; } - list_splice_init(&res, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = pcie->root_busno; @@ -874,13 +867,13 @@ static int nwl_pcie_probe(struct platform_device *pdev) err = nwl_pcie_enable_msi(pcie); if (err < 0) { dev_err(dev, "failed to enable MSI support: %d\n", err); - goto error; + return err; } } err = pci_scan_root_bus_bridge(bridge); if (err) - goto error; + return err; bus = bridge->bus; @@ -889,10 +882,6 @@ static int nwl_pcie_probe(struct platform_device *pdev) pcie_bus_configure_settings(child); pci_bus_add_devices(bus); return 0; - -error: - pci_free_resource_list(&res); - return err; } static struct platform_driver nwl_pcie_driver = { diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index 5bf3af3b28e6..98e55297815b 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -619,8 +619,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) struct pci_bus *bus, *child; struct pci_host_bridge *bridge; int err; - resource_size_t iobase = 0; - LIST_HEAD(res); if (!dev->of_node) return -ENODEV; @@ -647,19 +645,13 @@ static int xilinx_pcie_probe(struct platform_device *pdev) return err; } - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, - &iobase); + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, NULL); if (err) { dev_err(dev, "Getting bridge resources failed\n"); return err; } - err = devm_request_pci_bus_resources(dev, &res); - if (err) - goto error; - - - list_splice_init(&res, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = port; bridge->busnr = 0; @@ -673,7 +665,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev) #endif err = pci_scan_root_bus_bridge(bridge); if (err < 0) - goto error; + return err; bus = bridge->bus; @@ -682,10 +674,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) pcie_bus_configure_settings(child); pci_bus_add_devices(bus); return 0; - -error: - pci_free_resource_list(&res); - 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 4575e0c6dc4b..dac91d60701d 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -31,6 +31,9 @@ #define PCI_REG_VMLOCK 0x70 #define MB2_SHADOW_EN(vmlock) (vmlock & 0x2) +#define MB2_SHADOW_OFFSET 0x2000 +#define MB2_SHADOW_SIZE 16 + enum vmd_features { /* * Device may contain registers which hint the physical location of the @@ -94,9 +97,7 @@ struct vmd_dev { struct resource resources[3]; struct irq_domain *irq_domain; struct pci_bus *bus; - - struct dma_map_ops dma_ops; - struct dma_domain dma_domain; + u8 busn_start; }; static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) @@ -291,156 +292,12 @@ static struct msi_domain_info vmd_msi_domain_info = { .chip = &vmd_msi_controller, }; -/* - * VMD replaces the requester ID with its own. DMA mappings for devices in a - * VMD domain need to be mapped for the VMD, not the device requiring - * the mapping. - */ -static struct device *to_vmd_dev(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct vmd_dev *vmd = vmd_from_bus(pdev->bus); - - return &vmd->dev->dev; -} - -static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr, - gfp_t flag, unsigned long attrs) -{ - return dma_alloc_attrs(to_vmd_dev(dev), size, addr, flag, attrs); -} - -static void vmd_free(struct device *dev, size_t size, void *vaddr, - dma_addr_t addr, unsigned long attrs) -{ - return dma_free_attrs(to_vmd_dev(dev), size, vaddr, addr, attrs); -} - -static int vmd_mmap(struct device *dev, struct vm_area_struct *vma, - void *cpu_addr, dma_addr_t addr, size_t size, - unsigned long attrs) -{ - return dma_mmap_attrs(to_vmd_dev(dev), vma, cpu_addr, addr, size, - attrs); -} - -static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt, - void *cpu_addr, dma_addr_t addr, size_t size, - unsigned long attrs) -{ - return dma_get_sgtable_attrs(to_vmd_dev(dev), sgt, cpu_addr, addr, size, - attrs); -} - -static dma_addr_t vmd_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, - unsigned long attrs) -{ - return dma_map_page_attrs(to_vmd_dev(dev), page, offset, size, dir, - attrs); -} - -static void vmd_unmap_page(struct device *dev, dma_addr_t addr, size_t size, - enum dma_data_direction dir, unsigned long attrs) -{ - dma_unmap_page_attrs(to_vmd_dev(dev), addr, size, dir, attrs); -} - -static int vmd_map_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir, unsigned long attrs) -{ - return dma_map_sg_attrs(to_vmd_dev(dev), sg, nents, dir, attrs); -} - -static void vmd_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir, unsigned long attrs) -{ - dma_unmap_sg_attrs(to_vmd_dev(dev), sg, nents, dir, attrs); -} - -static void vmd_sync_single_for_cpu(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir) -{ - dma_sync_single_for_cpu(to_vmd_dev(dev), addr, size, dir); -} - -static void vmd_sync_single_for_device(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir) -{ - dma_sync_single_for_device(to_vmd_dev(dev), addr, size, dir); -} - -static void vmd_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - dma_sync_sg_for_cpu(to_vmd_dev(dev), sg, nents, dir); -} - -static void vmd_sync_sg_for_device(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - dma_sync_sg_for_device(to_vmd_dev(dev), sg, nents, dir); -} - -static int vmd_dma_supported(struct device *dev, u64 mask) -{ - return dma_supported(to_vmd_dev(dev), mask); -} - -static u64 vmd_get_required_mask(struct device *dev) -{ - return dma_get_required_mask(to_vmd_dev(dev)); -} - -static void vmd_teardown_dma_ops(struct vmd_dev *vmd) -{ - struct dma_domain *domain = &vmd->dma_domain; - - if (get_dma_ops(&vmd->dev->dev)) - del_dma_domain(domain); -} - -#define ASSIGN_VMD_DMA_OPS(source, dest, fn) \ - do { \ - if (source->fn) \ - dest->fn = vmd_##fn; \ - } while (0) - -static void vmd_setup_dma_ops(struct vmd_dev *vmd) -{ - const struct dma_map_ops *source = get_dma_ops(&vmd->dev->dev); - struct dma_map_ops *dest = &vmd->dma_ops; - struct dma_domain *domain = &vmd->dma_domain; - - domain->domain_nr = vmd->sysdata.domain; - domain->dma_ops = dest; - - if (!source) - return; - ASSIGN_VMD_DMA_OPS(source, dest, alloc); - ASSIGN_VMD_DMA_OPS(source, dest, free); - ASSIGN_VMD_DMA_OPS(source, dest, mmap); - ASSIGN_VMD_DMA_OPS(source, dest, get_sgtable); - ASSIGN_VMD_DMA_OPS(source, dest, map_page); - ASSIGN_VMD_DMA_OPS(source, dest, unmap_page); - ASSIGN_VMD_DMA_OPS(source, dest, map_sg); - ASSIGN_VMD_DMA_OPS(source, dest, unmap_sg); - ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_cpu); - ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_device); - ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_cpu); - ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_device); - ASSIGN_VMD_DMA_OPS(source, dest, dma_supported); - ASSIGN_VMD_DMA_OPS(source, dest, get_required_mask); - add_dma_domain(domain); -} -#undef ASSIGN_VMD_DMA_OPS - static char __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, unsigned int devfn, int reg, int len) { char __iomem *addr = vmd->cfgbar + - (bus->number << 20) + (devfn << 12) + reg; + ((bus->number - vmd->busn_start) << 20) + + (devfn << 12) + reg; if ((addr - vmd->cfgbar) + len >= resource_size(&vmd->dev->resource[VMD_CFGBAR])) @@ -563,7 +420,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) unsigned long flags; LIST_HEAD(resources); resource_size_t offset[2] = {0}; - resource_size_t membar2_offset = 0x2000, busn_start = 0; + resource_size_t membar2_offset = 0x2000; struct pci_bus *child; /* @@ -576,7 +433,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) u32 vmlock; int ret; - membar2_offset = 0x2018; + membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE; ret = pci_read_config_dword(vmd->dev, PCI_REG_VMLOCK, &vmlock); if (ret || vmlock == ~0) return -ENODEV; @@ -588,32 +445,46 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) if (!membar2) return -ENOMEM; offset[0] = vmd->dev->resource[VMD_MEMBAR1].start - - readq(membar2 + 0x2008); + readq(membar2 + MB2_SHADOW_OFFSET); offset[1] = vmd->dev->resource[VMD_MEMBAR2].start - - readq(membar2 + 0x2010); + readq(membar2 + MB2_SHADOW_OFFSET + 8); pci_iounmap(vmd->dev, membar2); } } /* * Certain VMD devices may have a root port configuration option which - * limits the bus range to between 0-127 or 128-255 + * limits the bus range to between 0-127, 128-255, or 224-255 */ if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { - u32 vmcap, vmconfig; - - pci_read_config_dword(vmd->dev, PCI_REG_VMCAP, &vmcap); - pci_read_config_dword(vmd->dev, PCI_REG_VMCONFIG, &vmconfig); - if (BUS_RESTRICT_CAP(vmcap) && - (BUS_RESTRICT_CFG(vmconfig) == 0x1)) - busn_start = 128; + u16 reg16; + + pci_read_config_word(vmd->dev, PCI_REG_VMCAP, ®16); + if (BUS_RESTRICT_CAP(reg16)) { + pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, + ®16); + + switch (BUS_RESTRICT_CFG(reg16)) { + case 1: + vmd->busn_start = 128; + break; + case 2: + vmd->busn_start = 224; + break; + case 3: + pci_err(vmd->dev, "Unknown Bus Offset Setting\n"); + return -ENODEV; + default: + break; + } + } } res = &vmd->dev->resource[VMD_CFGBAR]; vmd->resources[0] = (struct resource) { .name = "VMD CFGBAR", - .start = busn_start, - .end = busn_start + (resource_size(res) >> 20) - 1, + .start = vmd->busn_start, + .end = vmd->busn_start + (resource_size(res) >> 20) - 1, .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED, }; @@ -660,7 +531,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) .parent = res, }; - sd->vmd_domain = true; + sd->vmd_dev = vmd->dev; sd->domain = vmd_find_free_domain(); if (sd->domain < 0) return sd->domain; @@ -681,8 +552,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]); pci_add_resource_offset(&resources, &vmd->resources[2], offset[1]); - vmd->bus = pci_create_root_bus(&vmd->dev->dev, busn_start, &vmd_ops, - sd, &resources); + vmd->bus = pci_create_root_bus(&vmd->dev->dev, vmd->busn_start, + &vmd_ops, sd, &resources); if (!vmd->bus) { pci_free_resource_list(&resources); irq_domain_remove(vmd->irq_domain); @@ -690,7 +561,6 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) } vmd_attach_resources(vmd); - vmd_setup_dma_ops(vmd); dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain); pci_scan_child_bus(vmd->bus); @@ -805,7 +675,6 @@ static void vmd_remove(struct pci_dev *dev) pci_stop_root_bus(vmd->bus); pci_remove_root_bus(vmd->bus); vmd_cleanup_srcu(vmd); - vmd_teardown_dma_ops(vmd); vmd_detach_resources(vmd); irq_domain_remove(vmd->irq_domain); } @@ -818,7 +687,7 @@ static int vmd_suspend(struct device *dev) int i; for (i = 0; i < vmd->msix_count; i++) - devm_free_irq(dev, pci_irq_vector(pdev, i), &vmd->irqs[i]); + devm_free_irq(dev, pci_irq_vector(pdev, i), &vmd->irqs[i]); pci_save_state(pdev); return 0; @@ -849,6 +718,12 @@ static const struct pci_device_id vmd_ids[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0), .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f), + .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4c3d), + .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B), + .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, {0,} }; MODULE_DEVICE_TABLE(pci, vmd_ids); |