summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Kconfig20
-rw-r--r--drivers/pci/Makefile3
-rw-r--r--drivers/pci/bus.c14
-rw-r--r--drivers/pci/pci-ecam-generic.c208
-rw-r--r--drivers/pci/pci-efi.c19
-rw-r--r--drivers/pci/pci-efi.h2
-rw-r--r--drivers/pci/pci-imx6.c31
-rw-r--r--drivers/pci/pci-layerscape.c24
-rw-r--r--drivers/pci/pci-mvebu-phy.c2
-rw-r--r--drivers/pci/pci-mvebu.c15
-rw-r--r--drivers/pci/pci-mvebu.h2
-rw-r--r--drivers/pci/pci-tegra.c17
-rw-r--r--drivers/pci/pci.c262
-rw-r--r--drivers/pci/pci_iomap.c1
-rw-r--r--drivers/pci/pcie-designware-host.c8
-rw-r--r--drivers/pci/pcie-designware.c6
-rw-r--r--drivers/pci/pcie-designware.h4
-rw-r--r--drivers/pci/pcie-dw-rockchip.c300
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", &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);