diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/Kconfig | 20 | ||||
-rw-r--r-- | drivers/pci/Makefile | 3 | ||||
-rw-r--r-- | drivers/pci/bus.c | 14 | ||||
-rw-r--r-- | drivers/pci/pci-ecam-generic.c | 208 | ||||
-rw-r--r-- | drivers/pci/pci-efi.c | 19 | ||||
-rw-r--r-- | drivers/pci/pci-efi.h | 2 | ||||
-rw-r--r-- | drivers/pci/pci-imx6.c | 31 | ||||
-rw-r--r-- | drivers/pci/pci-layerscape.c | 24 | ||||
-rw-r--r-- | drivers/pci/pci-mvebu-phy.c | 2 | ||||
-rw-r--r-- | drivers/pci/pci-mvebu.c | 15 | ||||
-rw-r--r-- | drivers/pci/pci-mvebu.h | 2 | ||||
-rw-r--r-- | drivers/pci/pci-tegra.c | 17 | ||||
-rw-r--r-- | drivers/pci/pci.c | 262 | ||||
-rw-r--r-- | drivers/pci/pci_iomap.c | 1 | ||||
-rw-r--r-- | drivers/pci/pcie-designware-host.c | 8 | ||||
-rw-r--r-- | drivers/pci/pcie-designware.c | 6 | ||||
-rw-r--r-- | drivers/pci/pcie-designware.h | 4 | ||||
-rw-r--r-- | drivers/pci/pcie-dw-rockchip.c | 300 |
18 files changed, 862 insertions, 76 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 71d05055d4..8f37805d71 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -1,5 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only config HW_HAS_PCI - bool "Compile in PCI core" if COMPILE_TEST + bool if HW_HAS_PCI @@ -54,11 +55,26 @@ config PCI_LAYERSCAPE select OF_PCI select PCI +config PCI_ROCKCHIP + bool "Rockchip PCIe controller" + depends on ARCH_ROCKCHIP + select PCIE_DW + select OF_PCI + select PCI + config PCI_EFI bool "EFI PCI protocol" - depends on EFI_BOOTUP + depends on EFI_PAYLOAD select PCI +config PCI_ECAM_GENERIC + bool "Generic ECAM-based PCI host controller support" + select OF_PCI + select PCI + help + Say Y here if you want to enable support for generic ECAM-based + PCI host controllers, such as the one emulated by QEMU. + endmenu endif diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6fc4eaf6b2..9249bffecb 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Makefile for the PCI bus specific drivers. # @@ -11,3 +12,5 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o pcie-designware-host.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o obj-$(CONFIG_PCI_EFI) += pci-efi.o +obj-$(CONFIG_PCI_ECAM_GENERIC) += pci-ecam-generic.o +obj-$(CONFIG_PCI_ROCKCHIP) += pcie-dw-rockchip.o diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 251be4fffa..b6eab56d87 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <driver.h> @@ -49,7 +50,7 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, } EXPORT_SYMBOL(pci_match_id); -static int pci_match(struct device_d *dev, struct driver_d *drv) +static int pci_match(struct device *dev, struct driver *drv) { struct pci_dev *pdev = to_pci_dev(dev); struct pci_driver *pdrv = to_pci_driver(drv); @@ -64,7 +65,7 @@ static int pci_match(struct device_d *dev, struct driver_d *drv) return -1; } -static int pci_probe(struct device_d *dev) +static int pci_probe(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct pci_driver *pdrv = to_pci_driver(dev->driver); @@ -72,7 +73,7 @@ static int pci_probe(struct device_d *dev) return pdrv->probe(pdev, pdev->id); } -static void pci_remove(struct device_d *dev) +static void pci_remove(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct pci_driver *pdrv = to_pci_driver(dev->driver); @@ -96,7 +97,7 @@ pure_initcall(pci_bus_init); int pci_register_driver(struct pci_driver *pdrv) { - struct driver_d *drv = &pdrv->driver; + struct driver *drv = &pdrv->driver; if (!pdrv->id_table) return -EIO; @@ -110,9 +111,12 @@ int pci_register_driver(struct pci_driver *pdrv) int pci_register_device(struct pci_dev *pdev) { char str[6]; - struct device_d *dev = &pdev->dev; + struct device *dev = &pdev->dev; int ret; + if (!of_device_is_available(pdev->dev.of_node)) + return 0; + dev_set_name(dev, "pci-%04x:%04x.", pdev->vendor, pdev->device); dev->bus = &pci_bus; dev->id = DEVICE_ID_DYNAMIC; diff --git a/drivers/pci/pci-ecam-generic.c b/drivers/pci/pci-ecam-generic.c new file mode 100644 index 0000000000..e8609bd4b0 --- /dev/null +++ b/drivers/pci/pci-ecam-generic.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generic PCIE host provided by e.g. QEMU + * + * Heavily based on drivers/pci/pcie_xilinx.c + * + * Copyright (C) 2016 Imagination Technologies + */ + +#include <common.h> +#include <malloc.h> +#include <io.h> +#include <of.h> +#include <of_address.h> +#include <init.h> +#include <linux/pci.h> +#include <linux/sizes.h> + +struct generic_ecam_pcie { + struct pci_controller pci; + struct resource *cfg; + int first_busno; + struct resource io; + struct resource mem; + struct resource prefetch; +}; + +static inline struct generic_ecam_pcie *host_to_ecam(struct pci_controller *host) +{ + return container_of(host, struct generic_ecam_pcie, pci); +} + +static void __iomem *pci_generic_ecam_conf_address(const struct pci_bus *bus, + u32 devfn, int where) +{ + struct generic_ecam_pcie *ecam = host_to_ecam(bus->host); + void __iomem *addr; + + addr = IOMEM(ecam->cfg->start); + addr += (bus->number - ecam->first_busno) << 20; + addr += PCI_SLOT(devfn) << 15; + addr += PCI_FUNC(devfn) << 12; + addr += where; + + return addr; +} + +static bool pci_generic_ecam_addr_valid(const struct pci_bus *bus, u32 devfn) +{ + struct generic_ecam_pcie *ecam = host_to_ecam(bus->host); + int num_buses = DIV_ROUND_UP(resource_size(ecam->cfg), 1 << 16); + + return (bus->number >= ecam->first_busno && + bus->number < ecam->first_busno + num_buses); +} + +static int pci_generic_ecam_read_config(struct pci_bus *bus, + u32 devfn, int where, + int size, u32 *val) +{ + void __iomem *addr; + + if (!pci_generic_ecam_addr_valid(bus, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = pci_generic_ecam_conf_address(bus, devfn, where); + + if (!IS_ALIGNED((uintptr_t)addr, size)) { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (size == 4) { + *val = readl(addr); + } else if (size == 2) { + *val = readw(addr); + } else if (size == 1) { + *val = readb(addr); + } else { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_generic_ecam_write_config(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + void __iomem *addr; + + if (!pci_generic_ecam_addr_valid(bus, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = pci_generic_ecam_conf_address(bus, devfn, where); + + if (!IS_ALIGNED((uintptr_t)addr, size)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (size == 4) + writel(val, addr); + else if (size == 2) + writew(val, addr); + else if (size == 1) + writeb(val, addr); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +static void pcie_ecam_set_local_bus_nr(struct pci_controller *host, int busno) +{ + struct generic_ecam_pcie *ecam = host_to_ecam(host); + + ecam->first_busno = busno; +} + +static const struct pci_ops pci_generic_ecam_ops = { + .read = pci_generic_ecam_read_config, + .write = pci_generic_ecam_write_config, +}; + +static inline bool is_64bit(const struct resource *res) +{ + return res->flags & IORESOURCE_MEM_64; +} + +static int pcie_ecam_parse_dt(struct generic_ecam_pcie *ecam) +{ + struct device *dev = ecam->pci.parent; + struct device_node *np = dev->of_node; + struct of_pci_range_parser parser; + struct of_pci_range range; + struct resource res; + + if (of_pci_range_parser_init(&parser, np)) { + dev_err(dev, "missing \"ranges\" property\n"); + return -EINVAL; + } + + for_each_of_pci_range(&parser, &range) { + of_pci_range_to_resource(&range, np, &res); + + switch (res.flags & IORESOURCE_TYPE_BITS) { + case IORESOURCE_IO: + memcpy(&ecam->io, &res, sizeof(res)); + ecam->io.name = "I/O"; + break; + + case IORESOURCE_MEM: + if (res.flags & IORESOURCE_PREFETCH) { + memcpy(&ecam->prefetch, &res, sizeof(res)); + ecam->prefetch.name = "PREFETCH"; + } else { + /* Choose 32-bit mappings over 64-bit ones if possible */ + if (ecam->mem.name && !is_64bit(&ecam->mem) && is_64bit(&res)) + break; + + memcpy(&ecam->mem, &res, sizeof(res)); + ecam->mem.name = "MEM"; + } + break; + } + } + + return 0; +} + +static int pcie_ecam_probe(struct device *dev) +{ + struct generic_ecam_pcie *ecam; + struct resource *iores; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + ecam = xzalloc(sizeof(*ecam)); + ecam->cfg = iores; + ecam->pci.parent = dev; + ecam->pci.pci_ops = &pci_generic_ecam_ops; + ecam->pci.set_busno = pcie_ecam_set_local_bus_nr; + ecam->pci.mem_resource = &ecam->mem; + ecam->pci.io_resource = &ecam->io; + ecam->pci.mem_pref_resource = &ecam->prefetch; + + ret = pcie_ecam_parse_dt(ecam); + if (ret) + return ret; + + register_pci_controller(&ecam->pci); + return 0; +} + +static struct of_device_id pcie_ecam_dt_ids[] = { + { .compatible = "pci-host-ecam-generic" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, pcie_ecam_dt_ids); + +static struct driver pcie_ecam_driver = { + .name = "pcie-generic-ecam", + .probe = pcie_ecam_probe, + .of_compatible = pcie_ecam_dt_ids, +}; +device_platform_driver(pcie_ecam_driver); diff --git a/drivers/pci/pci-efi.c b/drivers/pci/pci-efi.c index e1fe11d070..67868d09b6 100644 --- a/drivers/pci/pci-efi.c +++ b/drivers/pci/pci-efi.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019 Ahmad Fatoum <a.fatoum@pengutronix.de> */ @@ -9,7 +9,7 @@ #include <init.h> #include <xfuncs.h> #include <efi.h> -#include <efi/efi.h> +#include <efi/efi-payload.h> #include <efi/efi-device.h> #include <linux/pci.h> @@ -17,7 +17,7 @@ struct efi_pci_priv { struct efi_pci_root_bridge_io_protocol *protocol; - struct device_d *dev; + struct device *dev; struct pci_controller pci; struct resource mem; struct resource mem_pref; @@ -34,7 +34,7 @@ struct pci_child_id { struct pci_child { struct efi_pci_io_protocol *protocol; - struct device_d *dev; + struct device *dev; struct list_head list; struct pci_child_id id; }; @@ -209,15 +209,22 @@ static u8 *acpi_parse_resource(u8 *next, struct resource *out) return next; } +static struct efi_driver efi_pci_driver; + /* EFI already enumerated the bus for us, match our new pci devices with the efi * handles */ static void efi_pci_fixup_dev_parent(struct pci_dev *dev) { - struct efi_pci_priv *priv = host_to_efi_pci(dev->bus->host); + struct efi_pci_priv *priv; struct pci_child *child; struct pci_child_id id; + if (dev->dev.driver != &efi_pci_driver.driver) + return; + + priv = host_to_efi_pci(dev->bus->host); + id.segmentno = priv->protocol->segmentno; id.busno = dev->bus->number; id.devno = PCI_SLOT(dev->devfn); @@ -261,7 +268,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, efi_pci_fixup_dev_parent); static int efi_pci_probe(struct efi_device *efidev) { - struct device_d *child; + struct device *child; struct efi_pci_priv *priv; efi_status_t efiret; void *resources; diff --git a/drivers/pci/pci-efi.h b/drivers/pci/pci-efi.h index 1943461cdf..1dba0b9906 100644 --- a/drivers/pci/pci-efi.h +++ b/drivers/pci/pci-efi.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only #ifndef __PCI_EFI_H_ #define __PCI_EFI_H_ diff --git a/drivers/pci/pci-imx6.c b/drivers/pci/pci-imx6.c index 05df9c0f79..ac62d961d9 100644 --- a/drivers/pci/pci-imx6.c +++ b/drivers/pci/pci-imx6.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * PCIe host controller driver for Freescale i.MX6 SoCs * @@ -30,9 +30,9 @@ #include <mfd/imx6q-iomuxc-gpr.h> #include <mfd/imx7-iomuxc-gpr.h> -#include <mach/imx6-regs.h> -#include <mach/imx7-regs.h> -#include <mach/imx8mq-regs.h> +#include <mach/imx/imx6-regs.h> +#include <mach/imx/imx7-regs.h> +#include <mach/imx/imx8mq-regs.h> #include "pcie-designware.h" @@ -297,7 +297,7 @@ static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie) static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) { - struct device_d *dev = imx6_pcie->pci->dev; + struct device *dev = imx6_pcie->pci->dev; u32 gpr1, gpr1x; unsigned int offset; int ret; @@ -351,7 +351,7 @@ static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) { u32 val; unsigned int retries; - struct device_d *dev = imx6_pcie->pci->dev; + struct device *dev = imx6_pcie->pci->dev; for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) { val = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR22); @@ -367,7 +367,7 @@ static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) { - struct device_d *dev = imx6_pcie->pci->dev; + struct device *dev = imx6_pcie->pci->dev; int ret; u32 gpr1; @@ -525,7 +525,7 @@ static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) { struct dw_pcie *pci = imx6_pcie->pci; - struct device_d *dev = pci->dev; + struct device *dev = pci->dev; uint32_t tmp; uint64_t start = get_time_ns(); @@ -540,7 +540,7 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) return -EINVAL; } -static void imx6_pcie_ltssm_enable(struct device_d *dev) +static void imx6_pcie_ltssm_enable(struct device *dev) { struct imx6_pcie *imx6_pcie = dev->priv; u32 gpr12; @@ -562,7 +562,7 @@ static void imx6_pcie_ltssm_enable(struct device_d *dev) static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) { struct dw_pcie *pci = imx6_pcie->pci; - struct device_d *dev = pci->dev; + struct device *dev = pci->dev; uint32_t tmp; int ret; @@ -669,7 +669,7 @@ static const struct dw_pcie_host_ops imx6_pcie_host_ops = { }; static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, - struct device_d *dev) + struct device *dev) { struct dw_pcie *pci = imx6_pcie->pci; struct pcie_port *pp = &pci->pp; @@ -686,12 +686,12 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, return 0; } -static int imx6_pcie_probe(struct device_d *dev) +static int imx6_pcie_probe(struct device *dev) { struct resource *iores; struct dw_pcie *pci; struct imx6_pcie *imx6_pcie; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; int ret; imx6_pcie = xzalloc(sizeof(*imx6_pcie)); @@ -814,7 +814,7 @@ static int imx6_pcie_probe(struct device_d *dev) return 0; } -static void imx6_pcie_remove(struct device_d *dev) +static void imx6_pcie_remove(struct device *dev) { struct imx6_pcie *imx6_pcie = dev->priv; @@ -874,8 +874,9 @@ static struct of_device_id imx6_pcie_of_match[] = { { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } , {}, }; +MODULE_DEVICE_TABLE(of, imx6_pcie_of_match); -static struct driver_d imx6_pcie_driver = { +static struct driver imx6_pcie_driver = { .name = "imx6-pcie", .of_compatible = DRV_OF_COMPAT(imx6_pcie_of_match), .probe = imx6_pcie_probe, diff --git a/drivers/pci/pci-layerscape.c b/drivers/pci/pci-layerscape.c index d8f03fa599..12a0ec71a7 100644 --- a/drivers/pci/pci-layerscape.c +++ b/drivers/pci/pci-layerscape.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * PCIe host controller driver for Freescale Layerscape SoCs * @@ -21,7 +21,7 @@ #include <linux/kernel.h> #include <of_address.h> #include <of_pci.h> -#include <regmap.h> +#include <linux/regmap.h> #include <magicvar.h> #include <globalvar.h> #include <mfd/syscon.h> @@ -217,11 +217,11 @@ static int ls1021_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct ls_pcie *pcie = to_ls_pcie(pci); - struct device_d *dev = pci->dev; + struct device *dev = pci->dev; u32 index[2]; int ret; - pcie->scfg = syscon_regmap_lookup_by_phandle(dev->device_node, + pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg"); if (IS_ERR(pcie->scfg)) { ret = PTR_ERR(pcie->scfg); @@ -230,7 +230,7 @@ static int ls1021_pcie_host_init(struct pcie_port *pp) return ret; } - if (of_property_read_u32_array(dev->device_node, + if (of_property_read_u32_array(dev->of_node, "fsl,pcie-scfg", index, 2)) { pcie->scfg = NULL; return -EINVAL; @@ -312,12 +312,13 @@ static const struct of_device_id ls_pcie_of_match[] = { { .compatible = "fsl,ls1088a-pcie", .data = &ls2088_drvdata }, { }, }; +MODULE_DEVICE_TABLE(of, ls_pcie_of_match); static int __init ls_add_pcie_port(struct ls_pcie *pcie) { struct dw_pcie *pci = &pcie->pci; struct pcie_port *pp = &pci->pp; - struct device_d *dev = pci->dev; + struct device *dev = pci->dev; int ret; pp->ops = pcie->drvdata->ops; @@ -369,7 +370,7 @@ BAREBOX_MAGICVAR(global.layerscape_pcie.share_stream_ids, static int ls_pcie_of_fixup(struct device_node *root, void *ctx) { struct ls_pcie *pcie = ctx; - struct device_d *dev = pcie->pci.dev; + struct device *dev = pcie->pci.dev; struct device_node *np; phandle iommu_handle = 0; char *name; @@ -378,7 +379,7 @@ static int ls_pcie_of_fixup(struct device_node *root, void *ctx) int ret, i; u32 devid, stream_id; - name = of_get_reproducible_name(dev->device_node); + name = of_get_reproducible_name(dev->of_node); np = root; do { @@ -479,7 +480,7 @@ out: return ret; } -static int __init ls_pcie_probe(struct device_d *dev) +static int __init ls_pcie_probe(struct device *dev) { struct dw_pcie *pci; struct ls_pcie *pcie; @@ -532,7 +533,7 @@ static int __init ls_pcie_probe(struct device_d *dev) return 0; } -static struct driver_d ls_pcie_driver = { +static struct driver ls_pcie_driver = { .name = "layerscape-pcie", .of_compatible = DRV_OF_COMPAT(ls_pcie_of_match), .probe = ls_pcie_probe, @@ -550,6 +551,9 @@ static void ls_pcie_fixup(struct pci_dev *pcidev) uint32_t devid; int base_bus_num = 0; + if (!of_match_device(ls_pcie_of_match, host->parent)) + return; + stream_id = ls_pcie_next_streamid(lspcie); index = ls_pcie_next_lut_index(lspcie); diff --git a/drivers/pci/pci-mvebu-phy.c b/drivers/pci/pci-mvebu-phy.c index f1bfc99eef..c47a7e739f 100644 --- a/drivers/pci/pci-mvebu-phy.c +++ b/drivers/pci/pci-mvebu-phy.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * SoC specific PCIe PHY setup for Marvell MVEBU SoCs * diff --git a/drivers/pci/pci-mvebu.c b/drivers/pci/pci-mvebu.c index 0c5e34116c..9e2c7dc648 100644 --- a/drivers/pci/pci-mvebu.c +++ b/drivers/pci/pci-mvebu.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * PCIe driver for Marvell MVEBU SoCs * @@ -262,7 +262,7 @@ static int mvebu_get_target_attr(struct device_node *np, int devfn, return -ENOENT; } -static struct mvebu_pcie *mvebu_pcie_port_probe(struct device_d *dev, +static struct mvebu_pcie *mvebu_pcie_port_probe(struct device *dev, struct device_node *np) { struct mvebu_pcie *pcie; @@ -292,14 +292,14 @@ static struct mvebu_pcie *mvebu_pcie_port_probe(struct device_d *dev, return ERR_PTR(-EINVAL); } - if (mvebu_get_target_attr(dev->device_node, devfn, IORESOURCE_MEM, + if (mvebu_get_target_attr(dev->of_node, devfn, IORESOURCE_MEM, &mem_target, &mem_attr)) { dev_err(dev, "unable to get target/attr for mem window\n"); return ERR_PTR(-EINVAL); } /* I/O windows are optional */ - mvebu_get_target_attr(dev->device_node, devfn, IORESOURCE_IO, + mvebu_get_target_attr(dev->of_node, devfn, IORESOURCE_IO, &io_target, &io_attr); reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags); @@ -395,10 +395,11 @@ static struct of_device_id mvebu_pcie_dt_ids[] = { #endif { }, }; +MODULE_DEVICE_TABLE(of, mvebu_pcie_dt_ids); -static int mvebu_pcie_probe(struct device_d *dev) +static int mvebu_pcie_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; const struct of_device_id *match = of_match_node(mvebu_pcie_dt_ids, np); struct mvebu_pcie_ops *ops = (struct mvebu_pcie_ops *)match->data; struct device_node *pnp; @@ -437,7 +438,7 @@ static int mvebu_pcie_probe(struct device_d *dev) return 0; } -static struct driver_d mvebu_pcie_driver = { +static struct driver mvebu_pcie_driver = { .name = "mvebu-pcie", .probe = mvebu_pcie_probe, .of_compatible = mvebu_pcie_dt_ids, diff --git a/drivers/pci/pci-mvebu.h b/drivers/pci/pci-mvebu.h index 2797bc4c8b..2185f20b41 100644 --- a/drivers/pci/pci-mvebu.h +++ b/drivers/pci/pci-mvebu.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * PCIe include for Marvell MVEBU SoCs * diff --git a/drivers/pci/pci-tegra.c b/drivers/pci/pci-tegra.c index b534285c97..fba8b47ece 100644 --- a/drivers/pci/pci-tegra.c +++ b/drivers/pci/pci-tegra.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de> * @@ -22,7 +22,7 @@ #include <linux/phy/phy.h> #include <linux/reset.h> #include <linux/sizes.h> -#include <mach/tegra-powergate.h> +#include <mach/tegra/tegra-powergate.h> #include <regulator.h> /* register definitions */ @@ -214,7 +214,7 @@ struct tegra_pcie_soc_data { }; struct tegra_pcie { - struct device_d *dev; + struct device *dev; struct pci_controller pci; void __iomem *pads; @@ -827,7 +827,7 @@ static int tegra_pcie_resets_get(struct tegra_pcie *pcie) static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { - struct device_d *dev = pcie->dev; + struct device *dev = pcie->dev; int err; err = tegra_pcie_clocks_get(pcie); @@ -903,7 +903,7 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie) static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, u32 *xbar) { - struct device_node *np = pcie->dev->device_node; + struct device_node *np = pcie->dev->of_node; if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { switch (lanes) { @@ -954,7 +954,7 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) { const struct tegra_pcie_soc_data *soc = pcie->soc_data; - struct device_node *np = pcie->dev->device_node, *port; + struct device_node *np = pcie->dev->of_node, *port; struct of_pci_range_parser parser; struct of_pci_range range; struct resource *rp_res; @@ -1230,8 +1230,9 @@ static __maybe_unused struct of_device_id tegra_pcie_of_match[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, tegra_pcie_of_match); -static int tegra_pcie_probe(struct device_d *dev) +static int tegra_pcie_probe(struct device *dev) { struct tegra_pcie *pcie; int err; @@ -1277,7 +1278,7 @@ put_resources: return err; } -static struct driver_d tegra_pcie_driver = { +static struct driver tegra_pcie_driver = { .name = "tegra-pcie", .of_compatible = DRV_OF_COMPAT(tegra_pcie_of_match), .probe = tegra_pcie_probe, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 950c509447..84678e40a9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1,8 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only #define pr_fmt(fmt) "pci: " fmt #include <common.h> #include <linux/sizes.h> #include <linux/pci.h> +#include <linux/bitfield.h> static unsigned int pci_scan_bus(struct pci_bus *bus); @@ -151,6 +153,189 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask) return size + 1; } +static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop) +{ + unsigned long flags = IORESOURCE_PCI_FIXED; + + switch (prop) { + case PCI_EA_P_MEM: + case PCI_EA_P_VF_MEM: + flags |= IORESOURCE_MEM; + break; + case PCI_EA_P_MEM_PREFETCH: + case PCI_EA_P_VF_MEM_PREFETCH: + flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; + break; + case PCI_EA_P_IO: + flags |= IORESOURCE_IO; + break; + default: + return 0; + } + + return flags; +} + +static struct resource *pci_ea_get_resource(struct pci_dev *dev, u8 bei, + u8 prop) +{ + if (bei <= PCI_EA_BEI_BAR5 && prop <= PCI_EA_P_IO) + return &dev->resource[bei]; + else if (bei == PCI_EA_BEI_ROM) + return &dev->resource[PCI_ROM_RESOURCE]; + else + return NULL; +} + +/* Read an Enhanced Allocation (EA) entry */ +static int pci_ea_read(struct pci_dev *dev, int offset) +{ + struct resource *res; + int ent_size, ent_offset = offset; + resource_size_t start, end; + unsigned long flags; + u32 dw0, bei, base, max_offset; + u8 prop; + bool support_64 = (sizeof(resource_size_t) >= 8); + + pci_read_config_dword(dev, ent_offset, &dw0); + ent_offset += 4; + + /* Entry size field indicates DWORDs after 1st */ + ent_size = (FIELD_GET(PCI_EA_ES, dw0) + 1) << 2; + + if (!(dw0 & PCI_EA_ENABLE)) /* Entry not enabled */ + goto out; + + bei = FIELD_GET(PCI_EA_BEI, dw0); + prop = FIELD_GET(PCI_EA_PP, dw0); + + /* + * If the Property is in the reserved range, try the Secondary + * Property instead. + */ + if (prop > PCI_EA_P_BRIDGE_IO && prop < PCI_EA_P_MEM_RESERVED) + prop = FIELD_GET(PCI_EA_SP, dw0); + if (prop > PCI_EA_P_BRIDGE_IO) + goto out; + + res = pci_ea_get_resource(dev, bei, prop); + if (!res) { + dev_dbg(&dev->dev, "Unsupported EA entry BEI: %u\n", bei); + goto out; + } + + flags = pci_ea_flags(dev, prop); + if (!flags) { + dev_err(&dev->dev, "Unsupported EA properties: %#x\n", prop); + goto out; + } + + /* Read Base */ + pci_read_config_dword(dev, ent_offset, &base); + start = (base & PCI_EA_FIELD_MASK); + ent_offset += 4; + + /* Read MaxOffset */ + pci_read_config_dword(dev, ent_offset, &max_offset); + ent_offset += 4; + + /* Read Base MSBs (if 64-bit entry) */ + if (base & PCI_EA_IS_64) { + u32 base_upper; + + pci_read_config_dword(dev, ent_offset, &base_upper); + ent_offset += 4; + + flags |= IORESOURCE_MEM_64; + + /* entry starts above 32-bit boundary, can't use */ + if (!support_64 && base_upper) + goto out; + + if (support_64) + start |= ((u64)base_upper << 32); + } + + end = start + (max_offset | 0x03); + + /* Read MaxOffset MSBs (if 64-bit entry) */ + if (max_offset & PCI_EA_IS_64) { + u32 max_offset_upper; + + pci_read_config_dword(dev, ent_offset, &max_offset_upper); + ent_offset += 4; + + flags |= IORESOURCE_MEM_64; + + /* entry too big, can't use */ + if (!support_64 && max_offset_upper) + goto out; + + if (support_64) + end += ((u64)max_offset_upper << 32); + } + + if (end < start) { + dev_err(&dev->dev, "EA Entry crosses address boundary\n"); + goto out; + } + + if (ent_size != ent_offset - offset) { + dev_err(&dev->dev, "EA Entry Size (%d) does not match length read (%d)\n", + ent_size, ent_offset - offset); + goto out; + } + + res->start = start; + res->end = end; + res->flags = flags; + + if (bei <= PCI_EA_BEI_BAR5) + dev_dbg(&dev->dev, "BAR %d: %pR (from Enhanced Allocation, properties %#02x)\n", + bei, res, prop); + else if (bei == PCI_EA_BEI_ROM) + dev_dbg(&dev->dev, "ROM: %pR (from Enhanced Allocation, properties %#02x)\n", + res, prop); + else if (bei >= PCI_EA_BEI_VF_BAR0 && bei <= PCI_EA_BEI_VF_BAR5) + dev_dbg(&dev->dev, "VF BAR %d: %pR (from Enhanced Allocation, properties %#02x)\n", + bei - PCI_EA_BEI_VF_BAR0, res, prop); + else + dev_dbg(&dev->dev, "BEI %d res: %pR (from Enhanced Allocation, properties %#02x)\n", + bei, res, prop); + +out: + return offset + ent_size; +} + +/* Enhanced Allocation Initialization */ +static void pci_ea_init(struct pci_dev *dev) +{ + int ea; + u8 num_ent; + int offset; + int i; + + /* find PCI EA capability in list */ + ea = pci_find_capability(dev, PCI_CAP_ID_EA); + if (!ea) + return; + + /* determine the number of entries */ + pci_bus_read_config_byte(dev->bus, dev->devfn, ea + PCI_EA_NUM_ENT, + &num_ent); + num_ent &= PCI_EA_NUM_ENT_MASK; + + offset = ea + PCI_EA_FIRST_ENT; + + /* Skip DWORD 2 for type 1 functions */ + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + offset += 4; + + /* parse each EA entry */ + for (i = 0; i < num_ent; ++i) + offset = pci_ea_read(dev, offset); +} static void setup_device(struct pci_dev *dev, int max_bar) { @@ -220,9 +405,11 @@ static void setup_device(struct pci_dev *dev, int max_bar) } *last_addr = ALIGN(*last_addr, size); - pci_write_config_dword(dev, pci_base_address_0, *last_addr); + pci_write_config_dword(dev, pci_base_address_0, + lower_32_bits(*last_addr)); if (mask & PCI_BASE_ADDRESS_MEM_TYPE_64) - pci_write_config_dword(dev, pci_base_address_1, 0); + pci_write_config_dword(dev, pci_base_address_1, + upper_32_bits(*last_addr)); start = *last_addr; *last_addr += size; } else { @@ -248,6 +435,8 @@ static void setup_device(struct pci_dev *dev, int max_bar) } } + pci_ea_init(dev); + pci_fixup_device(pci_fixup_header, dev); if (pcibios_assign_all_busses()) @@ -340,15 +529,15 @@ static void postscan_setup_bridge(struct pci_dev *dev) } static struct device_node * -pci_of_match_device(struct device_d *parent, unsigned int devfn) +pci_of_match_device(struct device *parent, unsigned int devfn) { struct device_node *np; u32 reg; - if (!IS_ENABLED(CONFIG_OFTREE) || !parent || !parent->device_node) + if (!IS_ENABLED(CONFIG_OFTREE) || !parent || !parent->of_node) return NULL; - for_each_child_of_node(parent->device_node, np) { + for_each_child_of_node(parent->of_node, np) { if (!of_property_read_u32_array(np, "reg", ®, 1)) { /* * Only match device/function pair of the device @@ -364,6 +553,38 @@ pci_of_match_device(struct device_d *parent, unsigned int devfn) return NULL; } +/** + * pcie_flr - initiate a PCIe function level reset + * @dev: device to reset + * + * Initiate a function level reset on @dev. + */ +int pci_flr(struct pci_dev *pdev) +{ + u16 val; + int pcie_off; + u32 cap; + + /* look for PCI Express Capability */ + pcie_off = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!pcie_off) + return -ENOENT; + + /* check FLR capability */ + pci_read_config_dword(pdev, pcie_off + PCI_EXP_DEVCAP, &cap); + if (!(cap & PCI_EXP_DEVCAP_FLR)) + return -ENOENT; + + pci_read_config_word(pdev, pcie_off + PCI_EXP_DEVCTL, &val); + val |= PCI_EXP_DEVCTL_BCR_FLR; + pci_write_config_word(pdev, pcie_off + PCI_EXP_DEVCTL, val); + + /* wait 100ms, per PCI spec */ + mdelay(100); + + return 0; +} + static unsigned int pci_scan_bus(struct pci_bus *bus) { struct pci_dev *dev; @@ -398,10 +619,10 @@ static unsigned int pci_scan_bus(struct pci_bus *bus) dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; dev->dev.parent = bus->parent; - dev->dev.device_node = pci_of_match_device(bus->parent, devfn); - if (dev->dev.device_node) - pr_debug("found DT node %s for device %04x:%04x\n", - dev->dev.device_node->full_name, + dev->dev.of_node = pci_of_match_device(bus->parent, devfn); + if (dev->dev.of_node) + pr_debug("found DT node %pOF for device %04x:%04x\n", + dev->dev.of_node, dev->vendor, dev->device); /* non-destructively determine if device can be a master: */ @@ -425,12 +646,15 @@ static unsigned int pci_scan_bus(struct pci_bus *bus) pr_debug("%02x:%02x [%04x:%04x]\n", bus->number, dev->devfn, dev->vendor, dev->device); - switch (hdr_type & 0x7f) { + switch (hdr_type & PCI_HEADER_TYPE_MASK) { case PCI_HEADER_TYPE_NORMAL: if (class == PCI_CLASS_BRIDGE_PCI) goto bad; setup_device(dev, 6); + + pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); + pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); break; case PCI_HEADER_TYPE_BRIDGE: child_bus = pci_alloc_bus(); @@ -538,6 +762,22 @@ int pci_enable_device(struct pci_dev *dev) } EXPORT_SYMBOL(pci_enable_device); +/** + * pci_select_bars - Make BAR mask from the type of resource + * @dev: the PCI device for which BAR mask is made + * @flags: resource type mask to be selected + * + * This helper routine makes bar mask from the type of resource. + */ +int pci_select_bars(struct pci_dev *dev, unsigned long flags) +{ + int i, bars = 0; + for (i = 0; i < PCI_NUM_RESOURCES; i++) + if (pci_resource_flags(dev, i) & flags) + bars |= (1 << i); + return bars; +} + static u8 __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap, int *ttl) { @@ -620,7 +860,7 @@ u8 pci_find_capability(struct pci_dev *dev, int cap) { u8 pos; - pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type); + pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type & PCI_HEADER_TYPE_MASK); if (pos) pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap); diff --git a/drivers/pci/pci_iomap.c b/drivers/pci/pci_iomap.c index 2f06e60e38..61459ac669 100644 --- a/drivers/pci/pci_iomap.c +++ b/drivers/pci/pci_iomap.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Implement the default iomap interfaces * diff --git a/drivers/pci/pcie-designware-host.c b/drivers/pci/pcie-designware-host.c index b2d46d38f8..b34fc482ed 100644 --- a/drivers/pci/pcie-designware-host.c +++ b/drivers/pci/pcie-designware-host.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Synopsys DesignWare PCIe host controller driver * @@ -71,8 +71,8 @@ static void dw_pcie_set_local_bus_nr(struct pci_controller *host, int busno) int __init dw_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct device_d *dev = pci->dev; - struct device_node *np = dev->device_node; + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; struct of_pci_range range; struct of_pci_range_parser parser; struct resource *cfg_res; @@ -87,7 +87,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) ns = of_n_size_cells(np); cfg_res = dev_get_resource_by_name(dev, IORESOURCE_MEM, "config"); - if (cfg_res) { + if (!IS_ERR(cfg_res)) { pp->cfg0_size = resource_size(cfg_res) >> 1; pp->cfg1_size = resource_size(cfg_res) >> 1; pp->cfg0_base = cfg_res->start; diff --git a/drivers/pci/pcie-designware.c b/drivers/pci/pcie-designware.c index e2007dba6d..9c8bc772f9 100644 --- a/drivers/pci/pcie-designware.c +++ b/drivers/pci/pcie-designware.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Synopsys DesignWare PCIe host controller driver * @@ -281,8 +281,8 @@ void dw_pcie_setup(struct dw_pcie *pci) int ret; u32 val; u32 lanes; - struct device_d *dev = pci->dev; - struct device_node *np = dev->device_node; + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; if (pci->version >= 0x480A || (!pci->version && dw_pcie_iatu_unroll_enabled(pci))) { diff --git a/drivers/pci/pcie-designware.h b/drivers/pci/pcie-designware.h index de20654e42..4bb0795d06 100644 --- a/drivers/pci/pcie-designware.h +++ b/drivers/pci/pcie-designware.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Synopsys DesignWare PCIe host controller driver * @@ -180,7 +180,7 @@ struct dw_pcie_ops { }; struct dw_pcie { - struct device_d *dev; + struct device *dev; void __iomem *dbi_base; /* Used when iatu_unroll_enabled is true */ void __iomem *atu_base; diff --git a/drivers/pci/pcie-dw-rockchip.c b/drivers/pci/pcie-dw-rockchip.c new file mode 100644 index 0000000000..cc771e2cae --- /dev/null +++ b/drivers/pci/pcie-dw-rockchip.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Rockchip SoCs. + * + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + * http://www.rock-chips.com + * + * Author: Simon Xue <xxm@rock-chips.com> + */ + +#include <common.h> +#include <clock.h> +#include <abort.h> +#include <malloc.h> +#include <io.h> +#include <init.h> +#include <gpio.h> +#include <asm/mmu.h> +#include <of_gpio.h> +#include <of_device.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <of_address.h> +#include <of_pci.h> +#include <linux/gpio/consumer.h> +#include <linux/pci.h> +#include <linux/phy/phy.h> +#include <linux/reset.h> +#include <linux/sizes.h> +#include <linux/bitfield.h> + +#include "pcie-designware.h" + +/* + * The upper 16 bits of PCIE_CLIENT_CONFIG are a write + * mask for the lower 16 bits. + */ +#define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val)) +#define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val) +#define HIWORD_DISABLE_BIT(val) HIWORD_UPDATE(val, ~val) + +#define PCIE_CLIENT_RC_MODE HIWORD_UPDATE_BIT(0x40) +#define PCIE_CLIENT_ENABLE_LTSSM HIWORD_UPDATE_BIT(0xc) +#define PCIE_SMLH_LINKUP BIT(16) +#define PCIE_RDLH_LINKUP BIT(17) +#define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP) +#define PCIE_L0S_ENTRY 0x11 +#define PCIE_CLIENT_GENERAL_CONTROL 0x0 +#define PCIE_CLIENT_INTR_STATUS_LEGACY 0x8 +#define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c +#define PCIE_CLIENT_GENERAL_DEBUG 0x104 +#define PCIE_CLIENT_HOT_RESET_CTRL 0x180 +#define PCIE_CLIENT_LTSSM_STATUS 0x300 +#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4) +#define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0) + +struct rockchip_pcie { + struct dw_pcie pci; + void __iomem *apb_base; + struct phy *phy; + struct clk_bulk_data *clks; + unsigned int clk_cnt; + struct reset_control *rst; + struct gpio_desc *rst_gpio; + struct regulator *vpcie3v3; + struct irq_domain *irq_domain; +}; + +static inline struct rockchip_pcie *to_rockchip_pcie(struct dw_pcie *dw) +{ + return container_of(dw, struct rockchip_pcie, pci); +} + +static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, + u32 reg) +{ + return readl_relaxed(rockchip->apb_base + reg); +} + +static void rockchip_pcie_writel_apb(struct rockchip_pcie *rockchip, + u32 val, u32 reg) +{ + writel_relaxed(val, rockchip->apb_base + reg); +} + +static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip) +{ + rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM, + PCIE_CLIENT_GENERAL_CONTROL); +} + +static int rockchip_pcie_link_up(struct dw_pcie *pci) +{ + struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); + u32 val = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_LTSSM_STATUS); + + if ((val & PCIE_LINKUP) == PCIE_LINKUP && + (val & PCIE_LTSSM_STATUS_MASK) == PCIE_L0S_ENTRY) + return 1; + + return 0; +} + +static int rockchip_pcie_start_link(struct dw_pcie *pci) +{ + struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); + + /* Reset device */ + gpiod_set_value(rockchip->rst_gpio, 0); + + rockchip_pcie_enable_ltssm(rockchip); + + /* + * PCIe requires the refclk to be stable for 100µs prior to releasing + * PERST. See table 2-4 in section 2.6.2 AC Specifications of the PCI + * Express Card Electromechanical Specification, 1.1. However, we don't + * know if the refclk is coming from RC's PHY or external OSC. If it's + * from RC, so enabling LTSSM is the just right place to release #PERST. + * We need more extra time as before, rather than setting just + * 100us as we don't know how long should the device need to reset. + */ + mdelay(100); + gpiod_set_value(rockchip->rst_gpio, 1); + + return 0; +} + +static int rockchip_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); + u32 val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE); + + /* LTSSM enable control mode */ + rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL); + + rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_RC_MODE, + PCIE_CLIENT_GENERAL_CONTROL); + + dw_pcie_setup_rc(pp); + rockchip_pcie_start_link(pci); + + return 0; +} + +static const struct dw_pcie_host_ops rockchip_pcie_host_ops = { + .host_init = rockchip_pcie_host_init, +}; + +static int rockchip_pcie_clk_init(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->pci.dev; + int ret; + + ret = clk_bulk_get_all(dev, &rockchip->clks); + if (ret < 0) + return ret; + + rockchip->clk_cnt = ret; + + return clk_bulk_enable(rockchip->clk_cnt, rockchip->clks); +} + +static int rockchip_pcie_resource_get(struct device *dev, + struct rockchip_pcie *rockchip) +{ + struct resource *r; + + r = dev_request_mem_resource_by_name(dev, "apb"); + if (IS_ERR(r)) + return PTR_ERR(r); + rockchip->apb_base = IOMEM(r->start); + + + r = dev_request_mem_resource_by_name(dev, "dbi"); + if (IS_ERR(r)) + return PTR_ERR(r); + rockchip->pci.dbi_base = IOMEM(r->start); + + rockchip->rst_gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(rockchip->rst_gpio)) + return PTR_ERR(rockchip->rst_gpio); + + rockchip->rst = reset_control_array_get(dev); + if (IS_ERR(rockchip->rst)) + return dev_err_probe(dev, PTR_ERR(rockchip->rst), + "failed to get reset lines\n"); + + return 0; +} + +static int rockchip_pcie_phy_init(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->pci.dev; + int ret; + + rockchip->phy = phy_get(dev, "pcie-phy"); + if (IS_ERR(rockchip->phy)) + return dev_err_probe(dev, PTR_ERR(rockchip->phy), + "missing PHY\n"); + + ret = phy_init(rockchip->phy); + if (ret < 0) + return ret; + + ret = phy_power_on(rockchip->phy); + if (ret) + phy_exit(rockchip->phy); + + return ret; +} + +static void rockchip_pcie_phy_deinit(struct rockchip_pcie *rockchip) +{ + phy_exit(rockchip->phy); + phy_power_off(rockchip->phy); +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = rockchip_pcie_link_up, +}; + +static int rockchip_pcie_probe(struct device *dev) +{ + struct rockchip_pcie *rockchip; + struct pcie_port *pp; + int ret; + + rockchip = xzalloc(sizeof(*rockchip)); + if (!rockchip) + return -ENOMEM; + + rockchip->pci.dev = dev; + rockchip->pci.ops = &dw_pcie_ops; + + pp = &rockchip->pci.pp; + pp->ops = &rockchip_pcie_host_ops; + + ret = rockchip_pcie_resource_get(dev, rockchip); + if (ret) + return ret; + + ret = reset_control_assert(rockchip->rst); + if (ret) + return ret; + + /* DON'T MOVE ME: must be enable before PHY init */ + rockchip->vpcie3v3 = regulator_get(dev, "vpcie3v3"); + if (IS_ERR(rockchip->vpcie3v3)) { + if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(rockchip->vpcie3v3), + "failed to get vpcie3v3 regulator\n"); + rockchip->vpcie3v3 = NULL; + } else { + ret = regulator_enable(rockchip->vpcie3v3); + if (ret) { + dev_err(dev, "failed to enable vpcie3v3 regulator\n"); + return ret; + } + } + + ret = rockchip_pcie_phy_init(rockchip); + if (ret) + goto disable_regulator; + + ret = reset_control_deassert(rockchip->rst); + if (ret) + goto deinit_phy; + + ret = rockchip_pcie_clk_init(rockchip); + if (ret) + goto deinit_phy; + + ret = dw_pcie_host_init(pp); + if (!ret) + return 0; + + clk_bulk_disable(rockchip->clk_cnt, rockchip->clks); +deinit_phy: + rockchip_pcie_phy_deinit(rockchip); +disable_regulator: + if (rockchip->vpcie3v3) + regulator_disable(rockchip->vpcie3v3); + + return ret; +} + +static const struct of_device_id rockchip_pcie_of_match[] = { + { .compatible = "rockchip,rk3568-pcie", }, + { .compatible = "rockchip,rk3588-pcie", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_pcie_of_match); + +static struct driver rockchip_pcie_driver = { + .name = "rockchip-dw-pcie", + .of_compatible = DRV_OF_COMPAT(rockchip_pcie_of_match), + .probe = rockchip_pcie_probe, +}; +device_platform_driver(rockchip_pcie_driver); |