From 94b84ac1979b3b70b80a6d499d0c397057a6c7b3 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 8 Apr 2020 18:10:54 -0500 Subject: PCI: dra7xx: Don't select CONFIG_PCI_DRA7XX_HOST by default Drivers should not be selected by default because that bloats the kernel for people who don't need them. Enable CONFIG_PCI_DRA7XX_HOST by default only if SOC_DRA7XX. Signed-off-by: Bjorn Helgaas Cc: Kishon Vijay Abraham I Cc: linux-omap@vger.kernel.org --- drivers/pci/controller/dwc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 03dcaf65d159..55d93b4bac58 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -26,7 +26,7 @@ config PCI_DRA7XX_HOST depends on OF && HAS_IOMEM && TI_PIPE3 select PCIE_DW_HOST select PCI_DRA7XX - default y + default y if SOC_DRA7XX help Enables support for the PCIe controller in the DRA7xx SoC to work in host mode. There are two instances of PCIe controller in DRA7xx. -- cgit v1.2.3 From fbedda4e4dc5a96d613dc056a82c3f14942f7998 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 8 Apr 2020 18:08:53 -0500 Subject: PCI: keystone: Don't select CONFIG_PCI_KEYSTONE_HOST by default Drivers should not be selected by default because that bloats the kernel for people who don't need them. Remove the "default y" for CONFIG_PCI_KEYSTONE_HOST. Signed-off-by: Bjorn Helgaas Cc: Murali Karicheri Cc: linux-arm-kernel@lists.infradead.org --- drivers/pci/controller/dwc/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 55d93b4bac58..ce13fd62d161 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -111,7 +111,6 @@ config PCI_KEYSTONE_HOST depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST select PCI_KEYSTONE - default y help Enables support for the PCIe controller in the Keystone SoC to work in host mode. The PCI controller on Keystone is based on -- cgit v1.2.3 From 0b104773b4f72ccd8af98a2f1efe69b174c344d3 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 9 Apr 2020 17:49:21 -0600 Subject: PCI: Constify struct pci_ecam_ops struct pci_ecam_ops is typically DT match table data which is defined to be const. It's also best practice for ops structs to be const. Ideally, we'd make struct pci_ops const as well, but that becomes pretty invasive, so for now we just cast it where needed. Link: https://lore.kernel.org/r/20200409234923.21598-2-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Acked-by: Catalin Marinas Cc: Catalin Marinas Cc: Will Deacon Cc: Lorenzo Pieralisi Cc: Andrew Murray Cc: Bjorn Helgaas Cc: "Rafael J. Wysocki" Cc: Len Brown Cc: Jonathan Chocron Cc: Zhou Wang Cc: Robert Richter Cc: Toan Le Cc: Marc Gonzalez Cc: Mans Rullgard Cc: linux-acpi@vger.kernel.org --- drivers/pci/controller/dwc/pcie-al.c | 2 +- drivers/pci/controller/dwc/pcie-hisi.c | 8 ++++---- drivers/pci/controller/pci-host-common.c | 6 +++--- drivers/pci/controller/pci-host-generic.c | 4 ++-- drivers/pci/controller/pci-thunder-ecam.c | 2 +- drivers/pci/controller/pci-thunder-pem.c | 4 ++-- drivers/pci/controller/pci-xgene.c | 4 ++-- drivers/pci/controller/pcie-tango.c | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index 1eeda2f6371f..270868f3859a 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -80,7 +80,7 @@ static int al_pcie_init(struct pci_config_window *cfg) return 0; } -struct pci_ecam_ops al_pcie_ops = { +const struct pci_ecam_ops al_pcie_ops = { .bus_shift = 20, .init = al_pcie_init, .pci_ops = { diff --git a/drivers/pci/controller/dwc/pcie-hisi.c b/drivers/pci/controller/dwc/pcie-hisi.c index 6d9e1b2b8f7b..90017045334d 100644 --- a/drivers/pci/controller/dwc/pcie-hisi.c +++ b/drivers/pci/controller/dwc/pcie-hisi.c @@ -104,7 +104,7 @@ static int hisi_pcie_init(struct pci_config_window *cfg) return 0; } -struct pci_ecam_ops hisi_pcie_ops = { +const struct pci_ecam_ops hisi_pcie_ops = { .bus_shift = 20, .init = hisi_pcie_init, .pci_ops = { @@ -362,7 +362,7 @@ static int hisi_pcie_platform_init(struct pci_config_window *cfg) return 0; } -struct pci_ecam_ops hisi_pcie_platform_ops = { +const struct pci_ecam_ops hisi_pcie_platform_ops = { .bus_shift = 20, .init = hisi_pcie_platform_init, .pci_ops = { @@ -375,11 +375,11 @@ struct pci_ecam_ops hisi_pcie_platform_ops = { static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { { .compatible = "hisilicon,hip06-pcie-ecam", - .data = (void *) &hisi_pcie_platform_ops, + .data = &hisi_pcie_platform_ops, }, { .compatible = "hisilicon,hip07-pcie-ecam", - .data = (void *) &hisi_pcie_platform_ops, + .data = &hisi_pcie_platform_ops, }, {}, }; diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index 250a3fc80ec6..f6d5dc068488 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -19,7 +19,7 @@ static void gen_pci_unmap_cfg(void *ptr) } static struct pci_config_window *gen_pci_init(struct device *dev, - struct list_head *resources, struct pci_ecam_ops *ops) + struct list_head *resources, const struct pci_ecam_ops *ops) { int err; struct resource cfgres; @@ -55,7 +55,7 @@ err_out: } int pci_host_common_probe(struct platform_device *pdev, - struct pci_ecam_ops *ops) + const struct pci_ecam_ops *ops) { struct device *dev = &pdev->dev; struct pci_host_bridge *bridge; @@ -82,7 +82,7 @@ int pci_host_common_probe(struct platform_device *pdev, bridge->dev.parent = dev; bridge->sysdata = cfg; bridge->busnr = cfg->busr.start; - bridge->ops = &ops->pci_ops; + bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pci-host-generic.c b/drivers/pci/controller/pci-host-generic.c index 75a2fb930d4b..7e9a7c0833b1 100644 --- a/drivers/pci/controller/pci-host-generic.c +++ b/drivers/pci/controller/pci-host-generic.c @@ -15,7 +15,7 @@ #include #include -static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { +static const struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { .bus_shift = 16, .pci_ops = { .map_bus = pci_ecam_map_bus, @@ -49,7 +49,7 @@ static void __iomem *pci_dw_ecam_map_bus(struct pci_bus *bus, return pci_ecam_map_bus(bus, devfn, where); } -static struct pci_ecam_ops pci_dw_ecam_bus_ops = { +static const struct pci_ecam_ops pci_dw_ecam_bus_ops = { .bus_shift = 20, .pci_ops = { .map_bus = pci_dw_ecam_map_bus, diff --git a/drivers/pci/controller/pci-thunder-ecam.c b/drivers/pci/controller/pci-thunder-ecam.c index 32d1d7b81ef4..c3fdd3e6b21c 100644 --- a/drivers/pci/controller/pci-thunder-ecam.c +++ b/drivers/pci/controller/pci-thunder-ecam.c @@ -345,7 +345,7 @@ static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn, return pci_generic_config_write(bus, devfn, where, size, val); } -struct pci_ecam_ops pci_thunder_ecam_ops = { +const struct pci_ecam_ops pci_thunder_ecam_ops = { .bus_shift = 20, .pci_ops = { .map_bus = pci_ecam_map_bus, diff --git a/drivers/pci/controller/pci-thunder-pem.c b/drivers/pci/controller/pci-thunder-pem.c index 9491e266b1ea..2e792707ceab 100644 --- a/drivers/pci/controller/pci-thunder-pem.c +++ b/drivers/pci/controller/pci-thunder-pem.c @@ -403,7 +403,7 @@ static int thunder_pem_acpi_init(struct pci_config_window *cfg) return thunder_pem_init(dev, cfg, res_pem); } -struct pci_ecam_ops thunder_pem_ecam_ops = { +const struct pci_ecam_ops thunder_pem_ecam_ops = { .bus_shift = 24, .init = thunder_pem_acpi_init, .pci_ops = { @@ -440,7 +440,7 @@ static int thunder_pem_platform_init(struct pci_config_window *cfg) return thunder_pem_init(dev, cfg, res_pem); } -static struct pci_ecam_ops pci_thunder_pem_ops = { +static const struct pci_ecam_ops pci_thunder_pem_ops = { .bus_shift = 24, .init = thunder_pem_platform_init, .pci_ops = { diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index de195fd430dc..d1efa8ffbae1 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -256,7 +256,7 @@ static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg) return xgene_pcie_ecam_init(cfg, XGENE_PCIE_IP_VER_1); } -struct pci_ecam_ops xgene_v1_pcie_ecam_ops = { +const struct pci_ecam_ops xgene_v1_pcie_ecam_ops = { .bus_shift = 16, .init = xgene_v1_pcie_ecam_init, .pci_ops = { @@ -271,7 +271,7 @@ static int xgene_v2_pcie_ecam_init(struct pci_config_window *cfg) return xgene_pcie_ecam_init(cfg, XGENE_PCIE_IP_VER_2); } -struct pci_ecam_ops xgene_v2_pcie_ecam_ops = { +const struct pci_ecam_ops xgene_v2_pcie_ecam_ops = { .bus_shift = 16, .init = xgene_v2_pcie_ecam_init, .pci_ops = { diff --git a/drivers/pci/controller/pcie-tango.c b/drivers/pci/controller/pcie-tango.c index 21a208da3f59..3b2b10906fdd 100644 --- a/drivers/pci/controller/pcie-tango.c +++ b/drivers/pci/controller/pcie-tango.c @@ -207,7 +207,7 @@ static int smp8759_config_write(struct pci_bus *bus, unsigned int devfn, return ret; } -static struct pci_ecam_ops smp8759_ecam_ops = { +static const struct pci_ecam_ops smp8759_ecam_ops = { .bus_shift = 20, .pci_ops = { .map_bus = pci_ecam_map_bus, -- cgit v1.2.3 From 0c59c06a7c90390c3985c9acd58a73320781c15e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 9 Apr 2020 17:49:22 -0600 Subject: PCI: host-generic: Support building as modules Enable building host-generic and its host-common dependency as a module. Link: https://lore.kernel.org/r/20200409234923.21598-3-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Will Deacon Acked-by: Bjorn Helgaas Cc: Lorenzo Pieralisi Cc: Andrew Murray Cc: Bjorn Helgaas Cc: Will Deacon Cc: linux-pci@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org --- drivers/pci/controller/Kconfig | 4 ++-- drivers/pci/controller/pci-host-common.c | 5 +++++ drivers/pci/controller/pci-host-generic.c | 7 +++++-- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 91bfdb784829..416a53414728 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -62,11 +62,11 @@ config PCIE_RCAR Say Y here if you want PCIe controller support on R-Car SoCs. config PCI_HOST_COMMON - bool + tristate select PCI_ECAM config PCI_HOST_GENERIC - bool "Generic PCI host controller" + tristate "Generic PCI host controller" depends on OF select PCI_HOST_COMMON select IRQ_DOMAIN diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index f6d5dc068488..6d15bc12e726 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -95,6 +96,7 @@ int pci_host_common_probe(struct platform_device *pdev, platform_set_drvdata(pdev, bridge->bus); return 0; } +EXPORT_SYMBOL_GPL(pci_host_common_probe); int pci_host_common_remove(struct platform_device *pdev) { @@ -107,3 +109,6 @@ int pci_host_common_remove(struct platform_device *pdev) return 0; } +EXPORT_SYMBOL_GPL(pci_host_common_remove); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-host-generic.c b/drivers/pci/controller/pci-host-generic.c index 7e9a7c0833b1..fd8cff61de14 100644 --- a/drivers/pci/controller/pci-host-generic.c +++ b/drivers/pci/controller/pci-host-generic.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -76,6 +77,7 @@ static const struct of_device_id gen_pci_of_match[] = { { }, }; +MODULE_DEVICE_TABLE(of, gen_pci_of_match); static int gen_pci_probe(struct platform_device *pdev) { @@ -92,9 +94,10 @@ static struct platform_driver gen_pci_driver = { .driver = { .name = "pci-host-generic", .of_match_table = gen_pci_of_match, - .suppress_bind_attrs = true, }, .probe = gen_pci_probe, .remove = pci_host_common_remove, }; -builtin_platform_driver(gen_pci_driver); +module_platform_driver(gen_pci_driver); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bf7116204657cabf6f74b5ade32424d04f480338 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 20 Apr 2020 08:52:27 +0200 Subject: PCI: dwc: Clean up computing of msix_tbl Commit 6f5e193bfb55 ("PCI: dwc: Fix dw_pcie_ep_raise_msix_irq() to get correct MSI-X table address") overcomplicated the computation of the msix_tbl address. Simplify it as it's simply the addr + offset. Provided addr is (void *) already. objdump -d shows no difference after this patch. Link: https://lore.kernel.org/r/20200420065227.4920-1-jslaby@suse.cz Signed-off-by: Jiri Slaby Signed-off-by: Lorenzo Pieralisi Cc: Kishon Vijay Abraham I Cc: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-ep.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 1cdcbd102ce8..c815d36905b6 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -433,7 +433,6 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct pci_epf_msix_tbl *msix_tbl; struct pci_epc *epc = ep->epc; - struct pci_epf_bar *epf_bar; u32 reg, msg_data, vec_ctrl; unsigned int aligned_offset; u32 tbl_offset; @@ -446,10 +445,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, bir = (tbl_offset & PCI_MSIX_TABLE_BIR); tbl_offset &= PCI_MSIX_TABLE_OFFSET; - epf_bar = ep->epf_bar[bir]; - msix_tbl = epf_bar->addr; - msix_tbl = (struct pci_epf_msix_tbl *)((char *)msix_tbl + tbl_offset); - + msix_tbl = ep->epf_bar[bir]->addr + tbl_offset; msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr; msg_data = msix_tbl[(interrupt_num - 1)].msg_data; vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl; -- cgit v1.2.3 From 819482a96790d25fca97ec5c752400edbef05753 Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Thu, 23 Apr 2020 11:18:03 +0800 Subject: PCI: dwc: Make hisi_pcie_platform_ops static Fix the following sparse warning: drivers/pci/controller/dwc/pcie-hisi.c:365:21: warning: symbol 'hisi_pcie_platform_ops' was not declared. Should it be static? Link: https://lore.kernel.org/r/1587611883-26960-1-git-send-email-zou_wei@huawei.com Reported-by: Hulk Robot Signed-off-by: Zou Wei Signed-off-by: Lorenzo Pieralisi Reviewed-by: Zhou Wang --- drivers/pci/controller/dwc/pcie-hisi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-hisi.c b/drivers/pci/controller/dwc/pcie-hisi.c index 6d9e1b2b8f7b..11f5ff711d4d 100644 --- a/drivers/pci/controller/dwc/pcie-hisi.c +++ b/drivers/pci/controller/dwc/pcie-hisi.c @@ -362,7 +362,7 @@ static int hisi_pcie_platform_init(struct pci_config_window *cfg) return 0; } -struct pci_ecam_ops hisi_pcie_platform_ops = { +static struct pci_ecam_ops hisi_pcie_platform_ops = { .bus_shift = 20, .init = hisi_pcie_platform_init, .pci_ops = { -- cgit v1.2.3 From bca718988b9008d0d5f504e2d318178fc84958c1 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Apr 2020 10:16:37 +0200 Subject: PCI: v3-semi: Fix a memory leak in v3_pci_probe() error handling paths If we fails somewhere in 'v3_pci_probe()', we need to free 'host'. Use the managed version of 'pci_alloc_host_bridge()' to do that easily. The use of managed resources is already widely used in this driver. Link: https://lore.kernel.org/r/20200418081637.1585-1-christophe.jaillet@wanadoo.fr Fixes: 68a15eb7bd0c ("PCI: v3-semi: Add V3 Semiconductor PCI host driver") Signed-off-by: Christophe JAILLET [lorenzo.pieralisi@arm.com: commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Linus Walleij --- drivers/pci/controller/pci-v3-semi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index bd05221f5a22..ddcb4571a79b 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -720,7 +720,7 @@ static int v3_pci_probe(struct platform_device *pdev) int irq; int ret; - host = pci_alloc_host_bridge(sizeof(*v3)); + host = devm_pci_alloc_host_bridge(dev, sizeof(*v3)); if (!host) return -ENOMEM; -- cgit v1.2.3 From c96efe26569204555705076368f8ef7565d06fa6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 27 Mar 2020 13:45:56 +0000 Subject: PCI: altera: Clean up indentation issue on a return statement A return statment is indented incorrectly, remove extraneous space. Link: https://lore.kernel.org/r/20200327134556.265411-1-colin.king@canonical.com Signed-off-by: Colin Ian King Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-altera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index b447c3e4abad..24cb1c331058 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -193,7 +193,7 @@ static bool altera_pcie_valid_device(struct altera_pcie *pcie, if (bus->number == pcie->root_bus_nr && dev > 0) return false; - return true; + return true; } static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) -- cgit v1.2.3 From b2f75a41eaa6bfc4aa6f6a1faefbf21c2c8d1588 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 9 Apr 2020 17:49:23 -0600 Subject: PCI: host-generic: Eliminate pci_host_common_probe wrappers Most ECAM host drivers are just different pci_ecam_ops which can be DT match table data. That's already the case in some cases, but let's do that for all the ECAM drivers. Then we can use of_device_get_match_data() in pci_host_common_probe() and eliminate the probe wrapper functions and use pci_host_common_probe() directly for probe. Link: https://lore.kernel.org/r/20200409234923.21598-4-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Zhou Wang Cc: Lorenzo Pieralisi Cc: Andrew Murray Cc: Bjorn Helgaas Cc: Will Deacon Cc: Robert Richter Cc: Marc Gonzalez Cc: Mans Rullgard Cc: linux-pci@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org --- drivers/pci/controller/dwc/pcie-hisi.c | 11 +---------- drivers/pci/controller/pci-host-common.c | 9 +++++++-- drivers/pci/controller/pci-host-generic.c | 15 +-------------- drivers/pci/controller/pci-thunder-ecam.c | 12 +++++------- drivers/pci/controller/pci-thunder-pem.c | 12 +++++------- drivers/pci/controller/pcie-tango.c | 7 +++++-- 6 files changed, 24 insertions(+), 42 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/dwc/pcie-hisi.c b/drivers/pci/controller/dwc/pcie-hisi.c index 90017045334d..0ba50fb473b1 100644 --- a/drivers/pci/controller/dwc/pcie-hisi.c +++ b/drivers/pci/controller/dwc/pcie-hisi.c @@ -332,15 +332,6 @@ static struct platform_driver hisi_pcie_driver = { }; builtin_platform_driver(hisi_pcie_driver); -static int hisi_pcie_almost_ecam_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct pci_ecam_ops *ops; - - ops = (struct pci_ecam_ops *)of_device_get_match_data(dev); - return pci_host_common_probe(pdev, ops); -} - static int hisi_pcie_platform_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; @@ -385,7 +376,7 @@ static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { }; static struct platform_driver hisi_pcie_almost_ecam_driver = { - .probe = hisi_pcie_almost_ecam_probe, + .probe = pci_host_common_probe, .driver = { .name = "hisi-pcie-almost-ecam", .of_match_table = hisi_pcie_almost_ecam_of_match, diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index 6d15bc12e726..953de57f6c57 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -55,15 +56,19 @@ err_out: return ERR_PTR(err); } -int pci_host_common_probe(struct platform_device *pdev, - const struct pci_ecam_ops *ops) +int pci_host_common_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pci_host_bridge *bridge; struct pci_config_window *cfg; struct list_head resources; + const struct pci_ecam_ops *ops; int ret; + ops = of_device_get_match_data(&pdev->dev); + if (!ops) + return -ENODEV; + bridge = devm_pci_alloc_host_bridge(dev, 0); if (!bridge) return -ENOMEM; diff --git a/drivers/pci/controller/pci-host-generic.c b/drivers/pci/controller/pci-host-generic.c index fd8cff61de14..b51977abfdf1 100644 --- a/drivers/pci/controller/pci-host-generic.c +++ b/drivers/pci/controller/pci-host-generic.c @@ -11,8 +11,6 @@ #include #include #include -#include -#include #include #include @@ -79,23 +77,12 @@ static const struct of_device_id gen_pci_of_match[] = { }; MODULE_DEVICE_TABLE(of, gen_pci_of_match); -static int gen_pci_probe(struct platform_device *pdev) -{ - const struct of_device_id *of_id; - struct pci_ecam_ops *ops; - - of_id = of_match_node(gen_pci_of_match, pdev->dev.of_node); - ops = (struct pci_ecam_ops *)of_id->data; - - return pci_host_common_probe(pdev, ops); -} - static struct platform_driver gen_pci_driver = { .driver = { .name = "pci-host-generic", .of_match_table = gen_pci_of_match, }, - .probe = gen_pci_probe, + .probe = pci_host_common_probe, .remove = pci_host_common_remove, }; module_platform_driver(gen_pci_driver); diff --git a/drivers/pci/controller/pci-thunder-ecam.c b/drivers/pci/controller/pci-thunder-ecam.c index c3fdd3e6b21c..7e8835fee5f7 100644 --- a/drivers/pci/controller/pci-thunder-ecam.c +++ b/drivers/pci/controller/pci-thunder-ecam.c @@ -357,22 +357,20 @@ const struct pci_ecam_ops pci_thunder_ecam_ops = { #ifdef CONFIG_PCI_HOST_THUNDER_ECAM static const struct of_device_id thunder_ecam_of_match[] = { - { .compatible = "cavium,pci-host-thunder-ecam" }, + { + .compatible = "cavium,pci-host-thunder-ecam", + .data = &pci_thunder_ecam_ops, + }, { }, }; -static int thunder_ecam_probe(struct platform_device *pdev) -{ - return pci_host_common_probe(pdev, &pci_thunder_ecam_ops); -} - static struct platform_driver thunder_ecam_driver = { .driver = { .name = KBUILD_MODNAME, .of_match_table = thunder_ecam_of_match, .suppress_bind_attrs = true, }, - .probe = thunder_ecam_probe, + .probe = pci_host_common_probe, }; builtin_platform_driver(thunder_ecam_driver); diff --git a/drivers/pci/controller/pci-thunder-pem.c b/drivers/pci/controller/pci-thunder-pem.c index 2e792707ceab..3f847969143e 100644 --- a/drivers/pci/controller/pci-thunder-pem.c +++ b/drivers/pci/controller/pci-thunder-pem.c @@ -451,22 +451,20 @@ static const struct pci_ecam_ops pci_thunder_pem_ops = { }; static const struct of_device_id thunder_pem_of_match[] = { - { .compatible = "cavium,pci-host-thunder-pem" }, + { + .compatible = "cavium,pci-host-thunder-pem", + .data = &pci_thunder_pem_ops, + }, { }, }; -static int thunder_pem_probe(struct platform_device *pdev) -{ - return pci_host_common_probe(pdev, &pci_thunder_pem_ops); -} - static struct platform_driver thunder_pem_driver = { .driver = { .name = KBUILD_MODNAME, .of_match_table = thunder_pem_of_match, .suppress_bind_attrs = true, }, - .probe = thunder_pem_probe, + .probe = pci_host_common_probe, }; builtin_platform_driver(thunder_pem_driver); diff --git a/drivers/pci/controller/pcie-tango.c b/drivers/pci/controller/pcie-tango.c index 3b2b10906fdd..c13367c30fc6 100644 --- a/drivers/pci/controller/pcie-tango.c +++ b/drivers/pci/controller/pcie-tango.c @@ -295,11 +295,14 @@ static int tango_pcie_probe(struct platform_device *pdev) spin_lock_init(&pcie->used_msi_lock); irq_set_chained_handler_and_data(virq, tango_msi_isr, pcie); - return pci_host_common_probe(pdev, &smp8759_ecam_ops); + return pci_host_common_probe(pdev); } static const struct of_device_id tango_pcie_ids[] = { - { .compatible = "sigma,smp8759-pcie" }, + { + .compatible = "sigma,smp8759-pcie", + .data = &smp8759_ecam_ops, + }, { }, }; -- cgit v1.2.3 From 2b9f217433e31d125fb697ca7974d3de3ecc3e92 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 4 Oct 2019 14:29:41 +0100 Subject: PCI: rcar: Fix incorrect programming of OB windows The outbound windows (PCIEPAUR(x), PCIEPALR(x)) describe a mapping between a CPU address (which is determined by the window number 'x') and a programmed PCI address - Thus allowing the controller to translate CPU accesses into PCI accesses. However the existing code incorrectly writes the CPU address - lets fix this by writing the PCI address instead. For memory transactions, existing DT users describe a 1:1 identity mapping and thus this change should have no effect. However the same isn't true for I/O. Link: https://lore.kernel.org/r/20191004132941.6660-1-andrew.murray@arm.com Fixes: c25da4778803 ("PCI: rcar: Add Renesas R-Car PCIe driver") Tested-by: Marek Vasut Signed-off-by: Andrew Murray Signed-off-by: Lorenzo Pieralisi Reviewed-by: Marek Vasut --- drivers/pci/controller/pcie-rcar.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/pcie-rcar.c b/drivers/pci/controller/pcie-rcar.c index 759c6542c5c8..1bae6a4abaae 100644 --- a/drivers/pci/controller/pcie-rcar.c +++ b/drivers/pci/controller/pcie-rcar.c @@ -333,11 +333,12 @@ static struct pci_ops rcar_pcie_ops = { }; static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie, - struct resource *res) + struct resource_entry *window) { /* Setup PCIe address space mappings for each resource */ resource_size_t size; resource_size_t res_start; + struct resource *res = window->res; u32 mask; rcar_pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win)); @@ -351,9 +352,9 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie, rcar_pci_write_reg(pcie, mask << 7, PCIEPAMR(win)); if (res->flags & IORESOURCE_IO) - res_start = pci_pio_to_address(res->start); + res_start = pci_pio_to_address(res->start) - window->offset; else - res_start = res->start; + res_start = res->start - window->offset; rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPAUR(win)); rcar_pci_write_reg(pcie, lower_32_bits(res_start) & ~0x7F, @@ -382,7 +383,7 @@ static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pci) switch (resource_type(res)) { case IORESOURCE_IO: case IORESOURCE_MEM: - rcar_pcie_setup_window(i, pci, res); + rcar_pcie_setup_window(i, pci, win); i++; break; case IORESOURCE_BUS: -- cgit v1.2.3 From ce351636c67f75a9f282ab69283be231d2c8e845 Mon Sep 17 00:00:00 2001 From: Kazufumi Ikeda Date: Sat, 14 Mar 2020 20:12:32 +0100 Subject: PCI: rcar: Add suspend/resume Add suspend/resume support for rcar. The resume handler reprograms the hardware based on the software state kept in specific device structures, so there is no need to save registers on suspend. Link: https://lore.kernel.org/r/20200314191232.3122290-1-marek.vasut@gmail.com Link: https://lore.kernel.org/r/20200426123148.56051-1-marek.vasut@gmail.com Signed-off-by: Kazufumi Ikeda Signed-off-by: Gaku Inami Signed-off-by: Marek Vasut Signed-off-by: Lorenzo Pieralisi Cc: Geert Uytterhoeven Cc: Phil Edworthy Cc: Simon Horman Cc: Wolfram Sang Cc: linux-renesas-soc@vger.kernel.org --- drivers/pci/controller/pcie-rcar.c | 90 +++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 15 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/pcie-rcar.c b/drivers/pci/controller/pcie-rcar.c index 1bae6a4abaae..59e55f56e386 100644 --- a/drivers/pci/controller/pcie-rcar.c +++ b/drivers/pci/controller/pcie-rcar.c @@ -153,6 +153,7 @@ struct rcar_pcie { int root_bus_nr; struct clk *bus_clk; struct rcar_msi msi; + int (*phy_init_fn)(struct rcar_pcie *pcie); }; static void rcar_pci_write_reg(struct rcar_pcie *pcie, u32 val, @@ -453,6 +454,32 @@ done: (macsr & LINK_SPEED) == LINK_SPEED_5_0GTS ? "5" : "2.5"); } +static void rcar_pcie_hw_enable(struct rcar_pcie *pci) +{ + struct resource_entry *win; + LIST_HEAD(res); + int i = 0; + + /* Try setting 5 GT/s link speed */ + rcar_pcie_force_speedup(pci); + + /* Setup PCI resources */ + resource_list_for_each_entry(win, &pci->resources) { + struct resource *res = win->res; + + if (!res->flags) + continue; + + switch (resource_type(res)) { + case IORESOURCE_IO: + case IORESOURCE_MEM: + rcar_pcie_setup_window(i, pci, win); + i++; + break; + } + } +} + static int rcar_pcie_enable(struct rcar_pcie *pcie) { struct device *dev = pcie->dev; @@ -892,11 +919,25 @@ static void rcar_pcie_unmap_msi(struct rcar_pcie *pcie) irq_domain_remove(msi->domain); } +static void rcar_pcie_hw_enable_msi(struct rcar_pcie *pcie) +{ + struct rcar_msi *msi = &pcie->msi; + unsigned long base; + + /* setup MSI data target */ + base = virt_to_phys((void *)msi->pages); + + rcar_pci_write_reg(pcie, lower_32_bits(base) | MSIFE, PCIEMSIALR); + rcar_pci_write_reg(pcie, upper_32_bits(base), PCIEMSIAUR); + + /* enable all MSI interrupts */ + rcar_pci_write_reg(pcie, 0xffffffff, PCIEMSIIER); +} + static int rcar_pcie_enable_msi(struct rcar_pcie *pcie) { struct device *dev = pcie->dev; struct rcar_msi *msi = &pcie->msi; - phys_addr_t base; int err, i; mutex_init(&msi->lock); @@ -935,17 +976,7 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie) /* setup MSI data target */ msi->pages = __get_free_pages(GFP_KERNEL, 0); - if (!msi->pages) { - err = -ENOMEM; - goto err; - } - base = virt_to_phys((void *)msi->pages); - - rcar_pci_write_reg(pcie, lower_32_bits(base) | MSIFE, PCIEMSIALR); - rcar_pci_write_reg(pcie, upper_32_bits(base), PCIEMSIAUR); - - /* enable all MSI interrupts */ - rcar_pci_write_reg(pcie, 0xffffffff, PCIEMSIIER); + rcar_pcie_hw_enable_msi(pcie); return 0; @@ -1117,7 +1148,6 @@ static int rcar_pcie_probe(struct platform_device *pdev) struct rcar_pcie *pcie; u32 data; int err; - int (*phy_init_fn)(struct rcar_pcie *); struct pci_host_bridge *bridge; bridge = pci_alloc_host_bridge(sizeof(*pcie)); @@ -1157,8 +1187,8 @@ static int rcar_pcie_probe(struct platform_device *pdev) if (err) goto err_clk_disable; - phy_init_fn = of_device_get_match_data(dev); - err = phy_init_fn(pcie); + pcie->phy_init_fn = of_device_get_match_data(dev); + err = pcie->phy_init_fn(pcie); if (err) { dev_err(dev, "failed to init PCIe PHY\n"); goto err_clk_disable; @@ -1220,6 +1250,35 @@ err_free_bridge: return err; } +static int __maybe_unused rcar_pcie_resume(struct device *dev) +{ + struct rcar_pcie *pcie = dev_get_drvdata(dev); + unsigned int data; + int err; + + err = rcar_pcie_parse_map_dma_ranges(pcie); + if (err) + return 0; + + /* Failure to get a link might just be that no cards are inserted */ + err = pcie->phy_init_fn(pcie); + if (err) { + dev_info(dev, "PCIe link down\n"); + return 0; + } + + data = rcar_pci_read_reg(pcie, MACSR); + dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); + + /* Enable MSI */ + if (IS_ENABLED(CONFIG_PCI_MSI)) + rcar_pcie_hw_enable_msi(pcie); + + rcar_pcie_hw_enable(pcie); + + return 0; +} + static int rcar_pcie_resume_noirq(struct device *dev) { struct rcar_pcie *pcie = dev_get_drvdata(dev); @@ -1235,6 +1294,7 @@ static int rcar_pcie_resume_noirq(struct device *dev) } static const struct dev_pm_ops rcar_pcie_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, rcar_pcie_resume) .resume_noirq = rcar_pcie_resume_noirq, }; -- cgit v1.2.3 From 63605f1cfcc56bcb25c48bbee75a679d85ba7675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 14 Apr 2020 12:25:12 +0200 Subject: PCI: tegra: Fix reporting GPIO error value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Error code is stored in rp->reset_gpio and not in err variable. Link: https://lore.kernel.org/r/20200414102512.27506-1-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding Acked-by: Rob Herring --- drivers/pci/controller/pci-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 3e64ba6a36a8..e3e917243e10 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2219,8 +2219,8 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) if (PTR_ERR(rp->reset_gpio) == -ENOENT) { rp->reset_gpio = NULL; } else { - dev_err(dev, "failed to get reset GPIO: %d\n", - err); + dev_err(dev, "failed to get reset GPIO: %ld\n", + PTR_ERR(rp->reset_gpio)); return PTR_ERR(rp->reset_gpio); } } -- cgit v1.2.3 From a18f4b6ea50b81e28bd05381883a531ab345f753 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 7 May 2020 13:33:12 +0100 Subject: PCI: rcar: Rename pcie-rcar.c to pcie-rcar-host.c This commit renames pcie-rcar.c to pcie-rcar-host.c in preparation for adding support for endpoint mode. CONFIG_PCIE_RCAR is kept so that arm64 defconfig change can be a separate patch. With this patch both config options PCIE_RCAR and PCIE_RCAR_HOST will be available but PCIE_RCAR internally selects PCIE_RCAR_HOST so that bisect builds wont be affected. Link: https://lore.kernel.org/r/1588854799-13710-2-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Lad Prabhakar Signed-off-by: Lorenzo Pieralisi Reviewed-by: Yoshihiro Shimoda --- drivers/pci/controller/Kconfig | 10 + drivers/pci/controller/Makefile | 2 +- drivers/pci/controller/pcie-rcar-host.c | 1310 +++++++++++++++++++++++++++++++ drivers/pci/controller/pcie-rcar.c | 1310 ------------------------------- 4 files changed, 1321 insertions(+), 1311 deletions(-) create mode 100644 drivers/pci/controller/pcie-rcar-host.c delete mode 100644 drivers/pci/controller/pcie-rcar.c (limited to 'drivers/pci/controller') diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 91bfdb784829..32dcab3c103f 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -58,8 +58,18 @@ config PCIE_RCAR bool "Renesas R-Car PCIe controller" depends on ARCH_RENESAS || COMPILE_TEST depends on PCI_MSI_IRQ_DOMAIN + select PCIE_RCAR_HOST help Say Y here if you want PCIe controller support on R-Car SoCs. + This option will be removed after arm64 defconfig is updated. + +config PCIE_RCAR_HOST + bool "Renesas R-Car PCIe host controller" + depends on ARCH_RENESAS || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + help + Say Y here if you want PCIe controller support on R-Car SoCs in host + mode. config PCI_HOST_COMMON bool diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 158c59771824..9dbccb5b24e1 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o -obj-$(CONFIG_PCIE_RCAR) += pcie-rcar.o +obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar-host.o obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c new file mode 100644 index 000000000000..59e55f56e386 --- /dev/null +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -0,0 +1,1310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe driver for Renesas R-Car SoCs + * Copyright (C) 2014 Renesas Electronics Europe Ltd + * + * Based on: + * arch/sh/drivers/pci/pcie-sh7786.c + * arch/sh/drivers/pci/ops-sh7786.c + * Copyright (C) 2009 - 2011 Paul Mundt + * + * Author: Phil Edworthy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCIECAR 0x000010 +#define PCIECCTLR 0x000018 +#define CONFIG_SEND_ENABLE BIT(31) +#define TYPE0 (0 << 8) +#define TYPE1 BIT(8) +#define PCIECDR 0x000020 +#define PCIEMSR 0x000028 +#define PCIEINTXR 0x000400 +#define PCIEPHYSR 0x0007f0 +#define PHYRDY BIT(0) +#define PCIEMSITXR 0x000840 + +/* Transfer control */ +#define PCIETCTLR 0x02000 +#define DL_DOWN BIT(3) +#define CFINIT BIT(0) +#define PCIETSTR 0x02004 +#define DATA_LINK_ACTIVE BIT(0) +#define PCIEERRFR 0x02020 +#define UNSUPPORTED_REQUEST BIT(4) +#define PCIEMSIFR 0x02044 +#define PCIEMSIALR 0x02048 +#define MSIFE BIT(0) +#define PCIEMSIAUR 0x0204c +#define PCIEMSIIER 0x02050 + +/* root port address */ +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) + +/* local address reg & mask */ +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) +#define LAM_PREFETCH BIT(3) +#define LAM_64BIT BIT(2) +#define LAR_ENABLE BIT(1) + +/* PCIe address reg & mask */ +#define PCIEPALR(x) (0x03400 + ((x) * 0x20)) +#define PCIEPAUR(x) (0x03404 + ((x) * 0x20)) +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) +#define PAR_ENABLE BIT(31) +#define IO_SPACE BIT(8) + +/* Configuration */ +#define PCICONF(x) (0x010000 + ((x) * 0x4)) +#define PMCAP(x) (0x010040 + ((x) * 0x4)) +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) +#define VCCAP(x) (0x010100 + ((x) * 0x4)) + +/* link layer */ +#define IDSETR1 0x011004 +#define TLCTLR 0x011048 +#define MACSR 0x011054 +#define SPCHGFIN BIT(4) +#define SPCHGFAIL BIT(6) +#define SPCHGSUC BIT(7) +#define LINK_SPEED (0xf << 16) +#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 +#define SPCNGRSN BIT(31) + +/* R-Car H1 PHY */ +#define H1_PCIEPHYADRR 0x04000c +#define WRITE_CMD BIT(16) +#define PHY_ACK BIT(24) +#define RATE_POS 12 +#define LANE_POS 8 +#define ADR_POS 0 +#define H1_PCIEPHYDOUTR 0x040014 + +/* R-Car Gen2 PHY */ +#define GEN2_PCIEPHYADDR 0x780 +#define GEN2_PCIEPHYDATA 0x784 +#define GEN2_PCIEPHYCTRL 0x78c + +#define INT_PCI_MSI_NR 32 + +#define RCONF(x) (PCICONF(0) + (x)) +#define RPMCAP(x) (PMCAP(0) + (x)) +#define REXPCAP(x) (EXPCAP(0) + (x)) +#define RVCCAP(x) (VCCAP(0) + (x)) + +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) + +#define RCAR_PCI_MAX_RESOURCES 4 +#define MAX_NR_INBOUND_MAPS 6 + +struct rcar_msi { + DECLARE_BITMAP(used, INT_PCI_MSI_NR); + struct irq_domain *domain; + struct msi_controller chip; + unsigned long pages; + struct mutex lock; + int irq1; + int irq2; +}; + +static inline struct rcar_msi *to_rcar_msi(struct msi_controller *chip) +{ + return container_of(chip, struct rcar_msi, chip); +} + +/* Structure representing the PCIe interface */ +struct rcar_pcie { + struct device *dev; + struct phy *phy; + void __iomem *base; + struct list_head resources; + int root_bus_nr; + struct clk *bus_clk; + struct rcar_msi msi; + int (*phy_init_fn)(struct rcar_pcie *pcie); +}; + +static void rcar_pci_write_reg(struct rcar_pcie *pcie, u32 val, + unsigned int reg) +{ + writel(val, pcie->base + reg); +} + +static u32 rcar_pci_read_reg(struct rcar_pcie *pcie, unsigned int reg) +{ + return readl(pcie->base + reg); +} + +enum { + RCAR_PCI_ACCESS_READ, + RCAR_PCI_ACCESS_WRITE, +}; + +static void rcar_rmw32(struct rcar_pcie *pcie, int where, u32 mask, u32 data) +{ + unsigned int shift = BITS_PER_BYTE * (where & 3); + u32 val = rcar_pci_read_reg(pcie, where & ~3); + + val &= ~(mask << shift); + val |= data << shift; + rcar_pci_write_reg(pcie, val, where & ~3); +} + +static u32 rcar_read_conf(struct rcar_pcie *pcie, int where) +{ + unsigned int shift = BITS_PER_BYTE * (where & 3); + u32 val = rcar_pci_read_reg(pcie, where & ~3); + + return val >> shift; +} + +/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */ +static int rcar_pcie_config_access(struct rcar_pcie *pcie, + unsigned char access_type, struct pci_bus *bus, + unsigned int devfn, int where, u32 *data) +{ + unsigned int dev, func, reg, index; + + dev = PCI_SLOT(devfn); + func = PCI_FUNC(devfn); + reg = where & ~3; + index = reg / 4; + + /* + * While each channel has its own memory-mapped extended config + * space, it's generally only accessible when in endpoint mode. + * When in root complex mode, the controller is unable to target + * itself with either type 0 or type 1 accesses, and indeed, any + * controller initiated target transfer to its own config space + * result in a completer abort. + * + * Each channel effectively only supports a single device, but as + * the same channel <-> device access works for any PCI_SLOT() + * value, we cheat a bit here and bind the controller's config + * space to devfn 0 in order to enable self-enumeration. In this + * case the regular ECAR/ECDR path is sidelined and the mangled + * config access itself is initiated as an internal bus transaction. + */ + if (pci_is_root_bus(bus)) { + if (dev != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (access_type == RCAR_PCI_ACCESS_READ) { + *data = rcar_pci_read_reg(pcie, PCICONF(index)); + } else { + /* Keep an eye out for changes to the root bus number */ + if (pci_is_root_bus(bus) && (reg == PCI_PRIMARY_BUS)) + pcie->root_bus_nr = *data & 0xff; + + rcar_pci_write_reg(pcie, *data, PCICONF(index)); + } + + return PCIBIOS_SUCCESSFUL; + } + + if (pcie->root_bus_nr < 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Clear errors */ + rcar_pci_write_reg(pcie, rcar_pci_read_reg(pcie, PCIEERRFR), PCIEERRFR); + + /* Set the PIO address */ + rcar_pci_write_reg(pcie, PCIE_CONF_BUS(bus->number) | + PCIE_CONF_DEV(dev) | PCIE_CONF_FUNC(func) | reg, PCIECAR); + + /* Enable the configuration access */ + if (bus->parent->number == pcie->root_bus_nr) + rcar_pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE0, PCIECCTLR); + else + rcar_pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE1, PCIECCTLR); + + /* Check for errors */ + if (rcar_pci_read_reg(pcie, PCIEERRFR) & UNSUPPORTED_REQUEST) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Check for master and target aborts */ + if (rcar_read_conf(pcie, RCONF(PCI_STATUS)) & + (PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (access_type == RCAR_PCI_ACCESS_READ) + *data = rcar_pci_read_reg(pcie, PCIECDR); + else + rcar_pci_write_reg(pcie, *data, PCIECDR); + + /* Disable the configuration access */ + rcar_pci_write_reg(pcie, 0, PCIECCTLR); + + return PCIBIOS_SUCCESSFUL; +} + +static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct rcar_pcie *pcie = bus->sysdata; + int ret; + + ret = rcar_pcie_config_access(pcie, RCAR_PCI_ACCESS_READ, + bus, devfn, where, val); + if (ret != PCIBIOS_SUCCESSFUL) { + *val = 0xffffffff; + return ret; + } + + if (size == 1) + *val = (*val >> (BITS_PER_BYTE * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (BITS_PER_BYTE * (where & 2))) & 0xffff; + + dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n", + bus->number, devfn, where, size, *val); + + return ret; +} + +/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */ +static int rcar_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct rcar_pcie *pcie = bus->sysdata; + unsigned int shift; + u32 data; + int ret; + + ret = rcar_pcie_config_access(pcie, RCAR_PCI_ACCESS_READ, + bus, devfn, where, &data); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + dev_dbg(&bus->dev, "pcie-config-write: bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08x\n", + bus->number, devfn, where, size, val); + + if (size == 1) { + shift = BITS_PER_BYTE * (where & 3); + data &= ~(0xff << shift); + data |= ((val & 0xff) << shift); + } else if (size == 2) { + shift = BITS_PER_BYTE * (where & 2); + data &= ~(0xffff << shift); + data |= ((val & 0xffff) << shift); + } else + data = val; + + ret = rcar_pcie_config_access(pcie, RCAR_PCI_ACCESS_WRITE, + bus, devfn, where, &data); + + return ret; +} + +static struct pci_ops rcar_pcie_ops = { + .read = rcar_pcie_read_conf, + .write = rcar_pcie_write_conf, +}; + +static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie, + struct resource_entry *window) +{ + /* Setup PCIe address space mappings for each resource */ + resource_size_t size; + resource_size_t res_start; + struct resource *res = window->res; + u32 mask; + + rcar_pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win)); + + /* + * The PAMR mask is calculated in units of 128Bytes, which + * keeps things pretty simple. + */ + size = resource_size(res); + mask = (roundup_pow_of_two(size) / SZ_128) - 1; + rcar_pci_write_reg(pcie, mask << 7, PCIEPAMR(win)); + + if (res->flags & IORESOURCE_IO) + res_start = pci_pio_to_address(res->start) - window->offset; + else + res_start = res->start - window->offset; + + rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPAUR(win)); + rcar_pci_write_reg(pcie, lower_32_bits(res_start) & ~0x7F, + PCIEPALR(win)); + + /* First resource is for IO */ + mask = PAR_ENABLE; + if (res->flags & IORESOURCE_IO) + mask |= IO_SPACE; + + rcar_pci_write_reg(pcie, mask, PCIEPTCTLR(win)); +} + +static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pci) +{ + struct resource_entry *win; + int i = 0; + + /* Setup PCI resources */ + resource_list_for_each_entry(win, &pci->resources) { + struct resource *res = win->res; + + if (!res->flags) + continue; + + switch (resource_type(res)) { + case IORESOURCE_IO: + case IORESOURCE_MEM: + rcar_pcie_setup_window(i, pci, win); + i++; + break; + case IORESOURCE_BUS: + pci->root_bus_nr = res->start; + break; + default: + continue; + } + + pci_add_resource(resource, res); + } + + return 1; +} + +static void rcar_pcie_force_speedup(struct rcar_pcie *pcie) +{ + struct device *dev = pcie->dev; + unsigned int timeout = 1000; + u32 macsr; + + if ((rcar_pci_read_reg(pcie, MACS2R) & LINK_SPEED) != LINK_SPEED_5_0GTS) + return; + + if (rcar_pci_read_reg(pcie, MACCTLR) & SPEED_CHANGE) { + dev_err(dev, "Speed change already in progress\n"); + return; + } + + macsr = rcar_pci_read_reg(pcie, MACSR); + if ((macsr & LINK_SPEED) == LINK_SPEED_5_0GTS) + goto done; + + /* Set target link speed to 5.0 GT/s */ + rcar_rmw32(pcie, EXPCAP(12), PCI_EXP_LNKSTA_CLS, + PCI_EXP_LNKSTA_CLS_5_0GB); + + /* Set speed change reason as intentional factor */ + rcar_rmw32(pcie, MACCGSPSETR, SPCNGRSN, 0); + + /* Clear SPCHGFIN, SPCHGSUC, and SPCHGFAIL */ + if (macsr & (SPCHGFIN | SPCHGSUC | SPCHGFAIL)) + rcar_pci_write_reg(pcie, macsr, MACSR); + + /* Start link speed change */ + rcar_rmw32(pcie, MACCTLR, SPEED_CHANGE, SPEED_CHANGE); + + while (timeout--) { + macsr = rcar_pci_read_reg(pcie, MACSR); + if (macsr & SPCHGFIN) { + /* Clear the interrupt bits */ + rcar_pci_write_reg(pcie, macsr, MACSR); + + if (macsr & SPCHGFAIL) + dev_err(dev, "Speed change failed\n"); + + goto done; + } + + msleep(1); + } + + dev_err(dev, "Speed change timed out\n"); + +done: + dev_info(dev, "Current link speed is %s GT/s\n", + (macsr & LINK_SPEED) == LINK_SPEED_5_0GTS ? "5" : "2.5"); +} + +static void rcar_pcie_hw_enable(struct rcar_pcie *pci) +{ + struct resource_entry *win; + LIST_HEAD(res); + int i = 0; + + /* Try setting 5 GT/s link speed */ + rcar_pcie_force_speedup(pci); + + /* Setup PCI resources */ + resource_list_for_each_entry(win, &pci->resources) { + struct resource *res = win->res; + + if (!res->flags) + continue; + + switch (resource_type(res)) { + case IORESOURCE_IO: + case IORESOURCE_MEM: + rcar_pcie_setup_window(i, pci, win); + i++; + break; + } + } +} + +static int rcar_pcie_enable(struct rcar_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + struct pci_bus *bus, *child; + int ret; + + /* Try setting 5 GT/s link speed */ + rcar_pcie_force_speedup(pcie); + + rcar_pcie_setup(&bridge->windows, pcie); + + pci_add_flags(PCI_REASSIGN_ALL_BUS); + + bridge->dev.parent = dev; + bridge->sysdata = pcie; + bridge->busnr = pcie->root_bus_nr; + bridge->ops = &rcar_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + if (IS_ENABLED(CONFIG_PCI_MSI)) + bridge->msi = &pcie->msi.chip; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret < 0) + return ret; + + bus = bridge->bus; + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(bus); + + return 0; +} + +static int phy_wait_for_ack(struct rcar_pcie *pcie) +{ + struct device *dev = pcie->dev; + unsigned int timeout = 100; + + while (timeout--) { + if (rcar_pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) + return 0; + + udelay(100); + } + + dev_err(dev, "Access to PCIe phy timed out\n"); + + return -ETIMEDOUT; +} + +static void phy_write_reg(struct rcar_pcie *pcie, + unsigned int rate, u32 addr, + unsigned int lane, u32 data) +{ + u32 phyaddr; + + phyaddr = WRITE_CMD | + ((rate & 1) << RATE_POS) | + ((lane & 0xf) << LANE_POS) | + ((addr & 0xff) << ADR_POS); + + /* Set write data */ + rcar_pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); + rcar_pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); + + /* Ignore errors as they will be dealt with if the data link is down */ + phy_wait_for_ack(pcie); + + /* Clear command */ + rcar_pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); + rcar_pci_write_reg(pcie, 0, H1_PCIEPHYADRR); + + /* Ignore errors as they will be dealt with if the data link is down */ + phy_wait_for_ack(pcie); +} + +static int rcar_pcie_wait_for_phyrdy(struct rcar_pcie *pcie) +{ + unsigned int timeout = 10; + + while (timeout--) { + if (rcar_pci_read_reg(pcie, PCIEPHYSR) & PHYRDY) + return 0; + + msleep(5); + } + + return -ETIMEDOUT; +} + +static int rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) +{ + unsigned int timeout = 10000; + + while (timeout--) { + if ((rcar_pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) + return 0; + + udelay(5); + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static int rcar_pcie_hw_init(struct rcar_pcie *pcie) +{ + int err; + + /* Begin initialization */ + rcar_pci_write_reg(pcie, 0, PCIETCTLR); + + /* Set mode */ + rcar_pci_write_reg(pcie, 1, PCIEMSR); + + err = rcar_pcie_wait_for_phyrdy(pcie); + if (err) + return err; + + /* + * Initial header for port config space is type 1, set the device + * class to match. Hardware takes care of propagating the IDSETR + * settings, so there is no need to bother with a quirk. + */ + rcar_pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); + + /* + * Setup Secondary Bus Number & Subordinate Bus Number, even though + * they aren't used, to avoid bridge being detected as broken. + */ + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); + + /* Initialize default capabilities. */ + rcar_rmw32(pcie, REXPCAP(0), 0xff, PCI_CAP_ID_EXP); + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, + PCI_HEADER_TYPE_BRIDGE); + + /* Enable data link layer active state reporting */ + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), PCI_EXP_LNKCAP_DLLLARC, + PCI_EXP_LNKCAP_DLLLARC); + + /* Write out the physical slot number = 0 */ + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); + + /* Set the completion timer timeout to the maximum 50ms. */ + rcar_rmw32(pcie, TLCTLR + 1, 0x3f, 50); + + /* Terminate list of capabilities (Next Capability Offset=0) */ + rcar_rmw32(pcie, RVCCAP(0), 0xfff00000, 0); + + /* Enable MSI */ + 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); + + /* This will timeout if we don't have a link. */ + err = rcar_pcie_wait_for_dl(pcie); + if (err) + return err; + + /* Enable INTx interrupts */ + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); + + wmb(); + + return 0; +} + +static int rcar_pcie_phy_init_h1(struct rcar_pcie *pcie) +{ + /* Initialize the phy */ + phy_write_reg(pcie, 0, 0x42, 0x1, 0x0EC34191); + phy_write_reg(pcie, 1, 0x42, 0x1, 0x0EC34180); + phy_write_reg(pcie, 0, 0x43, 0x1, 0x00210188); + phy_write_reg(pcie, 1, 0x43, 0x1, 0x00210188); + phy_write_reg(pcie, 0, 0x44, 0x1, 0x015C0014); + phy_write_reg(pcie, 1, 0x44, 0x1, 0x015C0014); + phy_write_reg(pcie, 1, 0x4C, 0x1, 0x786174A0); + phy_write_reg(pcie, 1, 0x4D, 0x1, 0x048000BB); + phy_write_reg(pcie, 0, 0x51, 0x1, 0x079EC062); + phy_write_reg(pcie, 0, 0x52, 0x1, 0x20000000); + phy_write_reg(pcie, 1, 0x52, 0x1, 0x20000000); + phy_write_reg(pcie, 1, 0x56, 0x1, 0x00003806); + + phy_write_reg(pcie, 0, 0x60, 0x1, 0x004B03A5); + phy_write_reg(pcie, 0, 0x64, 0x1, 0x3F0F1F0F); + phy_write_reg(pcie, 0, 0x66, 0x1, 0x00008000); + + return 0; +} + +static int rcar_pcie_phy_init_gen2(struct rcar_pcie *pcie) +{ + /* + * These settings come from the R-Car Series, 2nd Generation User's + * Manual, section 50.3.1 (2) Initialization of the physical layer. + */ + rcar_pci_write_reg(pcie, 0x000f0030, GEN2_PCIEPHYADDR); + rcar_pci_write_reg(pcie, 0x00381203, GEN2_PCIEPHYDATA); + rcar_pci_write_reg(pcie, 0x00000001, GEN2_PCIEPHYCTRL); + rcar_pci_write_reg(pcie, 0x00000006, GEN2_PCIEPHYCTRL); + + rcar_pci_write_reg(pcie, 0x000f0054, GEN2_PCIEPHYADDR); + /* The following value is for DC connection, no termination resistor */ + rcar_pci_write_reg(pcie, 0x13802007, GEN2_PCIEPHYDATA); + rcar_pci_write_reg(pcie, 0x00000001, GEN2_PCIEPHYCTRL); + rcar_pci_write_reg(pcie, 0x00000006, GEN2_PCIEPHYCTRL); + + return 0; +} + +static int rcar_pcie_phy_init_gen3(struct rcar_pcie *pcie) +{ + int err; + + err = phy_init(pcie->phy); + if (err) + return err; + + err = phy_power_on(pcie->phy); + if (err) + phy_exit(pcie->phy); + + return err; +} + +static int rcar_msi_alloc(struct rcar_msi *chip) +{ + int msi; + + mutex_lock(&chip->lock); + + msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR); + if (msi < INT_PCI_MSI_NR) + set_bit(msi, chip->used); + else + msi = -ENOSPC; + + mutex_unlock(&chip->lock); + + return msi; +} + +static int rcar_msi_alloc_region(struct rcar_msi *chip, int no_irqs) +{ + int msi; + + mutex_lock(&chip->lock); + msi = bitmap_find_free_region(chip->used, INT_PCI_MSI_NR, + order_base_2(no_irqs)); + mutex_unlock(&chip->lock); + + return msi; +} + +static void rcar_msi_free(struct rcar_msi *chip, unsigned long irq) +{ + mutex_lock(&chip->lock); + clear_bit(irq, chip->used); + mutex_unlock(&chip->lock); +} + +static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) +{ + struct rcar_pcie *pcie = data; + struct rcar_msi *msi = &pcie->msi; + struct device *dev = pcie->dev; + unsigned long reg; + + reg = rcar_pci_read_reg(pcie, PCIEMSIFR); + + /* MSI & INTx share an interrupt - we only handle MSI here */ + if (!reg) + return IRQ_NONE; + + while (reg) { + unsigned int index = find_first_bit(®, 32); + unsigned int msi_irq; + + /* clear the interrupt */ + rcar_pci_write_reg(pcie, 1 << index, PCIEMSIFR); + + msi_irq = irq_find_mapping(msi->domain, index); + if (msi_irq) { + if (test_bit(index, msi->used)) + generic_handle_irq(msi_irq); + else + dev_info(dev, "unhandled MSI\n"); + } else { + /* Unknown MSI, just clear it */ + dev_dbg(dev, "unexpected MSI\n"); + } + + /* see if there's any more pending in this vector */ + reg = rcar_pci_read_reg(pcie, PCIEMSIFR); + } + + return IRQ_HANDLED; +} + +static int rcar_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, + struct msi_desc *desc) +{ + struct rcar_msi *msi = to_rcar_msi(chip); + struct rcar_pcie *pcie = container_of(chip, struct rcar_pcie, msi.chip); + struct msi_msg msg; + unsigned int irq; + int hwirq; + + hwirq = rcar_msi_alloc(msi); + if (hwirq < 0) + return hwirq; + + irq = irq_find_mapping(msi->domain, hwirq); + if (!irq) { + rcar_msi_free(msi, hwirq); + return -EINVAL; + } + + irq_set_msi_desc(irq, desc); + + msg.address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE; + msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR); + msg.data = hwirq; + + pci_write_msi_msg(irq, &msg); + + return 0; +} + +static int rcar_msi_setup_irqs(struct msi_controller *chip, + struct pci_dev *pdev, int nvec, int type) +{ + struct rcar_pcie *pcie = container_of(chip, struct rcar_pcie, msi.chip); + struct rcar_msi *msi = to_rcar_msi(chip); + struct msi_desc *desc; + struct msi_msg msg; + unsigned int irq; + int hwirq; + int i; + + /* MSI-X interrupts are not supported */ + if (type == PCI_CAP_ID_MSIX) + return -EINVAL; + + WARN_ON(!list_is_singular(&pdev->dev.msi_list)); + desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list); + + hwirq = rcar_msi_alloc_region(msi, nvec); + if (hwirq < 0) + return -ENOSPC; + + irq = irq_find_mapping(msi->domain, hwirq); + if (!irq) + return -ENOSPC; + + for (i = 0; i < nvec; i++) { + /* + * irq_create_mapping() called from rcar_pcie_probe() pre- + * allocates descs, so there is no need to allocate descs here. + * We can therefore assume that if irq_find_mapping() above + * returns non-zero, then the descs are also successfully + * allocated. + */ + if (irq_set_msi_desc_off(irq, i, desc)) { + /* TODO: clear */ + return -EINVAL; + } + } + + desc->nvec_used = nvec; + desc->msi_attrib.multiple = order_base_2(nvec); + + msg.address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE; + msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR); + msg.data = hwirq; + + pci_write_msi_msg(irq, &msg); + + return 0; +} + +static void rcar_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) +{ + struct rcar_msi *msi = to_rcar_msi(chip); + struct irq_data *d = irq_get_irq_data(irq); + + rcar_msi_free(msi, d->hwirq); +} + +static struct irq_chip rcar_msi_irq_chip = { + .name = "R-Car PCIe MSI", + .irq_enable = pci_msi_unmask_irq, + .irq_disable = pci_msi_mask_irq, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &rcar_msi_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops msi_domain_ops = { + .map = rcar_msi_map, +}; + +static void rcar_pcie_unmap_msi(struct rcar_pcie *pcie) +{ + struct rcar_msi *msi = &pcie->msi; + int i, irq; + + for (i = 0; i < INT_PCI_MSI_NR; i++) { + irq = irq_find_mapping(msi->domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } + + irq_domain_remove(msi->domain); +} + +static void rcar_pcie_hw_enable_msi(struct rcar_pcie *pcie) +{ + struct rcar_msi *msi = &pcie->msi; + unsigned long base; + + /* setup MSI data target */ + base = virt_to_phys((void *)msi->pages); + + rcar_pci_write_reg(pcie, lower_32_bits(base) | MSIFE, PCIEMSIALR); + rcar_pci_write_reg(pcie, upper_32_bits(base), PCIEMSIAUR); + + /* enable all MSI interrupts */ + rcar_pci_write_reg(pcie, 0xffffffff, PCIEMSIIER); +} + +static int rcar_pcie_enable_msi(struct rcar_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct rcar_msi *msi = &pcie->msi; + int err, i; + + mutex_init(&msi->lock); + + msi->chip.dev = dev; + msi->chip.setup_irq = rcar_msi_setup_irq; + msi->chip.setup_irqs = rcar_msi_setup_irqs; + msi->chip.teardown_irq = rcar_msi_teardown_irq; + + msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR, + &msi_domain_ops, &msi->chip); + if (!msi->domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + for (i = 0; i < INT_PCI_MSI_NR; i++) + irq_create_mapping(msi->domain, i); + + /* Two irqs are for MSI, but they are also used for non-MSI irqs */ + err = devm_request_irq(dev, msi->irq1, rcar_pcie_msi_irq, + IRQF_SHARED | IRQF_NO_THREAD, + rcar_msi_irq_chip.name, pcie); + if (err < 0) { + dev_err(dev, "failed to request IRQ: %d\n", err); + goto err; + } + + err = devm_request_irq(dev, msi->irq2, rcar_pcie_msi_irq, + IRQF_SHARED | IRQF_NO_THREAD, + rcar_msi_irq_chip.name, pcie); + if (err < 0) { + dev_e