From 6e0832fa432ec99c94caee733c8f5851cf85560b Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 31 May 2018 09:12:37 +0800 Subject: PCI: Collect all native drivers under drivers/pci/controller/ Native PCI drivers for root complex devices were originally all in drivers/pci/host/. Some of these devices can also be operated in endpoint mode. Drivers for endpoint mode didn't seem to fit in the "host" directory, so we put both the root complex and endpoint drivers in per-device directories, e.g., drivers/pci/dwc/, drivers/pci/cadence/, etc. These per-device directories contain trivial Kconfig and Makefiles and clutter drivers/pci/. Make a new drivers/pci/controllers/ directory and collect all the device-specific drivers there. No functional change intended. Link: https://lkml.kernel.org/r/1520304202-232891-1-git-send-email-shawn.lin@rock-chips.com Signed-off-by: Shawn Lin [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas --- drivers/pci/Kconfig | 4 +- drivers/pci/Makefile | 6 +- drivers/pci/cadence/Kconfig | 27 - drivers/pci/cadence/Makefile | 4 - drivers/pci/cadence/pcie-cadence-ep.c | 549 ----- drivers/pci/cadence/pcie-cadence-host.c | 336 --- drivers/pci/cadence/pcie-cadence.c | 126 - drivers/pci/cadence/pcie-cadence.h | 311 --- drivers/pci/controller/Kconfig | 275 +++ drivers/pci/controller/Makefile | 49 + drivers/pci/controller/dwc/Kconfig | 197 ++ drivers/pci/controller/dwc/Makefile | 30 + drivers/pci/controller/dwc/pci-dra7xx.c | 846 +++++++ drivers/pci/controller/dwc/pci-exynos.c | 539 +++++ drivers/pci/controller/dwc/pci-imx6.c | 871 +++++++ drivers/pci/controller/dwc/pci-keystone-dw.c | 484 ++++ drivers/pci/controller/dwc/pci-keystone.c | 457 ++++ drivers/pci/controller/dwc/pci-keystone.h | 57 + drivers/pci/controller/dwc/pci-layerscape.c | 341 +++ drivers/pci/controller/dwc/pcie-armada8k.c | 282 +++ drivers/pci/controller/dwc/pcie-artpec6.c | 618 +++++ drivers/pci/controller/dwc/pcie-designware-ep.c | 422 ++++ drivers/pci/controller/dwc/pcie-designware-host.c | 722 ++++++ drivers/pci/controller/dwc/pcie-designware-plat.c | 259 ++ drivers/pci/controller/dwc/pcie-designware.c | 394 +++ drivers/pci/controller/dwc/pcie-designware.h | 387 +++ drivers/pci/controller/dwc/pcie-hisi.c | 398 +++ drivers/pci/controller/dwc/pcie-histb.c | 472 ++++ drivers/pci/controller/dwc/pcie-kirin.c | 515 ++++ drivers/pci/controller/dwc/pcie-qcom.c | 1299 ++++++++++ drivers/pci/controller/dwc/pcie-spear13xx.c | 314 +++ drivers/pci/controller/pci-aardvark.c | 978 ++++++++ drivers/pci/controller/pci-ftpci100.c | 619 +++++ drivers/pci/controller/pci-host-common.c | 118 + drivers/pci/controller/pci-host-generic.c | 100 + drivers/pci/controller/pci-hyperv.c | 2694 +++++++++++++++++++++ drivers/pci/controller/pci-mvebu.c | 1313 ++++++++++ drivers/pci/controller/pci-rcar-gen2.c | 428 ++++ drivers/pci/controller/pci-tegra.c | 2531 +++++++++++++++++++ drivers/pci/controller/pci-thunder-ecam.c | 380 +++ drivers/pci/controller/pci-thunder-pem.c | 473 ++++ drivers/pci/controller/pci-v3-semi.c | 963 ++++++++ drivers/pci/controller/pci-versatile.c | 239 ++ drivers/pci/controller/pci-xgene-msi.c | 543 +++++ drivers/pci/controller/pci-xgene.c | 689 ++++++ drivers/pci/controller/pcie-altera-msi.c | 291 +++ drivers/pci/controller/pcie-altera.c | 645 +++++ drivers/pci/controller/pcie-cadence-ep.c | 549 +++++ drivers/pci/controller/pcie-cadence-host.c | 336 +++ drivers/pci/controller/pcie-cadence.c | 126 + drivers/pci/controller/pcie-cadence.h | 311 +++ drivers/pci/controller/pcie-iproc-bcma.c | 112 + drivers/pci/controller/pcie-iproc-msi.c | 671 +++++ drivers/pci/controller/pcie-iproc-platform.c | 157 ++ drivers/pci/controller/pcie-iproc.c | 1432 +++++++++++ drivers/pci/controller/pcie-iproc.h | 119 + drivers/pci/controller/pcie-mediatek.c | 1218 ++++++++++ drivers/pci/controller/pcie-mobiveil.c | 866 +++++++ drivers/pci/controller/pcie-rcar.c | 1222 ++++++++++ drivers/pci/controller/pcie-rockchip-ep.c | 642 +++++ drivers/pci/controller/pcie-rockchip-host.c | 1142 +++++++++ drivers/pci/controller/pcie-rockchip.c | 424 ++++ drivers/pci/controller/pcie-rockchip.h | 338 +++ drivers/pci/controller/pcie-tango.c | 341 +++ drivers/pci/controller/pcie-xilinx-nwl.c | 917 +++++++ drivers/pci/controller/pcie-xilinx.c | 702 ++++++ drivers/pci/controller/vmd.c | 870 +++++++ drivers/pci/dwc/Kconfig | 197 -- drivers/pci/dwc/Makefile | 30 - drivers/pci/dwc/pci-dra7xx.c | 846 ------- drivers/pci/dwc/pci-exynos.c | 539 ----- drivers/pci/dwc/pci-imx6.c | 871 ------- drivers/pci/dwc/pci-keystone-dw.c | 484 ---- drivers/pci/dwc/pci-keystone.c | 457 ---- drivers/pci/dwc/pci-keystone.h | 57 - drivers/pci/dwc/pci-layerscape.c | 341 --- drivers/pci/dwc/pcie-armada8k.c | 282 --- drivers/pci/dwc/pcie-artpec6.c | 618 ----- drivers/pci/dwc/pcie-designware-ep.c | 422 ---- drivers/pci/dwc/pcie-designware-host.c | 722 ------ drivers/pci/dwc/pcie-designware-plat.c | 259 -- drivers/pci/dwc/pcie-designware.c | 394 --- drivers/pci/dwc/pcie-designware.h | 387 --- drivers/pci/dwc/pcie-hisi.c | 398 --- drivers/pci/dwc/pcie-histb.c | 472 ---- drivers/pci/dwc/pcie-kirin.c | 515 ---- drivers/pci/dwc/pcie-qcom.c | 1299 ---------- drivers/pci/dwc/pcie-spear13xx.c | 314 --- drivers/pci/host/Kconfig | 246 -- drivers/pci/host/Makefile | 43 - drivers/pci/host/pci-aardvark.c | 978 -------- drivers/pci/host/pci-ftpci100.c | 619 ----- drivers/pci/host/pci-host-common.c | 118 - drivers/pci/host/pci-host-generic.c | 100 - drivers/pci/host/pci-hyperv.c | 2694 --------------------- drivers/pci/host/pci-mvebu.c | 1313 ---------- drivers/pci/host/pci-rcar-gen2.c | 428 ---- drivers/pci/host/pci-tegra.c | 2531 ------------------- drivers/pci/host/pci-thunder-ecam.c | 380 --- drivers/pci/host/pci-thunder-pem.c | 473 ---- drivers/pci/host/pci-v3-semi.c | 963 -------- drivers/pci/host/pci-versatile.c | 239 -- drivers/pci/host/pci-xgene-msi.c | 543 ----- drivers/pci/host/pci-xgene.c | 689 ------ drivers/pci/host/pcie-altera-msi.c | 291 --- drivers/pci/host/pcie-altera.c | 645 ----- drivers/pci/host/pcie-iproc-bcma.c | 112 - drivers/pci/host/pcie-iproc-msi.c | 671 ----- drivers/pci/host/pcie-iproc-platform.c | 157 -- drivers/pci/host/pcie-iproc.c | 1432 ----------- drivers/pci/host/pcie-iproc.h | 119 - drivers/pci/host/pcie-mediatek.c | 1218 ---------- drivers/pci/host/pcie-mobiveil.c | 866 ------- drivers/pci/host/pcie-rcar.c | 1222 ---------- drivers/pci/host/pcie-rockchip-ep.c | 642 ----- drivers/pci/host/pcie-rockchip-host.c | 1142 --------- drivers/pci/host/pcie-rockchip.c | 424 ---- drivers/pci/host/pcie-rockchip.h | 338 --- drivers/pci/host/pcie-tango.c | 341 --- drivers/pci/host/pcie-xilinx-nwl.c | 917 ------- drivers/pci/host/pcie-xilinx.c | 702 ------ drivers/pci/host/vmd.c | 870 ------- 122 files changed, 35729 insertions(+), 35731 deletions(-) delete mode 100644 drivers/pci/cadence/Kconfig delete mode 100644 drivers/pci/cadence/Makefile delete mode 100644 drivers/pci/cadence/pcie-cadence-ep.c delete mode 100644 drivers/pci/cadence/pcie-cadence-host.c delete mode 100644 drivers/pci/cadence/pcie-cadence.c delete mode 100644 drivers/pci/cadence/pcie-cadence.h create mode 100644 drivers/pci/controller/Kconfig create mode 100644 drivers/pci/controller/Makefile create mode 100644 drivers/pci/controller/dwc/Kconfig create mode 100644 drivers/pci/controller/dwc/Makefile create mode 100644 drivers/pci/controller/dwc/pci-dra7xx.c create mode 100644 drivers/pci/controller/dwc/pci-exynos.c create mode 100644 drivers/pci/controller/dwc/pci-imx6.c create mode 100644 drivers/pci/controller/dwc/pci-keystone-dw.c create mode 100644 drivers/pci/controller/dwc/pci-keystone.c create mode 100644 drivers/pci/controller/dwc/pci-keystone.h create mode 100644 drivers/pci/controller/dwc/pci-layerscape.c create mode 100644 drivers/pci/controller/dwc/pcie-armada8k.c create mode 100644 drivers/pci/controller/dwc/pcie-artpec6.c create mode 100644 drivers/pci/controller/dwc/pcie-designware-ep.c create mode 100644 drivers/pci/controller/dwc/pcie-designware-host.c create mode 100644 drivers/pci/controller/dwc/pcie-designware-plat.c create mode 100644 drivers/pci/controller/dwc/pcie-designware.c create mode 100644 drivers/pci/controller/dwc/pcie-designware.h create mode 100644 drivers/pci/controller/dwc/pcie-hisi.c create mode 100644 drivers/pci/controller/dwc/pcie-histb.c create mode 100644 drivers/pci/controller/dwc/pcie-kirin.c create mode 100644 drivers/pci/controller/dwc/pcie-qcom.c create mode 100644 drivers/pci/controller/dwc/pcie-spear13xx.c create mode 100644 drivers/pci/controller/pci-aardvark.c create mode 100644 drivers/pci/controller/pci-ftpci100.c create mode 100644 drivers/pci/controller/pci-host-common.c create mode 100644 drivers/pci/controller/pci-host-generic.c create mode 100644 drivers/pci/controller/pci-hyperv.c create mode 100644 drivers/pci/controller/pci-mvebu.c create mode 100644 drivers/pci/controller/pci-rcar-gen2.c create mode 100644 drivers/pci/controller/pci-tegra.c create mode 100644 drivers/pci/controller/pci-thunder-ecam.c create mode 100644 drivers/pci/controller/pci-thunder-pem.c create mode 100644 drivers/pci/controller/pci-v3-semi.c create mode 100644 drivers/pci/controller/pci-versatile.c create mode 100644 drivers/pci/controller/pci-xgene-msi.c create mode 100644 drivers/pci/controller/pci-xgene.c create mode 100644 drivers/pci/controller/pcie-altera-msi.c create mode 100644 drivers/pci/controller/pcie-altera.c create mode 100644 drivers/pci/controller/pcie-cadence-ep.c create mode 100644 drivers/pci/controller/pcie-cadence-host.c create mode 100644 drivers/pci/controller/pcie-cadence.c create mode 100644 drivers/pci/controller/pcie-cadence.h create mode 100644 drivers/pci/controller/pcie-iproc-bcma.c create mode 100644 drivers/pci/controller/pcie-iproc-msi.c create mode 100644 drivers/pci/controller/pcie-iproc-platform.c create mode 100644 drivers/pci/controller/pcie-iproc.c create mode 100644 drivers/pci/controller/pcie-iproc.h create mode 100644 drivers/pci/controller/pcie-mediatek.c create mode 100644 drivers/pci/controller/pcie-mobiveil.c create mode 100644 drivers/pci/controller/pcie-rcar.c create mode 100644 drivers/pci/controller/pcie-rockchip-ep.c create mode 100644 drivers/pci/controller/pcie-rockchip-host.c create mode 100644 drivers/pci/controller/pcie-rockchip.c create mode 100644 drivers/pci/controller/pcie-rockchip.h create mode 100644 drivers/pci/controller/pcie-tango.c create mode 100644 drivers/pci/controller/pcie-xilinx-nwl.c create mode 100644 drivers/pci/controller/pcie-xilinx.c create mode 100644 drivers/pci/controller/vmd.c delete mode 100644 drivers/pci/dwc/Kconfig delete mode 100644 drivers/pci/dwc/Makefile delete mode 100644 drivers/pci/dwc/pci-dra7xx.c delete mode 100644 drivers/pci/dwc/pci-exynos.c delete mode 100644 drivers/pci/dwc/pci-imx6.c delete mode 100644 drivers/pci/dwc/pci-keystone-dw.c delete mode 100644 drivers/pci/dwc/pci-keystone.c delete mode 100644 drivers/pci/dwc/pci-keystone.h delete mode 100644 drivers/pci/dwc/pci-layerscape.c delete mode 100644 drivers/pci/dwc/pcie-armada8k.c delete mode 100644 drivers/pci/dwc/pcie-artpec6.c delete mode 100644 drivers/pci/dwc/pcie-designware-ep.c delete mode 100644 drivers/pci/dwc/pcie-designware-host.c delete mode 100644 drivers/pci/dwc/pcie-designware-plat.c delete mode 100644 drivers/pci/dwc/pcie-designware.c delete mode 100644 drivers/pci/dwc/pcie-designware.h delete mode 100644 drivers/pci/dwc/pcie-hisi.c delete mode 100644 drivers/pci/dwc/pcie-histb.c delete mode 100644 drivers/pci/dwc/pcie-kirin.c delete mode 100644 drivers/pci/dwc/pcie-qcom.c delete mode 100644 drivers/pci/dwc/pcie-spear13xx.c delete mode 100644 drivers/pci/host/Kconfig delete mode 100644 drivers/pci/host/Makefile delete mode 100644 drivers/pci/host/pci-aardvark.c delete mode 100644 drivers/pci/host/pci-ftpci100.c delete mode 100644 drivers/pci/host/pci-host-common.c delete mode 100644 drivers/pci/host/pci-host-generic.c delete mode 100644 drivers/pci/host/pci-hyperv.c delete mode 100644 drivers/pci/host/pci-mvebu.c delete mode 100644 drivers/pci/host/pci-rcar-gen2.c delete mode 100644 drivers/pci/host/pci-tegra.c delete mode 100644 drivers/pci/host/pci-thunder-ecam.c delete mode 100644 drivers/pci/host/pci-thunder-pem.c delete mode 100644 drivers/pci/host/pci-v3-semi.c delete mode 100644 drivers/pci/host/pci-versatile.c delete mode 100644 drivers/pci/host/pci-xgene-msi.c delete mode 100644 drivers/pci/host/pci-xgene.c delete mode 100644 drivers/pci/host/pcie-altera-msi.c delete mode 100644 drivers/pci/host/pcie-altera.c delete mode 100644 drivers/pci/host/pcie-iproc-bcma.c delete mode 100644 drivers/pci/host/pcie-iproc-msi.c delete mode 100644 drivers/pci/host/pcie-iproc-platform.c delete mode 100644 drivers/pci/host/pcie-iproc.c delete mode 100644 drivers/pci/host/pcie-iproc.h delete mode 100644 drivers/pci/host/pcie-mediatek.c delete mode 100644 drivers/pci/host/pcie-mobiveil.c delete mode 100644 drivers/pci/host/pcie-rcar.c delete mode 100644 drivers/pci/host/pcie-rockchip-ep.c delete mode 100644 drivers/pci/host/pcie-rockchip-host.c delete mode 100644 drivers/pci/host/pcie-rockchip.c delete mode 100644 drivers/pci/host/pcie-rockchip.h delete mode 100644 drivers/pci/host/pcie-tango.c delete mode 100644 drivers/pci/host/pcie-xilinx-nwl.c delete mode 100644 drivers/pci/host/pcie-xilinx.c delete mode 100644 drivers/pci/host/vmd.c (limited to 'drivers/pci') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index b2f07635e94d..56ff8f6d31fc 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -145,8 +145,6 @@ config PCI_HYPERV PCI devices from a PCI backend to support PCI driver domains. source "drivers/pci/hotplug/Kconfig" -source "drivers/pci/cadence/Kconfig" -source "drivers/pci/dwc/Kconfig" -source "drivers/pci/host/Kconfig" +source "drivers/pci/controller/Kconfig" source "drivers/pci/endpoint/Kconfig" source "drivers/pci/switch/Kconfig" diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 84c9eef6b1c3..535201984b8b 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -28,14 +28,10 @@ obj-$(CONFIG_PCI_PF_STUB) += pci-pf-stub.o obj-$(CONFIG_PCI_ECAM) += ecam.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o -obj-y += host/ +obj-y += controller/ obj-y += switch/ # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ -obj-$(CONFIG_PCIE_CADENCE) += cadence/ -# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW -obj-y += dwc/ - ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG diff --git a/drivers/pci/cadence/Kconfig b/drivers/pci/cadence/Kconfig deleted file mode 100644 index e6824cb56c16..000000000000 --- a/drivers/pci/cadence/Kconfig +++ /dev/null @@ -1,27 +0,0 @@ -menu "Cadence PCIe controllers support" - -config PCIE_CADENCE - bool - -config PCIE_CADENCE_HOST - bool "Cadence PCIe host controller" - depends on OF - depends on PCI - select IRQ_DOMAIN - select PCIE_CADENCE - help - Say Y here if you want to support the Cadence PCIe controller in host - mode. This PCIe controller may be embedded into many different vendors - SoCs. - -config PCIE_CADENCE_EP - bool "Cadence PCIe endpoint controller" - depends on OF - depends on PCI_ENDPOINT - select PCIE_CADENCE - help - Say Y here if you want to support the Cadence PCIe controller in - endpoint mode. This PCIe controller may be embedded into many - different vendors SoCs. - -endmenu diff --git a/drivers/pci/cadence/Makefile b/drivers/pci/cadence/Makefile deleted file mode 100644 index 719392b97998..000000000000 --- a/drivers/pci/cadence/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o -obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o -obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c deleted file mode 100644 index 3d8283e450a9..000000000000 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ /dev/null @@ -1,549 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2017 Cadence -// Cadence PCIe endpoint controller driver. -// Author: Cyrille Pitchen - -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-cadence.h" - -#define CDNS_PCIE_EP_MIN_APERTURE 128 /* 128 bytes */ -#define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE 0x1 -#define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x3 - -/** - * struct cdns_pcie_ep - private data for this PCIe endpoint controller driver - * @pcie: Cadence PCIe controller - * @max_regions: maximum number of regions supported by hardware - * @ob_region_map: bitmask of mapped outbound regions - * @ob_addr: base addresses in the AXI bus where the outbound regions start - * @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ - * dedicated outbound regions is mapped. - * @irq_cpu_addr: base address in the CPU space where a write access triggers - * the sending of a memory write (MSI) / normal message (legacy - * IRQ) TLP through the PCIe bus. - * @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ - * dedicated outbound region. - * @irq_pci_fn: the latest PCI function that has updated the mapping of - * the MSI/legacy IRQ dedicated outbound region. - * @irq_pending: bitmask of asserted legacy IRQs. - */ -struct cdns_pcie_ep { - struct cdns_pcie pcie; - u32 max_regions; - unsigned long ob_region_map; - phys_addr_t *ob_addr; - phys_addr_t irq_phys_addr; - void __iomem *irq_cpu_addr; - u64 irq_pci_addr; - u8 irq_pci_fn; - u8 irq_pending; -}; - -static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, - struct pci_epf_header *hdr) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - struct cdns_pcie *pcie = &ep->pcie; - - cdns_pcie_ep_fn_writew(pcie, fn, PCI_DEVICE_ID, hdr->deviceid); - cdns_pcie_ep_fn_writeb(pcie, fn, PCI_REVISION_ID, hdr->revid); - cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CLASS_PROG, hdr->progif_code); - cdns_pcie_ep_fn_writew(pcie, fn, PCI_CLASS_DEVICE, - hdr->subclass_code | hdr->baseclass_code << 8); - cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CACHE_LINE_SIZE, - hdr->cache_line_size); - cdns_pcie_ep_fn_writew(pcie, fn, PCI_SUBSYSTEM_ID, hdr->subsys_id); - cdns_pcie_ep_fn_writeb(pcie, fn, PCI_INTERRUPT_PIN, hdr->interrupt_pin); - - /* - * Vendor ID can only be modified from function 0, all other functions - * use the same vendor ID as function 0. - */ - if (fn == 0) { - /* Update the vendor IDs. */ - u32 id = CDNS_PCIE_LM_ID_VENDOR(hdr->vendorid) | - CDNS_PCIE_LM_ID_SUBSYS(hdr->subsys_vendor_id); - - cdns_pcie_writel(pcie, CDNS_PCIE_LM_ID, id); - } - - return 0; -} - -static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, - struct pci_epf_bar *epf_bar) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - struct cdns_pcie *pcie = &ep->pcie; - dma_addr_t bar_phys = epf_bar->phys_addr; - enum pci_barno bar = epf_bar->barno; - int flags = epf_bar->flags; - u32 addr0, addr1, reg, cfg, b, aperture, ctrl; - u64 sz; - - /* BAR size is 2^(aperture + 7) */ - sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE); - /* - * roundup_pow_of_two() returns an unsigned long, which is not suited - * for 64bit values. - */ - sz = 1ULL << fls64(sz - 1); - aperture = ilog2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */ - - if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { - ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS; - } else { - bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH); - bool is_64bits = sz > SZ_2G; - - if (is_64bits && (bar & 1)) - return -EINVAL; - - if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) - epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; - - if (is_64bits && is_prefetch) - ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; - else if (is_prefetch) - ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS; - else if (is_64bits) - ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS; - else - ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS; - } - - addr0 = lower_32_bits(bar_phys); - addr1 = upper_32_bits(bar_phys); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), - addr0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), - addr1); - - if (bar < BAR_4) { - reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); - b = bar; - } else { - reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); - b = bar - BAR_4; - } - - cfg = cdns_pcie_readl(pcie, reg); - cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | - CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); - cfg |= (CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) | - CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl)); - cdns_pcie_writel(pcie, reg, cfg); - - return 0; -} - -static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, - struct pci_epf_bar *epf_bar) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - struct cdns_pcie *pcie = &ep->pcie; - enum pci_barno bar = epf_bar->barno; - u32 reg, cfg, b, ctrl; - - if (bar < BAR_4) { - reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); - b = bar; - } else { - reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); - b = bar - BAR_4; - } - - ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED; - cfg = cdns_pcie_readl(pcie, reg); - cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | - CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); - cfg |= CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl); - cdns_pcie_writel(pcie, reg, cfg); - - cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0); -} - -static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr, - u64 pci_addr, size_t size) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - struct cdns_pcie *pcie = &ep->pcie; - u32 r; - - r = find_first_zero_bit(&ep->ob_region_map, - sizeof(ep->ob_region_map) * BITS_PER_LONG); - if (r >= ep->max_regions - 1) { - dev_err(&epc->dev, "no free outbound region\n"); - return -EINVAL; - } - - cdns_pcie_set_outbound_region(pcie, fn, r, false, addr, pci_addr, size); - - set_bit(r, &ep->ob_region_map); - ep->ob_addr[r] = addr; - - return 0; -} - -static void cdns_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, - phys_addr_t addr) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - struct cdns_pcie *pcie = &ep->pcie; - u32 r; - - for (r = 0; r < ep->max_regions - 1; r++) - if (ep->ob_addr[r] == addr) - break; - - if (r == ep->max_regions - 1) - return; - - cdns_pcie_reset_outbound_region(pcie, r); - - ep->ob_addr[r] = 0; - clear_bit(r, &ep->ob_region_map); -} - -static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 mmc) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - struct cdns_pcie *pcie = &ep->pcie; - u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; - u16 flags; - - /* - * Set the Multiple Message Capable bitfield into the Message Control - * register. - */ - flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); - flags = (flags & ~PCI_MSI_FLAGS_QMASK) | (mmc << 1); - flags |= PCI_MSI_FLAGS_64BIT; - flags &= ~PCI_MSI_FLAGS_MASKBIT; - cdns_pcie_ep_fn_writew(pcie, fn, cap + PCI_MSI_FLAGS, flags); - - return 0; -} - -static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - struct cdns_pcie *pcie = &ep->pcie; - u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; - u16 flags, mmc, mme; - - /* Validate that the MSI feature is actually enabled. */ - flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); - if (!(flags & PCI_MSI_FLAGS_ENABLE)) - return -EINVAL; - - /* - * Get the Multiple Message Enable bitfield from the Message Control - * register. - */ - mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1; - mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; - - return mme; -} - -static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, - u8 intx, bool is_asserted) -{ - struct cdns_pcie *pcie = &ep->pcie; - u32 r = ep->max_regions - 1; - u32 offset; - u16 status; - u8 msg_code; - - intx &= 3; - - /* Set the outbound region if needed. */ - if (unlikely(ep->irq_pci_addr != CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY || - ep->irq_pci_fn != fn)) { - /* Last region was reserved for IRQ writes. */ - cdns_pcie_set_outbound_region_for_normal_msg(pcie, fn, r, - ep->irq_phys_addr); - ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY; - ep->irq_pci_fn = fn; - } - - if (is_asserted) { - ep->irq_pending |= BIT(intx); - msg_code = MSG_CODE_ASSERT_INTA + intx; - } else { - ep->irq_pending &= ~BIT(intx); - msg_code = MSG_CODE_DEASSERT_INTA + intx; - } - - status = cdns_pcie_ep_fn_readw(pcie, fn, PCI_STATUS); - if (((status & PCI_STATUS_INTERRUPT) != 0) ^ (ep->irq_pending != 0)) { - status ^= PCI_STATUS_INTERRUPT; - cdns_pcie_ep_fn_writew(pcie, fn, PCI_STATUS, status); - } - - offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) | - CDNS_PCIE_NORMAL_MSG_CODE(msg_code) | - CDNS_PCIE_MSG_NO_DATA; - writel(0, ep->irq_cpu_addr + offset); -} - -static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 intx) -{ - u16 cmd; - - cmd = cdns_pcie_ep_fn_readw(&ep->pcie, fn, PCI_COMMAND); - if (cmd & PCI_COMMAND_INTX_DISABLE) - return -EINVAL; - - cdns_pcie_ep_assert_intx(ep, fn, intx, true); - /* - * The mdelay() value was taken from dra7xx_pcie_raise_legacy_irq() - * from drivers/pci/dwc/pci-dra7xx.c - */ - mdelay(1); - cdns_pcie_ep_assert_intx(ep, fn, intx, false); - return 0; -} - -static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, - u8 interrupt_num) -{ - struct cdns_pcie *pcie = &ep->pcie; - u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; - u16 flags, mme, data, data_mask; - u8 msi_count; - u64 pci_addr, pci_addr_mask = 0xff; - - /* Check whether the MSI feature has been enabled by the PCI host. */ - flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); - if (!(flags & PCI_MSI_FLAGS_ENABLE)) - return -EINVAL; - - /* Get the number of enabled MSIs */ - mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; - msi_count = 1 << mme; - if (!interrupt_num || interrupt_num > msi_count) - return -EINVAL; - - /* Compute the data value to be written. */ - data_mask = msi_count - 1; - data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64); - data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask); - - /* Get the PCI address where to write the data into. */ - pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI); - pci_addr <<= 32; - pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO); - pci_addr &= GENMASK_ULL(63, 2); - - /* Set the outbound region if needed. */ - if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) || - ep->irq_pci_fn != fn)) { - /* Last region was reserved for IRQ writes. */ - cdns_pcie_set_outbound_region(pcie, fn, ep->max_regions - 1, - false, - ep->irq_phys_addr, - pci_addr & ~pci_addr_mask, - pci_addr_mask + 1); - ep->irq_pci_addr = (pci_addr & ~pci_addr_mask); - ep->irq_pci_fn = fn; - } - writew(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask)); - - return 0; -} - -static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, - enum pci_epc_irq_type type, u8 interrupt_num) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - - switch (type) { - case PCI_EPC_IRQ_LEGACY: - return cdns_pcie_ep_send_legacy_irq(ep, fn, 0); - - case PCI_EPC_IRQ_MSI: - return cdns_pcie_ep_send_msi_irq(ep, fn, interrupt_num); - - default: - break; - } - - return -EINVAL; -} - -static int cdns_pcie_ep_start(struct pci_epc *epc) -{ - struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - struct cdns_pcie *pcie = &ep->pcie; - struct pci_epf *epf; - u32 cfg; - - /* - * BIT(0) is hardwired to 1, hence function 0 is always enabled - * and can't be disabled anyway. - */ - cfg = BIT(0); - list_for_each_entry(epf, &epc->pci_epf, list) - cfg |= BIT(epf->func_no); - cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg); - - /* - * The PCIe links are automatically established by the controller - * once for all at powerup: the software can neither start nor stop - * those links later at runtime. - * - * Then we only have to notify the EP core that our links are already - * established. However we don't call directly pci_epc_linkup() because - * we've already locked the epc->lock. - */ - list_for_each_entry(epf, &epc->pci_epf, list) - pci_epf_linkup(epf); - - return 0; -} - -static const struct pci_epc_ops cdns_pcie_epc_ops = { - .write_header = cdns_pcie_ep_write_header, - .set_bar = cdns_pcie_ep_set_bar, - .clear_bar = cdns_pcie_ep_clear_bar, - .map_addr = cdns_pcie_ep_map_addr, - .unmap_addr = cdns_pcie_ep_unmap_addr, - .set_msi = cdns_pcie_ep_set_msi, - .get_msi = cdns_pcie_ep_get_msi, - .raise_irq = cdns_pcie_ep_raise_irq, - .start = cdns_pcie_ep_start, -}; - -static const struct of_device_id cdns_pcie_ep_of_match[] = { - { .compatible = "cdns,cdns-pcie-ep" }, - - { }, -}; - -static int cdns_pcie_ep_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct cdns_pcie_ep *ep; - struct cdns_pcie *pcie; - struct pci_epc *epc; - struct resource *res; - int ret; - - ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); - if (!ep) - return -ENOMEM; - - pcie = &ep->pcie; - pcie->is_rc = false; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); - pcie->reg_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->reg_base)) { - dev_err(dev, "missing \"reg\"\n"); - return PTR_ERR(pcie->reg_base); - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); - if (!res) { - dev_err(dev, "missing \"mem\"\n"); - return -EINVAL; - } - pcie->mem_res = res; - - ret = of_property_read_u32(np, "cdns,max-outbound-regions", - &ep->max_regions); - if (ret < 0) { - dev_err(dev, "missing \"cdns,max-outbound-regions\"\n"); - return ret; - } - ep->ob_addr = devm_kzalloc(dev, ep->max_regions * sizeof(*ep->ob_addr), - GFP_KERNEL); - if (!ep->ob_addr) - return -ENOMEM; - - pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - dev_err(dev, "pm_runtime_get_sync() failed\n"); - goto err_get_sync; - } - - /* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */ - cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0)); - - epc = devm_pci_epc_create(dev, &cdns_pcie_epc_ops); - if (IS_ERR(epc)) { - dev_err(dev, "failed to create epc device\n"); - ret = PTR_ERR(epc); - goto err_init; - } - - epc_set_drvdata(epc, ep); - - if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0) - epc->max_functions = 1; - - ret = pci_epc_mem_init(epc, pcie->mem_res->start, - resource_size(pcie->mem_res)); - if (ret < 0) { - dev_err(dev, "failed to initialize the memory space\n"); - goto err_init; - } - - ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr, - SZ_128K); - if (!ep->irq_cpu_addr) { - dev_err(dev, "failed to reserve memory space for MSI\n"); - ret = -ENOMEM; - goto free_epc_mem; - } - ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE; - - return 0; - - free_epc_mem: - pci_epc_mem_exit(epc); - - err_init: - pm_runtime_put_sync(dev); - - err_get_sync: - pm_runtime_disable(dev); - - return ret; -} - -static void cdns_pcie_ep_shutdown(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - int ret; - - ret = pm_runtime_put_sync(dev); - if (ret < 0) - dev_dbg(dev, "pm_runtime_put_sync failed\n"); - - pm_runtime_disable(dev); - - /* The PCIe controller can't be disabled. */ -} - -static struct platform_driver cdns_pcie_ep_driver = { - .driver = { - .name = "cdns-pcie-ep", - .of_match_table = cdns_pcie_ep_of_match, - }, - .probe = cdns_pcie_ep_probe, - .shutdown = cdns_pcie_ep_shutdown, -}; -builtin_platform_driver(cdns_pcie_ep_driver); diff --git a/drivers/pci/cadence/pcie-cadence-host.c b/drivers/pci/cadence/pcie-cadence-host.c deleted file mode 100644 index a4ebbd37b553..000000000000 --- a/drivers/pci/cadence/pcie-cadence-host.c +++ /dev/null @@ -1,336 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2017 Cadence -// Cadence PCIe host controller driver. -// Author: Cyrille Pitchen - -#include -#include -#include -#include -#include - -#include "pcie-cadence.h" - -/** - * struct cdns_pcie_rc - private data for this PCIe Root Complex driver - * @pcie: Cadence PCIe controller - * @dev: pointer to PCIe device - * @cfg_res: start/end offsets in the physical system memory to map PCI - * configuration space accesses - * @bus_range: first/last buses behind the PCIe host controller - * @cfg_base: IO mapped window to access the PCI configuration space of a - * single function at a time - * @max_regions: maximum number of regions supported by the hardware - * @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address - * translation (nbits sets into the "no BAR match" register) - * @vendor_id: PCI vendor ID - * @device_id: PCI device ID - */ -struct cdns_pcie_rc { - struct cdns_pcie pcie; - struct device *dev; - struct resource *cfg_res; - struct resource *bus_range; - void __iomem *cfg_base; - u32 max_regions; - u32 no_bar_nbits; - u16 vendor_id; - u16 device_id; -}; - -static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, - int where) -{ - struct pci_host_bridge *bridge = pci_find_host_bridge(bus); - struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge); - struct cdns_pcie *pcie = &rc->pcie; - unsigned int busn = bus->number; - u32 addr0, desc0; - - if (busn == rc->bus_range->start) { - /* - * Only the root port (devfn == 0) is connected to this bus. - * All other PCI devices are behind some bridge hence on another - * bus. - */ - if (devfn) - return NULL; - - return pcie->reg_base + (where & 0xfff); - } - - /* Update Output registers for AXI region 0. */ - addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | - CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) | - CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(busn); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(0), addr0); - - /* Configuration Type 0 or Type 1 access. */ - desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | - CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); - /* - * The bus number was already set once for all in desc1 by - * cdns_pcie_host_init_address_translation(). - */ - if (busn == rc->bus_range->start + 1) - desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0; - else - desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1; - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(0), desc0); - - return rc->cfg_base + (where & 0xfff); -} - -static struct pci_ops cdns_pcie_host_ops = { - .map_bus = cdns_pci_map_bus, - .read = pci_generic_config_read, - .write = pci_generic_config_write, -}; - -static const struct of_device_id cdns_pcie_host_of_match[] = { - { .compatible = "cdns,cdns-pcie-host" }, - - { }, -}; - -static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) -{ - struct cdns_pcie *pcie = &rc->pcie; - u32 value, ctrl; - - /* - * Set the root complex BAR configuration register: - * - disable both BAR0 and BAR1. - * - enable Prefetchable Memory Base and Limit registers in type 1 - * config space (64 bits). - * - enable IO Base and Limit registers in type 1 config - * space (32 bits). - */ - ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED; - value = CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(ctrl) | - CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(ctrl) | - CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE | - CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS | - CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE | - CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS; - cdns_pcie_writel(pcie, CDNS_PCIE_LM_RC_BAR_CFG, value); - - /* Set root port configuration space */ - if (rc->vendor_id != 0xffff) - cdns_pcie_rp_writew(pcie, PCI_VENDOR_ID, rc->vendor_id); - if (rc->device_id != 0xffff) - cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id); - - cdns_pcie_rp_writeb(pcie, PCI_CLASS_REVISION, 0); - cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0); - cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); - - return 0; -} - -static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) -{ - struct cdns_pcie *pcie = &rc->pcie; - struct resource *cfg_res = rc->cfg_res; - struct resource *mem_res = pcie->mem_res; - struct resource *bus_range = rc->bus_range; - struct device *dev = rc->dev; - struct device_node *np = dev->of_node; - struct of_pci_range_parser parser; - struct of_pci_range range; - u32 addr0, addr1, desc1; - u64 cpu_addr; - int r, err; - - /* - * Reserve region 0 for PCI configure space accesses: - * OB_REGION_PCI_ADDR0 and OB_REGION_DESC0 are updated dynamically by - * cdns_pci_map_bus(), other region registers are set here once for all. - */ - addr1 = 0; /* Should be programmed to zero. */ - desc1 = CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus_range->start); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(0), addr1); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(0), desc1); - - cpu_addr = cfg_res->start - mem_res->start; - addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(12) | - (lower_32_bits(cpu_addr) & GENMASK(31, 8)); - addr1 = upper_32_bits(cpu_addr); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(0), addr0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(0), addr1); - - err = of_pci_range_parser_init(&parser, np); - if (err) - return err; - - r = 1; - for_each_of_pci_range(&parser, &range) { - bool is_io; - - if (r >= rc->max_regions) - break; - - if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM) - is_io = false; - else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO) - is_io = true; - else - continue; - - cdns_pcie_set_outbound_region(pcie, 0, r, is_io, - range.cpu_addr, - range.pci_addr, - range.size); - r++; - } - - /* - * Set Root Port no BAR match Inbound Translation registers: - * needed for MSI and DMA. - * Root Port BAR0 and BAR1 are disabled, hence no need to set their - * inbound translation registers. - */ - addr0 = CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(rc->no_bar_nbits); - addr1 = 0; - cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR0(RP_NO_BAR), addr0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR1(RP_NO_BAR), addr1); - - return 0; -} - -static int cdns_pcie_host_init(struct device *dev, - struct list_head *resources, - struct cdns_pcie_rc *rc) -{ - struct resource *bus_range = NULL; - int err; - - /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); - if (err) - return err; - - rc->bus_range = bus_range; - rc->pcie.bus = bus_range->start; - - err = cdns_pcie_host_init_root_port(rc); - if (err) - goto err_out; - - err = cdns_pcie_host_init_address_translation(rc); - if (err) - goto err_out; - - return 0; - - err_out: - pci_free_resource_list(resources); - return err; -} - -static int cdns_pcie_host_probe(struct platform_device *pdev) -{ - const char *type; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct pci_host_bridge *bridge; - struct list_head resources; - struct cdns_pcie_rc *rc; - struct cdns_pcie *pcie; - struct resource *res; - int ret; - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); - if (!bridge) - return -ENOMEM; - - rc = pci_host_bridge_priv(bridge); - rc->dev = dev; - - pcie = &rc->pcie; - pcie->is_rc = true; - - rc->max_regions = 32; - of_property_read_u32(np, "cdns,max-outbound-regions", &rc->max_regions); - - rc->no_bar_nbits = 32; - of_property_read_u32(np, "cdns,no-bar-match-nbits", &rc->no_bar_nbits); - - rc->vendor_id = 0xffff; - of_property_read_u16(np, "vendor-id", &rc->vendor_id); - - rc->device_id = 0xffff; - of_property_read_u16(np, "device-id", &rc->device_id); - - type = of_get_property(np, "device_type", NULL); - if (!type || strcmp(type, "pci")) { - dev_err(dev, "invalid \"device_type\" %s\n", type); - return -EINVAL; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); - pcie->reg_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->reg_base)) { - dev_err(dev, "missing \"reg\"\n"); - return PTR_ERR(pcie->reg_base); - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); - rc->cfg_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(rc->cfg_base)) { - dev_err(dev, "missing \"cfg\"\n"); - return PTR_ERR(rc->cfg_base); - } - rc->cfg_res = res; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); - if (!res) { - dev_err(dev, "missing \"mem\"\n"); - return -EINVAL; - } - pcie->mem_res = res; - - pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - dev_err(dev, "pm_runtime_get_sync() failed\n"); - goto err_get_sync; - } - - ret = cdns_pcie_host_init(dev, &resources, rc); - if (ret) - goto err_init; - - list_splice_init(&resources, &bridge->windows); - bridge->dev.parent = dev; - bridge->busnr = pcie->bus; - bridge->ops = &cdns_pcie_host_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - ret = pci_host_probe(bridge); - if (ret < 0) - goto err_host_probe; - - return 0; - - err_host_probe: - pci_free_resource_list(&resources); - - err_init: - pm_runtime_put_sync(dev); - - err_get_sync: - pm_runtime_disable(dev); - - return ret; -} - -static struct platform_driver cdns_pcie_host_driver = { - .driver = { - .name = "cdns-pcie-host", - .of_match_table = cdns_pcie_host_of_match, - }, - .probe = cdns_pcie_host_probe, -}; -builtin_platform_driver(cdns_pcie_host_driver); diff --git a/drivers/pci/cadence/pcie-cadence.c b/drivers/pci/cadence/pcie-cadence.c deleted file mode 100644 index 138d113eb45d..000000000000 --- a/drivers/pci/cadence/pcie-cadence.c +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2017 Cadence -// Cadence PCIe controller driver. -// Author: Cyrille Pitchen - -#include - -#include "pcie-cadence.h" - -void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, - u32 r, bool is_io, - u64 cpu_addr, u64 pci_addr, size_t size) -{ - /* - * roundup_pow_of_two() returns an unsigned long, which is not suited - * for 64bit values. - */ - u64 sz = 1ULL << fls64(size - 1); - int nbits = ilog2(sz); - u32 addr0, addr1, desc0, desc1; - - if (nbits < 8) - nbits = 8; - - /* Set the PCI address */ - addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) | - (lower_32_bits(pci_addr) & GENMASK(31, 8)); - addr1 = upper_32_bits(pci_addr); - - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), addr0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), addr1); - - /* Set the PCIe header descriptor */ - if (is_io) - desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO; - else - desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM; - desc1 = 0; - - /* - * Whatever Bit [23] is set or not inside DESC0 register of the outbound - * PCIe descriptor, the PCI function number must be set into - * Bits [26:24] of DESC0 anyway. - * - * In Root Complex mode, the function number is always 0 but in Endpoint - * mode, the PCIe controller may support more than one function. This - * function number needs to be set properly into the outbound PCIe - * descriptor. - * - * Besides, setting Bit [23] is mandatory when in Root Complex mode: - * then the driver must provide the bus, resp. device, number in - * Bits [7:0] of DESC1, resp. Bits[31:27] of DESC0. Like the function - * number, the device number is always 0 in Root Complex mode. - * - * However when in Endpoint mode, we can clear Bit [23] of DESC0, hence - * the PCIe controller will use the captured values for the bus and - * device numbers. - */ - if (pcie->is_rc) { - /* The device and function numbers are always 0. */ - desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | - CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); - desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); - } else { - /* - * Use captured values for bus and device numbers but still - * need to set the function number. - */ - desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(fn); - } - - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), desc0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); - - /* Set the CPU address */ - cpu_addr -= pcie->mem_res->start; - addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) | - (lower_32_bits(cpu_addr) & GENMASK(31, 8)); - addr1 = upper_32_bits(cpu_addr); - - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), addr0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), addr1); -} - -void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, - u32 r, u64 cpu_addr) -{ - u32 addr0, addr1, desc0, desc1; - - desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG; - desc1 = 0; - - /* See cdns_pcie_set_outbound_region() comments above. */ - if (pcie->is_rc) { - desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | - CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); - desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); - } else { - desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(fn); - } - - /* Set the CPU address */ - cpu_addr -= pcie->mem_res->start; - addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(17) | - (lower_32_bits(cpu_addr) & GENMASK(31, 8)); - addr1 = upper_32_bits(cpu_addr); - - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), 0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), 0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), desc0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), addr0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), addr1); -} - -void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r) -{ - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), 0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), 0); - - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), 0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), 0); - - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), 0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), 0); -} diff --git a/drivers/pci/cadence/pcie-cadence.h b/drivers/pci/cadence/pcie-cadence.h deleted file mode 100644 index 4bb27333b05c..000000000000 --- a/drivers/pci/cadence/pcie-cadence.h +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2017 Cadence -// Cadence PCIe controller driver. -// Author: Cyrille Pitchen - -#ifndef _PCIE_CADENCE_H -#define _PCIE_CADENCE_H - -#include -#include - -/* - * Local Management Registers - */ -#define CDNS_PCIE_LM_BASE 0x00100000 - -/* Vendor ID Register */ -#define CDNS_PCIE_LM_ID (CDNS_PCIE_LM_BASE + 0x0044) -#define CDNS_PCIE_LM_ID_VENDOR_MASK GENMASK(15, 0) -#define CDNS_PCIE_LM_ID_VENDOR_SHIFT 0 -#define CDNS_PCIE_LM_ID_VENDOR(vid) \ - (((vid) << CDNS_PCIE_LM_ID_VENDOR_SHIFT) & CDNS_PCIE_LM_ID_VENDOR_MASK) -#define CDNS_PCIE_LM_ID_SUBSYS_MASK GENMASK(31, 16) -#define CDNS_PCIE_LM_ID_SUBSYS_SHIFT 16 -#define CDNS_PCIE_LM_ID_SUBSYS(sub) \ - (((sub) << CDNS_PCIE_LM_ID_SUBSYS_SHIFT) & CDNS_PCIE_LM_ID_SUBSYS_MASK) - -/* Root Port Requestor ID Register */ -#define CDNS_PCIE_LM_RP_RID (CDNS_PCIE_LM_BASE + 0x0228) -#define CDNS_PCIE_LM_RP_RID_MASK GENMASK(15, 0) -#define CDNS_PCIE_LM_RP_RID_SHIFT 0 -#define CDNS_PCIE_LM_RP_RID_(rid) \ - (((rid) << CDNS_PCIE_LM_RP_RID_SHIFT) & CDNS_PCIE_LM_RP_RID_MASK) - -/* Endpoint Bus and Device Number Register */ -#define CDNS_PCIE_LM_EP_ID (CDNS_PCIE_LM_BASE + 0x022c) -#define CDNS_PCIE_LM_EP_ID_DEV_MASK GENMASK(4, 0) -#define CDNS_PCIE_LM_EP_ID_DEV_SHIFT 0 -#define CDNS_PCIE_LM_EP_ID_BUS_MASK GENMASK(15, 8) -#define CDNS_PCIE_LM_EP_ID_BUS_SHIFT 8 - -/* Endpoint Function f BAR b Configuration Registers */ -#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn) \ - (CDNS_PCIE_LM_BASE + 0x0240 + (fn) * 0x0008) -#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn) \ - (CDNS_PCIE_LM_BASE + 0x0244 + (fn) * 0x0008) -#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \ - (GENMASK(4, 0) << ((b) * 8)) -#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \ - (((a) << ((b) * 8)) & CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b)) -#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b) \ - (GENMASK(7, 5) << ((b) * 8)) -#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, c) \ - (((c) << ((b) * 8 + 5)) & CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)) - -/* Endpoint Function Configuration Register */ -#define CDNS_PCIE_LM_EP_FUNC_CFG (CDNS_PCIE_LM_BASE + 0x02c0) - -/* Root Complex BAR Configuration Register */ -#define CDNS_PCIE_LM_RC_BAR_CFG (CDNS_PCIE_LM_BASE + 0x0300) -#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK GENMASK(5, 0) -#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE(a) \ - (((a) << 0) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK) -#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK GENMASK(8, 6) -#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(c) \ - (((c) << 6) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK) -#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK GENMASK(13, 9) -#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE(a) \ - (((a) << 9) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK) -#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK GENMASK(16, 14) -#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(c) \ - (((c) << 14) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK) -#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE BIT(17) -#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_32BITS 0 -#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS BIT(18) -#define CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE BIT(19) -#define CDNS_PCIE_LM_RC_BAR_CFG_IO_16BITS 0 -#define CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS BIT(20) -#define CDNS_PCIE_LM_RC_BAR_CFG_CHECK_ENABLE BIT(31) - -/* BAR control values applicable to both Endpoint Function and Root Complex */ -#define CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED 0x0 -#define CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS 0x1 -#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS 0x4 -#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5 -#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS 0x6 -#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7 - - -/* - * Endpoint Function Registers (PCI configuration space for endpoint functions) - */ -#define CDNS_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12)) - -#define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90 - -/* - * Root Port Registers (PCI configuration space for the root port function) - */ -#define CDNS_PCIE_RP_BASE 0x00200000 - - -/* - * Address Translation Registers - */ -#define CDNS_PCIE_AT_BASE 0x00400000 - -/* Region r Outbound AXI to PCIe Address Translation Register 0 */ -#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r) \ - (CDNS_PCIE_AT_BASE + 0x0000 + ((r) & 0x1f) * 0x0020) -#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK GENMASK(5, 0) -#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) \ - (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK) -#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(19, 12) -#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \ - (((devfn) << 12) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK) -#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(27, 20) -#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(bus) \ - (((bus) << 20) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK) - -/* Region r Outbound AXI to PCIe Address Translation Register 1 */ -#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r) \ - (CDNS_PCIE_AT_BASE + 0x0004 + ((r) & 0x1f) * 0x0020) - -/* Region r Outbound PCIe Descriptor Register 0 */ -#define CDNS_PCIE_AT_OB_REGION_DESC0(r) \ - (CDNS_PCIE_AT_BASE + 0x0008 + ((r) & 0x1f) * 0x0020) -#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(3, 0) -#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM 0x2 -#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO 0x6 -#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 0xa -#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 0xb -#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG 0xc -#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_VENDOR_MSG 0xd -/* Bit 23 MUST be set in RC mode. */ -#define CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID BIT(23) -#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK GENMASK(31, 24) -#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(devfn) \ - (((devfn) << 24) & CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK) - -/* Region r Outbound PCIe Descriptor Register 1 */ -#define CDNS_PCIE_AT_OB_REGION_DESC1(r) \ - (CDNS_PCIE_AT_BASE + 0x000c + ((r) & 0x1f) * 0x0020) -#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK GENMASK(7, 0) -#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus) \ - ((bus) & CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK) - -/* Region r AXI Region Base Address Register 0 */ -#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r) \ - (CDNS_PCIE_AT_BASE + 0x0018 + ((r) & 0x1f) * 0x0020) -#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK GENMASK(5, 0) -#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) \ - (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK) - -/* Region r AXI Region Base Address Register 1 */ -#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r) \ - (CDNS_PCIE_AT_BASE + 0x001c + ((r) & 0x1f) * 0x0020) - -/* Root Port BAR Inbound PCIe to AXI Address Translation Register */ -#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0(bar) \ - (CDNS_PCIE_AT_BASE + 0x0800 + (bar) * 0x0008) -#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK GENMASK(5, 0) -#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(nbits) \ - (((nbits) - 1) & CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK) -#define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \ - (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008) - -enum cdns_pcie_rp_bar { - RP_BAR0, - RP_BAR1, - RP_NO_BAR -}; - -/* Endpoint Function BAR Inbound PCIe to AXI Address Translation Register */ -#define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ - (CDNS_PCIE_AT_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008) -#define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \ - (CDNS_PCIE_AT_BASE + 0x0844 + (fn) * 0x0040 + (bar) * 0x0008) - -/* Normal/Vendor specific message access: offset inside some outbound region */ -#define CDNS_PCIE_NORMAL_MSG_ROUTING_MASK GENMASK(7, 5) -#define CDNS_PCIE_NORMAL_MSG_ROUTING(route) \ - (((route) << 5) & CDNS_PCIE_NORMAL_MSG_ROUTING_MASK) -#define CDNS_PCIE_NORMAL_MSG_CODE_MASK GENMASK(15, 8) -#define CDNS_PCIE_NORMAL_MSG_CODE(code) \ - (((code) << 8) & CDNS_PCIE_NORMAL_MSG_CODE_MASK) -#define CDNS_PCIE_MSG_NO_DATA BIT(16) - -enum cdns_pcie_msg_code { - MSG_CODE_ASSERT_INTA = 0x20, - MSG_CODE_ASSERT_INTB = 0x21, - MSG_CODE_ASSERT_INTC = 0x22, - MSG_CODE_ASSERT_INTD = 0x23, - MSG_CODE_DEASSERT_INTA = 0x24, - MSG_CODE_DEASSERT_INTB = 0x25, - MSG_CODE_DEASSERT_INTC = 0x26, - MSG_CODE_DEASSERT_INTD = 0x27, -}; - -enum cdns_pcie_msg_routing { - /* Route to Root Complex */ - MSG_ROUTING_TO_RC, - - /* Use Address Routing */ - MSG_ROUTING_BY_ADDR, - - /* Use ID Routing */ - MSG_ROUTING_BY_ID, - - /* Route as Broadcast Message from Root Complex */ - MSG_ROUTING_BCAST, - - /* Local message; terminate at receiver (INTx messages) */ - MSG_ROUTING_LOCAL, - - /* Gather & route to Root Complex (PME_TO_Ack message) */ - MSG_ROUTING_GATHER, -}; - -/** - * struct cdns_pcie - private data for Cadence PCIe controller drivers - * @reg_base: IO mapped register base - * @mem_res: start/end offsets in the physical system memory to map PCI accesses - * @is_rc: tell whether the PCIe controller mode is Root Complex or Endpoint. - * @bus: In Root Complex mode, the bus number - */ -struct cdns_pcie { - void __iomem *reg_base; - struct resource *mem_res; - bool is_rc; - u8 bus; -}; - -/* Register access */ -static inline void cdns_pcie_writeb(struct cdns_pcie *pcie, u32 reg, u8 value) -{ - writeb(value, pcie->reg_base + reg); -} - -static inline void cdns_pcie_writew(struct cdns_pcie *pcie, u32 reg, u16 value) -{ - writew(value, pcie->reg_base + reg); -} - -static inline void cdns_pcie_writel(struct cdns_pcie *pcie, u32 reg, u32 value) -{ - writel(value, pcie->reg_base + reg); -} - -static inline u32 cdns_pcie_readl(struct cdns_pcie *pcie, u32 reg) -{ - return readl(pcie->reg_base + reg); -} - -/* Root Port register access */ -static inline void cdns_pcie_rp_writeb(struct cdns_pcie *pcie, - u32 reg, u8 value) -{ - writeb(value, pcie->reg_base + CDNS_PCIE_RP_BASE + reg); -} - -static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, - u32 reg, u16 value) -{ - writew(value, pcie->reg_base + CDNS_PCIE_RP_BASE + reg); -} - -/* Endpoint Function register access */ -static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn, - u32 reg, u8 value) -{ - writeb(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); -} - -static inline void cdns_pcie_ep_fn_writew(struct cdns_pcie *pcie, u8 fn, - u32 reg, u16 value) -{ - writew(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); -} - -static inline void cdns_pcie_ep_fn_writel(struct cdns_pcie *pcie, u8 fn, - u32 reg, u16 value) -{ - writel(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); -} - -static inline u8 cdns_pcie_ep_fn_readb(struct cdns_pcie *pcie, u8 fn, u32 reg) -{ - return readb(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); -} - -static inline u16 cdns_pcie_ep_fn_readw(struct cdns_pcie *pcie, u8 fn, u32 reg) -{ - return readw(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); -} - -static inline u32 cdns_pcie_ep_fn_readl(struct cdns_pcie *pcie, u8 fn, u32 reg) -{ - return readl(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); -} - -void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, - u32 r, bool is_io, - u64 cpu_addr, u64 pci_addr, size_t size); - -void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, - u32 r, u64 cpu_addr); - -void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); - -#endif /* _PCIE_CADENCE_H */ diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig new file mode 100644 index 000000000000..18fa09b3ac8f --- /dev/null +++ b/drivers/pci/controller/Kconfig @@ -0,0 +1,275 @@ +# SPDX-License-Identifier: GPL-2.0 + +menu "PCI controller drivers" + depends on PCI + +config PCI_MVEBU + bool "Marvell EBU PCIe controller" + depends on ARCH_MVEBU || ARCH_DOVE || COMPILE_TEST + depends on MVEBU_MBUS + depends on ARM + depends on OF + +config PCI_AARDVARK + bool "Aardvark PCIe controller" + depends on (ARCH_MVEBU && ARM64) || COMPILE_TEST + depends on OF + depends on PCI_MSI_IRQ_DOMAIN + help + Add support for Aardvark 64bit PCIe Host Controller. This + controller is part of the South Bridge of the Marvel Armada + 3700 SoC. + +menu "Cadence PCIe controllers support" + +config PCIE_CADENCE + bool + +config PCIE_CADENCE_HOST + bool "Cadence PCIe host controller" + depends on OF + depends on PCI + select IRQ_DOMAIN + select PCIE_CADENCE + help + Say Y here if you want to support the Cadence PCIe controller in host + mode. This PCIe controller may be embedded into many different vendors + SoCs. + +config PCIE_CADENCE_EP + bool "Cadence PCIe endpoint controller" + depends on OF + depends on PCI_ENDPOINT + select PCIE_CADENCE + help + Say Y here if you want to support the Cadence PCIe controller in + endpoint mode. This PCIe controller may be embedded into many + different vendors SoCs. + +endmenu + +config PCIE_XILINX_NWL + bool "NWL PCIe Core" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + help + Say 'Y' here if you want kernel support for Xilinx + NWL PCIe controller. The controller can act as Root Port + or End Point. The current option selection will only + support root port enabling. + +config PCI_FTPCI100 + bool "Faraday Technology FTPCI100 PCI controller" + depends on OF + default ARCH_GEMINI + +config PCI_TEGRA + bool "NVIDIA Tegra PCIe controller" + depends on ARCH_TEGRA || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + help + Say Y here if you want support for the PCIe host controller found + on NVIDIA Tegra SoCs. + +config PCI_RCAR_GEN2 + bool "Renesas R-Car Gen2 Internal PCI controller" + depends on ARCH_RENESAS || COMPILE_TEST + depends on ARM + help + Say Y here if you want internal PCI support on R-Car Gen2 SoC. + There are 3 internal PCI controllers available with a single + built-in EHCI/OHCI host controller present on each one. + +config PCIE_RCAR + bool "Renesas R-Car PCIe 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. + +config PCI_HOST_COMMON + bool + select PCI_ECAM + +config PCI_HOST_GENERIC + bool "Generic PCI host controller" + depends on OF + select PCI_HOST_COMMON + select IRQ_DOMAIN + select PCI_DOMAINS + help + Say Y here if you want to support a simple generic PCI host + controller, such as the one emulated by kvmtool. + +config PCIE_XILINX + bool "Xilinx AXI PCIe host bridge support" + depends on ARCH_ZYNQ || MICROBLAZE || (MIPS && PCI_DRIVERS_GENERIC) || COMPILE_TEST + help + Say 'Y' here if you want kernel to support the Xilinx AXI PCIe + Host Bridge driver. + +config PCI_XGENE + bool "X-Gene PCIe controller" + depends on ARM64 || COMPILE_TEST + depends on OF || (ACPI && PCI_QUIRKS) + help + Say Y here if you want internal PCI support on APM X-Gene SoC. + There are 5 internal PCIe ports available. Each port is GEN3 capable + and have varied lanes from x1 to x8. + +config PCI_XGENE_MSI + bool "X-Gene v1 PCIe MSI feature" + depends on PCI_XGENE + depends on PCI_MSI_IRQ_DOMAIN + default y + help + Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC. + This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC. + +config PCI_V3_SEMI + bool "V3 Semiconductor PCI controller" + depends on OF + depends on ARM || COMPILE_TEST + default ARCH_INTEGRATOR_AP + +config PCI_VERSATILE + bool "ARM Versatile PB PCI controller" + depends on ARCH_VERSATILE + +config PCIE_IPROC + tristate + select PCI_DOMAINS + help + This enables the iProc PCIe core controller support for Broadcom's + iProc family of SoCs. An appropriate bus interface driver needs + to be enabled to select this. + +config PCIE_IPROC_PLATFORM + tristate "Broadcom iProc PCIe platform bus driver" + depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST) + depends on OF + select PCIE_IPROC + default ARCH_BCM_IPROC + help + Say Y here if you want to use the Broadcom iProc PCIe controller + through the generic platform bus interface + +config PCIE_IPROC_BCMA + tristate "Broadcom iProc PCIe BCMA bus driver" + depends on ARM && (ARCH_BCM_IPROC || COMPILE_TEST) + select PCIE_IPROC + select BCMA + default ARCH_BCM_5301X + help + Say Y here if you want to use the Broadcom iProc PCIe controller + through the BCMA bus interface + +config PCIE_IPROC_MSI + bool "Broadcom iProc PCIe MSI support" + depends on PCIE_IPROC_PLATFORM || PCIE_IPROC_BCMA + depends on PCI_MSI_IRQ_DOMAIN + default ARCH_BCM_IPROC + help + Say Y here if you want to enable MSI support for Broadcom's iProc + PCIe controller + +config PCIE_ALTERA + bool "Altera PCIe controller" + depends on ARM || NIOS2 || COMPILE_TEST + select PCI_DOMAINS + help + Say Y here if you want to enable PCIe controller support on Altera + FPGA. + +config PCIE_ALTERA_MSI + bool "Altera PCIe MSI feature" + depends on PCIE_ALTERA + depends on PCI_MSI_IRQ_DOMAIN + help + Say Y here if you want PCIe MSI support for the Altera FPGA. + This MSI driver supports Altera MSI to GIC controller IP. + +config PCI_HOST_THUNDER_PEM + bool "Cavium Thunder PCIe controller to off-chip devices" + depends on ARM64 || COMPILE_TEST + depends on OF || (ACPI && PCI_QUIRKS) + select PCI_HOST_COMMON + help + Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs. + +config PCI_HOST_THUNDER_ECAM + bool "Cavium Thunder ECAM controller to on-chip devices on pass-1.x silicon" + depends on ARM64 || COMPILE_TEST + depends on OF || (ACPI && PCI_QUIRKS) + select PCI_HOST_COMMON + help + Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs. + +config PCIE_ROCKCHIP + bool + depends on PCI + +config PCIE_ROCKCHIP_HOST + tristate "Rockchip PCIe host controller" + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on OF + depends on PCI_MSI_IRQ_DOMAIN + select MFD_SYSCON + select PCIE_ROCKCHIP + help + Say Y here if you want internal PCI support on Rockchip SoC. + There is 1 internal PCIe port available to support GEN2 with + 4 slots. + +config PCIE_ROCKCHIP_EP + bool "Rockchip PCIe endpoint controller" + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on OF + depends on PCI_ENDPOINT + select MFD_SYSCON + select PCIE_ROCKCHIP + help + Say Y here if you want to support Rockchip PCIe controller in + endpoint mode on Rockchip SoC. There is 1 internal PCIe port + available to support GEN2 with 4 slots. + +config PCIE_MEDIATEK + bool "MediaTek PCIe controller" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on OF + depends on PCI_MSI_IRQ_DOMAIN + help + Say Y here if you want to enable PCIe controller support on + MediaTek SoCs. + +config PCIE_TANGO_SMP8759 + bool "Tango SMP8759 PCIe controller (DANGEROUS)" + depends on ARCH_TANGO && PCI_MSI && OF + depends on BROKEN + select PCI_HOST_COMMON + help + Say Y here to enable PCIe controller support for Sigma Designs + Tango SMP8759-based systems. + + Note: The SMP8759 controller multiplexes PCI config and MMIO + accesses, and Linux doesn't provide a way to serialize them. + This can lead to data corruption if drivers perform concurrent + config and MMIO accesses. + +config VMD + depends on PCI_MSI && X86_64 && SRCU + tristate "Intel Volume Management Device Driver" + ---help--- + Adds support for the Intel Volume Management Device (VMD). VMD is a + secondary PCI host bridge that allows PCI Express root ports, + and devices attached to them, to be removed from the default + PCI domain and placed within the VMD domain. This provides + more bus resources than are otherwise possible with a + single domain. If you know your system provides one of these and + has devices attached to it, say Y; if you are not sure, say N. + + To compile this driver as a module, choose M here: the + module will be called vmd. + +source "drivers/pci/controller/dwc/Kconfig" +endmenu diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile new file mode 100644 index 000000000000..24322b92f200 --- /dev/null +++ b/drivers/pci/controller/Makefile @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o +obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o +obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o +obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o +obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o +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_PCI_HOST_COMMON) += pci-host-common.o +obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o +obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o +obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o +obj-$(CONFIG_PCI_V3_SEMI) += pci-v3-semi.o +obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o +obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o +obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o +obj-$(CONFIG_PCIE_IPROC_MSI) += pcie-iproc-msi.o +obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o +obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o +obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o +obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o +obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o +obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o +obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o +obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o +obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o +obj-$(CONFIG_VMD) += vmd.o +# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW +obj-y += dwc/ + + +# The following drivers are for devices that use the generic ACPI +# pci_root.c driver but don't support standard ECAM config access. +# They contain MCFG quirks to replace the generic ECAM accessors with +# device-specific ones that are shared with the DT driver. + +# The ACPI driver is generic and should not require driver-specific +# config options to be enabled, so we always build these drivers on +# ARM64 and use internal ifdefs to only build the pieces we need +# depending on whether ACPI, the DT driver, or both are enabled. + +ifdef CONFIG_PCI +obj-$(CONFIG_ARM64) += pci-thunder-ecam.o +obj-$(CONFIG_ARM64) += pci-thunder-pem.o +obj-$(CONFIG_ARM64) += pci-xgene.o +endif diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig new file mode 100644 index 000000000000..16f52c626b4b --- /dev/null +++ b/drivers/pci/controller/dwc/Kconfig @@ -0,0 +1,197 @@ +# SPDX-License-Identifier: GPL-2.0 + +menu "DesignWare PCI Core Support" + depends on PCI + +config PCIE_DW + bool + +config PCIE_DW_HOST + bool + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW + +config PCIE_DW_EP + bool + depends on PCI_ENDPOINT + select PCIE_DW + +config PCI_DRA7XX + bool + +config PCI_DRA7XX_HOST + bool "TI DRA7xx PCIe controller Host Mode" + depends on SOC_DRA7XX || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + depends on OF && HAS_IOMEM && TI_PIPE3 + select PCIE_DW_HOST + select PCI_DRA7XX + default y + 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. + This controller can work either as EP or RC. In order to enable + host-specific features PCI_DRA7XX_HOST must be selected and in order + to enable device-specific features PCI_DRA7XX_EP must be selected. + This uses the DesignWare core. + +config PCI_DRA7XX_EP + bool "TI DRA7xx PCIe controller Endpoint Mode" + depends on SOC_DRA7XX || COMPILE_TEST + depends on PCI_ENDPOINT + depends on OF && HAS_IOMEM && TI_PIPE3 + select PCIE_DW_EP + select PCI_DRA7XX + help + Enables support for the PCIe controller in the DRA7xx SoC to work in + endpoint mode. There are two instances of PCIe controller in DRA7xx. + This controller can work either as EP or RC. In order to enable + host-specific features PCI_DRA7XX_HOST must be selected and in order + to enable device-specific features PCI_DRA7XX_EP must be selected. + This uses the DesignWare core. + +config PCIE_DW_PLAT + bool + +config PCIE_DW_PLAT_HOST + bool "Platform bus based DesignWare PCIe Controller - Host mode" + depends on PCI && PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + select PCIE_DW_PLAT + default y + help + Enables support for the PCIe controller in the Designware IP to + work in host mode. There are two instances of PCIe controller in + Designware IP. + This controller can work either as EP or RC. In order to enable + host-specific features PCIE_DW_PLAT_HOST must be selected and in + order to enable device-specific features PCI_DW_PLAT_EP must be + selected. + +config PCIE_DW_PLAT_EP + bool "Platform bus based DesignWare PCIe Controller - Endpoint mode" + depends on PCI && PCI_MSI_IRQ_DOMAIN + depends on PCI_ENDPOINT + select PCIE_DW_EP + select PCIE_DW_PLAT + help + Enables support for the PCIe controller in the Designware IP to + work in endpoint mode. There are two instances of PCIe controller + in Designware IP. + This controller can work either as EP or RC. In order to enable + host-specific features PCIE_DW_PLAT_HOST must be selected and in + order to enable device-specific features PCI_DW_PLAT_EP must be + selected. + +config PCI_EXYNOS + bool "Samsung Exynos PCIe controller" + depends on SOC_EXYNOS5440 || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + +config PCI_IMX6 + bool "Freescale i.MX6 PCIe controller" + depends on SOC_IMX6Q || (ARM && COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + +config PCIE_SPEAR13XX + bool "STMicroelectronics SPEAr PCIe controller" + depends on ARCH_SPEAR13XX || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want PCIe support on SPEAr13XX SoCs. + +config PCI_KEYSTONE + bool "TI Keystone PCIe controller" + depends on ARCH_KEYSTONE || (ARM && COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want to enable PCI controller support on Keystone + SoCs. The PCI controller on Keystone is based on DesignWare hardware + and therefore the driver re-uses the DesignWare core functions to + implement the driver. + +config PCI_LAYERSCAPE + bool "Freescale Layerscape PCIe controller" + depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select MFD_SYSCON + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support on Layerscape SoCs. + +config PCI_HISI + depends on OF && (ARM64 || COMPILE_TEST) + bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers" + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + select PCI_HOST_COMMON + help + Say Y here if you want PCIe controller support on HiSilicon + Hip05 and Hip06 SoCs + +config PCIE_QCOM + bool "Qualcomm PCIe controller" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here to enable PCIe controller support on Qualcomm SoCs. The + PCIe controller uses the DesignWare core plus Qualcomm-specific + hardware wrappers. + +config PCIE_ARMADA_8K + bool "Marvell Armada-8K PCIe controller" + depends on ARCH_MVEBU || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want to enable PCIe controller support on + Armada-8K SoCs. The PCIe controller on Armada-8K is based on + DesignWare hardware and therefore the driver re-uses the + DesignWare core functions to implement the driver. + +config PCIE_ARTPEC6 + bool + +config PCIE_ARTPEC6_HOST + bool "Axis ARTPEC-6 PCIe controller Host Mode" + depends on MACH_ARTPEC6 || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + select PCIE_ARTPEC6 + help + Enables support for the PCIe controller in the ARTPEC-6 SoC to work in + host mode. This uses the DesignWare core. + +config PCIE_ARTPEC6_EP + bool "Axis ARTPEC-6 PCIe controller Endpoint Mode" + depends on MACH_ARTPEC6 || COMPILE_TEST + depends on PCI_ENDPOINT + select PCIE_DW_EP + select PCIE_ARTPEC6 + help + Enables support for the PCIe controller in the ARTPEC-6 SoC to work in + endpoint mode. This uses the DesignWare core. + +config PCIE_KIRIN + depends on OF && (ARM64 || COMPILE_TEST) + bool "HiSilicon Kirin series SoCs PCIe controllers" + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support + on HiSilicon Kirin series SoCs. + +config PCIE_HISI_STB + bool "HiSilicon STB SoCs PCIe controllers" + depends on ARCH_HISI || COMPILE_TEST + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support on HiSilicon STB SoCs + +endmenu diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile new file mode 100644 index 000000000000..5d2ce72c7a52 --- /dev/null +++ b/drivers/pci/controller/dwc/Makefile @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PCIE_DW) += pcie-designware.o +obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o +obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o +obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o +obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o +obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o +obj-$(CONFIG_PCI_IMX6) += pci-imx6.o +obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o +obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o +obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o +obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o +obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o +obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o +obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o + +# The following drivers are for devices that use the generic ACPI +# pci_root.c driver but don't support standard ECAM config access. +# They contain MCFG quirks to replace the generic ECAM accessors with +# device-specific ones that are shared with the DT driver. + +# The ACPI driver is generic and should not require driver-specific +# config options to be enabled, so we always build these drivers on +# ARM64 and use internal ifdefs to only build the pieces we need +# depending on whether ACPI, the DT driver, or both are enabled. + +ifdef CONFIG_PCI +obj-$(CONFIG_ARM64) += pcie-hisi.o +endif diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c new file mode 100644 index 000000000000..cfaeef81d868 --- /dev/null +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs + * + * Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Kishon Vijay Abraham I + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../pci.h" +#include "pcie-designware.h" + +/* PCIe controller wrapper DRA7XX configuration registers */ + +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN 0x0024 +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN 0x0028 +#define ERR_SYS BIT(0) +#define ERR_FATAL BIT(1) +#define ERR_NONFATAL BIT(2) +#define ERR_COR BIT(3) +#define ERR_AXI BIT(4) +#define ERR_ECRC BIT(5) +#define PME_TURN_OFF BIT(8) +#define PME_TO_ACK BIT(9) +#define PM_PME BIT(10) +#define LINK_REQ_RST BIT(11) +#define LINK_UP_EVT BIT(12) +#define CFG_BME_EVT BIT(13) +#define CFG_MSE_EVT BIT(14) +#define INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \ + ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \ + LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT) + +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI 0x0034 +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI 0x0038 +#define INTA BIT(0) +#define INTB BIT(1) +#define INTC BIT(2) +#define INTD BIT(3) +#define MSI BIT(4) +#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD) + +#define PCIECTRL_TI_CONF_DEVICE_TYPE 0x0100 +#define DEVICE_TYPE_EP 0x0 +#define DEVICE_TYPE_LEG_EP 0x1 +#define DEVICE_TYPE_RC 0x4 + +#define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104 +#define LTSSM_EN 0x1 + +#define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C +#define LINK_UP BIT(16) +#define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF + +#define EXP_CAP_ID_OFFSET 0x70 + +#define PCIECTRL_TI_CONF_INTX_ASSERT 0x0124 +#define PCIECTRL_TI_CONF_INTX_DEASSERT 0x0128 + +#define PCIECTRL_TI_CONF_MSI_XMT 0x012c +#define MSI_REQ_GRANT BIT(0) +#define MSI_VECTOR_SHIFT 7 + +struct dra7xx_pcie { + struct dw_pcie *pci; + void __iomem *base; /* DT ti_conf */ + int phy_count; /* DT phy-names count */ + struct phy **phy; + int link_gen; + struct irq_domain *irq_domain; + enum dw_pcie_device_mode mode; +}; + +struct dra7xx_pcie_of_data { + enum dw_pcie_device_mode mode; +}; + +#define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev) + +static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset) +{ + return readl(pcie->base + offset); +} + +static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, + u32 value) +{ + writel(value, pcie->base + offset); +} + +static u64 dra7xx_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) +{ + return pci_addr & DRA7XX_CPU_TO_BUS_ADDR; +} + +static int dra7xx_pcie_link_up(struct dw_pcie *pci) +{ + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); + + return !!(reg & LINK_UP); +} + +static void dra7xx_pcie_stop_link(struct dw_pcie *pci) +{ + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + u32 reg; + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); + reg &= ~LTSSM_EN; + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); +} + +static int dra7xx_pcie_establish_link(struct dw_pcie *pci) +{ + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + struct device *dev = pci->dev; + u32 reg; + u32 exp_cap_off = EXP_CAP_ID_OFFSET; + + if (dw_pcie_link_up(pci)) { + dev_err(dev, "link is already up\n"); + return 0; + } + + if (dra7xx->link_gen == 1) { + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCAP, + 4, ®); + if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { + reg &= ~((u32)PCI_EXP_LNKCAP_SLS); + reg |= PCI_EXP_LNKCAP_SLS_2_5GB; + dw_pcie_write(pci->dbi_base + exp_cap_off + + PCI_EXP_LNKCAP, 4, reg); + } + + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2, + 2, ®); + if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { + reg &= ~((u32)PCI_EXP_LNKCAP_SLS); + reg |= PCI_EXP_LNKCAP_SLS_2_5GB; + dw_pcie_write(pci->dbi_base + exp_cap_off + + PCI_EXP_LNKCTL2, 2, reg); + } + } + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); + reg |= LTSSM_EN; + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); + + return 0; +} + +static void dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie *dra7xx) +{ + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, + LEG_EP_INTERRUPTS | MSI); + + dra7xx_pcie_writel(dra7xx, + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, + MSI | LEG_EP_INTERRUPTS); +} + +static void dra7xx_pcie_enable_wrapper_interrupts(struct dra7xx_pcie *dra7xx) +{ + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, + INTERRUPTS); + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, + INTERRUPTS); +} + +static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) +{ + dra7xx_pcie_enable_wrapper_interrupts(dra7xx); + dra7xx_pcie_enable_msi_interrupts(dra7xx); +} + +static int dra7xx_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + + dw_pcie_setup_rc(pp); + + dra7xx_pcie_establish_link(pci); + dw_pcie_wait_for_link(pci); + dw_pcie_msi_init(pp); + dra7xx_pcie_enable_interrupts(dra7xx); + + return 0; +} + +static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = { + .host_init = dra7xx_pcie_host_init, +}; + +static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { + .map = dra7xx_pcie_intx_map, + .xlate = pci_irqd_intx_xlate, +}; + +static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node = of_get_next_child(node, NULL); + + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return -ENODEV; + } + + dra7xx->irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &intx_domain_ops, pp); + if (!dra7xx->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return -ENODEV; + } + + return 0; +} + +static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) +{ + struct dra7xx_pcie *dra7xx = arg; + struct dw_pcie *pci = dra7xx->pci; + struct pcie_port *pp = &pci->pp; + unsigned long reg; + u32 virq, bit; + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI); + + switch (reg) { + case MSI: + dw_handle_msi_irq(pp); + break; + case INTA: + case INTB: + case INTC: + case INTD: + for_each_set_bit(bit, ®, PCI_NUM_INTX) { + virq = irq_find_mapping(dra7xx->irq_domain, bit); + if (virq) + generic_handle_irq(virq); + } + break; + } + + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, reg); + + return IRQ_HANDLED; +} + +static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) +{ + struct dra7xx_pcie *dra7xx = arg; + struct dw_pcie *pci = dra7xx->pci; + struct device *dev = pci->dev; + struct dw_pcie_ep *ep = &pci->ep; + u32 reg; + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); + + if (reg & ERR_SYS) + dev_dbg(dev, "System Error\n"); + + if (reg & ERR_FATAL) + dev_dbg(dev, "Fatal Error\n"); + + if (reg & ERR_NONFATAL) + dev_dbg(dev, "Non Fatal Error\n"); + + if (reg & ERR_COR) + dev_dbg(dev, "Correctable Error\n"); + + if (reg & ERR_AXI) + dev_dbg(dev, "AXI tag lookup fatal Error\n"); + + if (reg & ERR_ECRC) + dev_dbg(dev, "ECRC Error\n"); + + if (reg & PME_TURN_OFF) + dev_dbg(dev, + "Power Management Event Turn-Off message received\n"); + + if (reg & PME_TO_ACK) + dev_dbg(dev, + "Power Management Turn-Off Ack message received\n"); + + if (reg & PM_PME) + dev_dbg(dev, "PM Power Management Event message received\n"); + + if (reg & LINK_REQ_RST) + dev_dbg(dev, "Link Request Reset\n"); + + if (reg & LINK_UP_EVT) { + if (dra7xx->mode == DW_PCIE_EP_TYPE) + dw_pcie_ep_linkup(ep); + dev_dbg(dev, "Link-up state change\n"); + } + + if (reg & CFG_BME_EVT) + dev_dbg(dev, "CFG 'Bus Master Enable' change\n"); + + if (reg & CFG_MSE_EVT) + dev_dbg(dev, "CFG 'Memory Space Enable' change\n"); + + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg); + + return IRQ_HANDLED; +} + +static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + enum pci_barno bar; + + for (bar = BAR_0; bar <= BAR_5; bar++) + dw_pcie_ep_reset_bar(pci, bar); + + dra7xx_pcie_enable_wrapper_interrupts(dra7xx); +} + +static void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx) +{ + dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1); + mdelay(1); + dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_DEASSERT, 0x1); +} + +static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx, + u8 interrupt_num) +{ + u32 reg; + + reg = (interrupt_num - 1) << MSI_VECTOR_SHIFT; + reg |= MSI_REQ_GRANT; + dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_MSI_XMT, reg); +} + +static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, + enum pci_epc_irq_type type, u8 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + + switch (type) { + case PCI_EPC_IRQ_LEGACY: + dra7xx_pcie_raise_legacy_irq(dra7xx); + break; + case PCI_EPC_IRQ_MSI: + dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num); + break; + default: + dev_err(pci->dev, "UNKNOWN IRQ type\n"); + } + + return 0; +} + +static struct dw_pcie_ep_ops pcie_ep_ops = { + .ep_init = dra7xx_pcie_ep_init, + .raise_irq = dra7xx_pcie_raise_irq, +}; + +static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, + struct platform_device *pdev) +{ + int ret; + struct dw_pcie_ep *ep; + struct resource *res; + struct device *dev = &pdev->dev; + struct dw_pcie *pci = dra7xx->pci; + + ep = &pci->ep; + ep->ops = &pcie_ep_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics"); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics2"); + pci->dbi_base2 = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base2)) + return PTR_ERR(pci->dbi_base2); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); + if (!res) + return -EINVAL; + + ep->phys_base = res->start; + ep->addr_size = resource_size(res); + + ret = dw_pcie_ep_init(ep); + if (ret) { + dev_err(dev, "failed to initialize endpoint\n"); + return ret; + } + + return 0; +} + +static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, + struct platform_device *pdev) +{ + int ret; + struct dw_pcie *pci = dra7xx->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; + struct resource *res; + + pp->irq = platform_get_irq(pdev, 1); + if (pp->irq < 0) { + dev_err(dev, "missing IRQ resource\n"); + return pp->irq; + } + + ret = devm_request_irq(dev, pp->irq, dra7xx_pcie_msi_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, + "dra7-pcie-msi", dra7xx); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + + ret = dra7xx_pcie_init_irq_domain(pp); + if (ret < 0) + return ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics"); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + pp->ops = &dra7xx_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .cpu_addr_fixup = dra7xx_pcie_cpu_addr_fixup, + .start_link = dra7xx_pcie_establish_link, + .stop_link = dra7xx_pcie_stop_link, + .link_up = dra7xx_pcie_link_up, +}; + +static void dra7xx_pcie_disable_phy(struct dra7xx_pcie *dra7xx) +{ + int phy_count = dra7xx->phy_count; + + while (phy_count--) { + phy_power_off(dra7xx->phy[phy_count]); + phy_exit(dra7xx->phy[phy_count]); + } +} + +static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx) +{ + int phy_count = dra7xx->phy_count; + int ret; + int i; + + for (i = 0; i < phy_count; i++) { + ret = phy_init(dra7xx->phy[i]); + if (ret < 0) + goto err_phy; + + ret = phy_power_on(dra7xx->phy[i]); + if (ret < 0) { + phy_exit(dra7xx->phy[i]); + goto err_phy; + } + } + + return 0; + +err_phy: + while (--i >= 0) { + phy_power_off(dra7xx->phy[i]); + phy_exit(dra7xx->phy[i]); + } + + return ret; +} + +static const struct dra7xx_pcie_of_data dra7xx_pcie_rc_of_data = { + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = { + .mode = DW_PCIE_EP_TYPE, +}; + +static const struct of_device_id of_dra7xx_pcie_match[] = { + { + .compatible = "ti,dra7-pcie", + .data = &dra7xx_pcie_rc_of_data, + }, + { + .compatible = "ti,dra7-pcie-ep", + .data = &dra7xx_pcie_ep_of_data, + }, + {}, +}; + +/* + * dra7xx_pcie_ep_unaligned_memaccess: workaround for AM572x/AM571x Errata i870 + * @dra7xx: the dra7xx device where the workaround should be applied + * + * Access to the PCIe slave port that are not 32-bit aligned will result + * in incorrect mapping to TLP Address and Byte enable fields. Therefore, + * byte and half-word accesses are not possible to byte offset 0x1, 0x2, or + * 0x3. + * + * To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1. + */ +static int dra7xx_pcie_ep_unaligned_memaccess(struct device *dev) +{ + int ret; + struct device_node *np = dev->of_node; + struct of_phandle_args args; + struct regmap *regmap; + + regmap = syscon_regmap_lookup_by_phandle(np, + "ti,syscon-unaligned-access"); + if (IS_ERR(regmap)) { + dev_dbg(dev, "can't get ti,syscon-unaligned-access\n"); + return -EINVAL; + } + + ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-unaligned-access", + 2, 0, &args); + if (ret) { + dev_err(dev, "failed to parse ti,syscon-unaligned-access\n"); + return ret; + } + + ret = regmap_update_bits(regmap, args.args[0], args.args[1], + args.args[1]); + if (ret) + dev_err(dev, "failed to enable unaligned access\n"); + + of_node_put(args.np); + + return ret; +} + +static int __init dra7xx_pcie_probe(struct platform_device *pdev) +{ + u32 reg; + int ret; + int irq; + int i; + int phy_count; + struct phy **phy; + struct device_link **link; + void __iomem *base; + struct resource *res; + struct dw_pcie *pci; + struct dra7xx_pcie *dra7xx; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + char name[10]; + struct gpio_desc *reset; + const struct of_device_id *match; + const struct dra7xx_pcie_of_data *data; + enum dw_pcie_device_mode mode; + + match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev); + if (!match) + return -EINVAL; + + data = (struct dra7xx_pcie_of_data *)match->data; + mode = (enum dw_pcie_device_mode)data->mode; + + dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); + if (!dra7xx) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "missing IRQ resource: %d\n", irq); + return irq; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf"); + base = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!base) + return -ENOMEM; + + phy_count = of_property_count_strings(np, "phy-names"); + if (phy_count < 0) { + dev_err(dev, "unable to find the strings\n"); + return phy_count; + } + + phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL); + if (!phy) + return -ENOMEM; + + link = devm_kzalloc(dev, sizeof(*link) * phy_count, GFP_KERNEL); + if (!link) + return -ENOMEM; + + for (i = 0; i < phy_count; i++) { + snprintf(name, sizeof(name), "pcie-phy%d", i); + phy[i] = devm_phy_get(dev, name); + if (IS_ERR(phy[i])) + return PTR_ERR(phy[i]); + + link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS); + if (!link[i]) { + ret = -EINVAL; + goto err_link; + } + } + + dra7xx->base = base; + dra7xx->phy = phy; + dra7xx->pci = pci; + dra7xx->phy_count = phy_count; + + ret = dra7xx_pcie_enable_phy(dra7xx); + if (ret) { + dev_err(dev, "failed to enable phy\n"); + return ret; + } + + platform_set_drvdata(pdev, dra7xx); + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync failed\n"); + goto err_get_sync; + } + + reset = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); + if (IS_ERR(reset)) { + ret = PTR_ERR(reset); + dev_err(&pdev->dev, "gpio request failed, ret %d\n", ret); + goto err_gpio; + } + + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); + reg &= ~LTSSM_EN; + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); + + dra7xx->link_gen = of_pci_get_max_link_speed(np); + if (dra7xx->link_gen < 0 || dra7xx->link_gen > 2) + dra7xx->link_gen = 2; + + switch (mode) { + case DW_PCIE_RC_TYPE: + if (!IS_ENABLED(CONFIG_PCI_DRA7XX_HOST)) { + ret = -ENODEV; + goto err_gpio; + } + + dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE, + DEVICE_TYPE_RC); + ret = dra7xx_add_pcie_port(dra7xx, pdev); + if (ret < 0) + goto err_gpio; + break; + case DW_PCIE_EP_TYPE: + if (!IS_ENABLED(CONFIG_PCI_DRA7XX_EP)) { + ret = -ENODEV; + goto err_gpio; + } + + dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE, + DEVICE_TYPE_EP); + + ret = dra7xx_pcie_ep_unaligned_memaccess(dev); + if (ret) + goto err_gpio; + + ret = dra7xx_add_pcie_ep(dra7xx, pdev); + if (ret < 0) + goto err_gpio; + break; + default: + dev_err(dev, "INVALID device type %d\n", mode); + } + dra7xx->mode = mode; + + ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler, + IRQF_SHARED, "dra7xx-pcie-main", dra7xx); + if (ret) { + dev_err(dev, "failed to request irq\n"); + goto err_gpio; + } + + return 0; + +err_gpio: + pm_runtime_put(dev); + +err_get_sync: + pm_runtime_disable(dev); + dra7xx_pcie_disable_phy(dra7xx); + +err_link: + while (--i >= 0) + device_link_del(link[i]); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int dra7xx_pcie_suspend(struct device *dev) +{ + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + struct dw_pcie *pci = dra7xx->pci; + u32 val; + + if (dra7xx->mode != DW_PCIE_RC_TYPE) + return 0; + + /* clear MSE */ + val = dw_pcie_readl_dbi(pci, PCI_COMMAND); + val &= ~PCI_COMMAND_MEMORY; + dw_pcie_writel_dbi(pci, PCI_COMMAND, val); + + return 0; +} + +static int dra7xx_pcie_resume(struct device *dev) +{ + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + struct dw_pcie *pci = dra7xx->pci; + u32 val; + + if (dra7xx->mode != DW_PCIE_RC_TYPE) + return 0; + + /* set MSE */ + val = dw_pcie_readl_dbi(pci, PCI_COMMAND); + val |= PCI_COMMAND_MEMORY; + dw_pcie_writel_dbi(pci, PCI_COMMAND, val); + + return 0; +} + +static int dra7xx_pcie_suspend_noirq(struct device *dev) +{ + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + + dra7xx_pcie_disable_phy(dra7xx); + + return 0; +} + +static int dra7xx_pcie_resume_noirq(struct device *dev) +{ + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + int ret; + + ret = dra7xx_pcie_enable_phy(dra7xx); + if (ret) { + dev_err(dev, "failed to enable phy\n"); + return ret; + } + + return 0; +} +#endif + +static void dra7xx_pcie_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + int ret; + + dra7xx_pcie_stop_link(dra7xx->pci); + + ret = pm_runtime_put_sync(dev); + if (ret < 0) + dev_dbg(dev, "pm_runtime_put_sync failed\n"); + + pm_runtime_disable(dev); + dra7xx_pcie_disable_phy(dra7xx); +} + +static const struct dev_pm_ops dra7xx_pcie_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend, dra7xx_pcie_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend_noirq, + dra7xx_pcie_resume_noirq) +}; + +static struct platform_driver dra7xx_pcie_driver = { + .driver = { + .name = "dra7-pcie", + .of_match_table = of_dra7xx_pcie_match, + .suppress_bind_attrs = true, + .pm = &dra7xx_pcie_pm_ops, + }, + .shutdown = dra7xx_pcie_shutdown, +}; +builtin_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe); diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c new file mode 100644 index 000000000000..4cc1e5df8c79 --- /dev/null +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Samsung EXYNOS SoCs + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define to_exynos_pcie(x) dev_get_drvdata((x)->dev) + +/* PCIe ELBI registers */ +#define PCIE_IRQ_PULSE 0x000 +#define IRQ_INTA_ASSERT BIT(0) +#define IRQ_INTB_ASSERT BIT(2) +#define IRQ_INTC_ASSERT BIT(4) +#define IRQ_INTD_ASSERT BIT(6) +#define PCIE_IRQ_LEVEL 0x004 +#define PCIE_IRQ_SPECIAL 0x008 +#define PCIE_IRQ_EN_PULSE 0x00c +#define PCIE_IRQ_EN_LEVEL 0x010 +#define IRQ_MSI_ENABLE BIT(2) +#define PCIE_IRQ_EN_SPECIAL 0x014 +#define PCIE_PWR_RESET 0x018 +#define PCIE_CORE_RESET 0x01c +#define PCIE_CORE_RESET_ENABLE BIT(0) +#define PCIE_STICKY_RESET 0x020 +#define PCIE_NONSTICKY_RESET 0x024 +#define PCIE_APP_INIT_RESET 0x028 +#define PCIE_APP_LTSSM_ENABLE 0x02c +#define PCIE_ELBI_RDLH_LINKUP 0x064 +#define PCIE_ELBI_LTSSM_ENABLE 0x1 +#define PCIE_ELBI_SLV_AWMISC 0x11c +#define PCIE_ELBI_SLV_ARMISC 0x120 +#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21) + +struct exynos_pcie_mem_res { + void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */ +}; + +struct exynos_pcie_clk_res { + struct clk *clk; + struct clk *bus_clk; +}; + +struct exynos_pcie { + struct dw_pcie *pci; + struct exynos_pcie_mem_res *mem_res; + struct exynos_pcie_clk_res *clk_res; + const struct exynos_pcie_ops *ops; + int reset_gpio; + + struct phy *phy; +}; + +struct exynos_pcie_ops { + int (*get_mem_resources)(struct platform_device *pdev, + struct exynos_pcie *ep); + int (*get_clk_resources)(struct exynos_pcie *ep); + int (*init_clk_resources)(struct exynos_pcie *ep); + void (*deinit_clk_resources)(struct exynos_pcie *ep); +}; + +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev, + struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct device *dev = pci->dev; + struct resource *res; + + ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL); + if (!ep->mem_res) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ep->mem_res->elbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ep->mem_res->elbi_base)) + return PTR_ERR(ep->mem_res->elbi_base); + + return 0; +} + +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct device *dev = pci->dev; + + ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL); + if (!ep->clk_res) + return -ENOMEM; + + ep->clk_res->clk = devm_clk_get(dev, "pcie"); + if (IS_ERR(ep->clk_res->clk)) { + dev_err(dev, "Failed to get pcie rc clock\n"); + return PTR_ERR(ep->clk_res->clk); + } + + ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus"); + if (IS_ERR(ep->clk_res->bus_clk)) { + dev_err(dev, "Failed to get pcie bus clock\n"); + return PTR_ERR(ep->clk_res->bus_clk); + } + + return 0; +} + +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct device *dev = pci->dev; + int ret; + + ret = clk_prepare_enable(ep->clk_res->clk); + if (ret) { + dev_err(dev, "cannot enable pcie rc clock"); + return ret; + } + + ret = clk_prepare_enable(ep->clk_res->bus_clk); + if (ret) { + dev_err(dev, "cannot enable pcie bus clock"); + goto err_bus_clk; + } + + return 0; + +err_bus_clk: + clk_disable_unprepare(ep->clk_res->clk); + + return ret; +} + +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep) +{ + clk_disable_unprepare(ep->clk_res->bus_clk); + clk_disable_unprepare(ep->clk_res->clk); +} + +static const struct exynos_pcie_ops exynos5440_pcie_ops = { + .get_mem_resources = exynos5440_pcie_get_mem_resources, + .get_clk_resources = exynos5440_pcie_get_clk_resources, + .init_clk_resources = exynos5440_pcie_init_clk_resources, + .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources, +}; + +static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg) +{ + writel(val, base + reg); +} + +static u32 exynos_pcie_readl(void __iomem *base, u32 reg) +{ + return readl(base + reg); +} + +static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC); + if (on) + val |= PCIE_ELBI_SLV_DBI_ENABLE; + else + val &= ~PCIE_ELBI_SLV_DBI_ENABLE; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC); +} + +static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC); + if (on) + val |= PCIE_ELBI_SLV_DBI_ENABLE; + else + val &= ~PCIE_ELBI_SLV_DBI_ENABLE; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC); +} + +static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET); + val &= ~PCIE_CORE_RESET_ENABLE; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET); +} + +static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET); + val |= PCIE_CORE_RESET_ENABLE; + + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET); +} + +static void exynos_pcie_assert_reset(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct device *dev = pci->dev; + + if (ep->reset_gpio >= 0) + devm_gpio_request_one(dev, ep->reset_gpio, + GPIOF_OUT_INIT_HIGH, "RESET"); +} + +static int exynos_pcie_establish_link(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; + + if (dw_pcie_link_up(pci)) { + dev_err(dev, "Link already up\n"); + return 0; + } + + exynos_pcie_assert_core_reset(ep); + + phy_reset(ep->phy); + + exynos_pcie_writel(ep->mem_res->elbi_base, 1, + PCIE_PWR_RESET); + + phy_power_on(ep->phy); + phy_init(ep->phy); + + exynos_pcie_deassert_core_reset(ep); + dw_pcie_setup_rc(pp); + exynos_pcie_assert_reset(ep); + + /* assert LTSSM enable */ + exynos_pcie_writel(ep->mem_res->elbi_base, PCIE_ELBI_LTSSM_ENABLE, + PCIE_APP_LTSSM_ENABLE); + + /* check if the link is up or not */ + if (!dw_pcie_wait_for_link(pci)) + return 0; + + phy_power_off(ep->phy); + return -ETIMEDOUT; +} + +static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE); + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE); +} + +static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep) +{ + u32 val; + + /* enable INTX interrupt */ + val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | + IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE); +} + +static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) +{ + struct exynos_pcie *ep = arg; + + exynos_pcie_clear_irq_pulse(ep); + return IRQ_HANDLED; +} + +static void exynos_pcie_msi_init(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct pcie_port *pp = &pci->pp; + u32 val; + + dw_pcie_msi_init(pp); + + /* enable MSI interrupt */ + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL); + val |= IRQ_MSI_ENABLE; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL); +} + +static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep) +{ + exynos_pcie_enable_irq_pulse(ep); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + exynos_pcie_msi_init(ep); +} + +static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size) +{ + struct exynos_pcie *ep = to_exynos_pcie(pci); + u32 val; + + exynos_pcie_sideband_dbi_r_mode(ep, true); + dw_pcie_read(base + reg, size, &val); + exynos_pcie_sideband_dbi_r_mode(ep, false); + return val; +} + +static void exynos_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size, u32 val) +{ + struct exynos_pcie *ep = to_exynos_pcie(pci); + + exynos_pcie_sideband_dbi_w_mode(ep, true); + dw_pcie_write(base + reg, size, val); + exynos_pcie_sideband_dbi_w_mode(ep, false); +} + +static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct exynos_pcie *ep = to_exynos_pcie(pci); + int ret; + + exynos_pcie_sideband_dbi_r_mode(ep, true); + ret = dw_pcie_read(pci->dbi_base + where, size, val); + exynos_pcie_sideband_dbi_r_mode(ep, false); + return ret; +} + +static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct exynos_pcie *ep = to_exynos_pcie(pci); + int ret; + + exynos_pcie_sideband_dbi_w_mode(ep, true); + ret = dw_pcie_write(pci->dbi_base + where, size, val); + exynos_pcie_sideband_dbi_w_mode(ep, false); + return ret; +} + +static int exynos_pcie_link_up(struct dw_pcie *pci) +{ + struct exynos_pcie *ep = to_exynos_pcie(pci); + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_RDLH_LINKUP); + if (val == PCIE_ELBI_LTSSM_ENABLE) + return 1; + + return 0; +} + +static int exynos_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct exynos_pcie *ep = to_exynos_pcie(pci); + + exynos_pcie_establish_link(ep); + exynos_pcie_enable_interrupts(ep); + + return 0; +} + +static const struct dw_pcie_host_ops exynos_pcie_host_ops = { + .rd_own_conf = exynos_pcie_rd_own_conf, + .wr_own_conf = exynos_pcie_wr_own_conf, + .host_init = exynos_pcie_host_init, +}; + +static int __init exynos_add_pcie_port(struct exynos_pcie *ep, + struct platform_device *pdev) +{ + struct dw_pcie *pci = ep->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + pp->irq = platform_get_irq(pdev, 1); + if (pp->irq < 0) { + dev_err(dev, "failed to get irq\n"); + return pp->irq; + } + ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler, + IRQF_SHARED, "exynos-pcie", ep); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (pp->msi_irq < 0) { + dev_err(dev, "failed to get msi irq\n"); + return pp->msi_irq; + } + } + + pp->root_bus_nr = -1; + pp->ops = &exynos_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .read_dbi = exynos_pcie_read_dbi, + .write_dbi = exynos_pcie_write_dbi, + .link_up = exynos_pcie_link_up, +}; + +static int __init exynos_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct exynos_pcie *ep; + struct device_node *np = dev->of_node; + int ret; + + ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + ep->pci = pci; + ep->ops = (const struct exynos_pcie_ops *) + of_device_get_match_data(dev); + + ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + + ep->phy = devm_of_phy_get(dev, np, NULL); + if (IS_ERR(ep->phy)) { + if (PTR_ERR(ep->phy) == -EPROBE_DEFER) + return PTR_ERR(ep->phy); + + ep->phy = NULL; + } + + if (ep->ops && ep->ops->get_mem_resources) { + ret = ep->ops->get_mem_resources(pdev, ep); + if (ret) + return ret; + } + + if (ep->ops && ep->ops->get_clk_resources && + ep->ops->init_clk_resources) { + ret = ep->ops->get_clk_resources(ep); + if (ret) + return ret; + ret = ep->ops->init_clk_resources(ep); + if (ret) + return ret; + } + + platform_set_drvdata(pdev, ep); + + ret = exynos_add_pcie_port(ep, pdev); + if (ret < 0) + goto fail_probe; + + return 0; + +fail_probe: + phy_exit(ep->phy); + + if (ep->ops && ep->ops->deinit_clk_resources) + ep->ops->deinit_clk_resources(ep); + return ret; +} + +static int __exit exynos_pcie_remove(struct platform_device *pdev) +{ + struct exynos_pcie *ep = platform_get_drvdata(pdev); + + if (ep->ops && ep->ops->deinit_clk_resources) + ep->ops->deinit_clk_resources(ep); + + return 0; +} + +static const struct of_device_id exynos_pcie_of_match[] = { + { + .compatible = "samsung,exynos5440-pcie", + .data = &exynos5440_pcie_ops + }, + {}, +}; + +static struct platform_driver exynos_pcie_driver = { + .remove = __exit_p(exynos_pcie_remove), + .driver = { + .name = "exynos-pcie", + .of_match_table = exynos_pcie_of_match, + }, +}; + +/* Exynos PCIe driver does not allow module unload */ + +static int __init exynos_pcie_init(void) +{ + return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe); +} +subsys_initcall(exynos_pcie_init); diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c new file mode 100644 index 000000000000..80f604602783 --- /dev/null +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -0,0 +1,871 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Freescale i.MX6 SoCs + * + * Copyright (C) 2013 Kosagi + * http://www.kosagi.com + * + * Author: Sean Cross + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define to_imx6_pcie(x) dev_get_drvdata((x)->dev) + +enum imx6_pcie_variants { + IMX6Q, + IMX6SX, + IMX6QP, + IMX7D, +}; + +struct imx6_pcie { + struct dw_pcie *pci; + int reset_gpio; + bool gpio_active_high; + struct clk *pcie_bus; + struct clk *pcie_phy; + struct clk *pcie_inbound_axi; + struct clk *pcie; + struct regmap *iomuxc_gpr; + struct reset_control *pciephy_reset; + struct reset_control *apps_reset; + enum imx6_pcie_variants variant; + u32 tx_deemph_gen1; + u32 tx_deemph_gen2_3p5db; + u32 tx_deemph_gen2_6db; + u32 tx_swing_full; + u32 tx_swing_low; + int link_gen; + struct regulator *vpcie; +}; + +/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ +#define PHY_PLL_LOCK_WAIT_MAX_RETRIES 2000 +#define PHY_PLL_LOCK_WAIT_USLEEP_MIN 50 +#define PHY_PLL_LOCK_WAIT_USLEEP_MAX 200 + +/* PCIe Root Complex registers (memory-mapped) */ +#define PCIE_RC_LCR 0x7c +#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1 +#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2 +#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf + +#define PCIE_RC_LCSR 0x80 + +/* PCIe Port Logic registers (memory-mapped) */ +#define PL_OFFSET 0x700 +#define PCIE_PL_PFLR (PL_OFFSET + 0x08) +#define PCIE_PL_PFLR_LINK_STATE_MASK (0x3f << 16) +#define PCIE_PL_PFLR_FORCE_LINK (1 << 15) +#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) +#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29) +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP (1 << 4) + +#define PCIE_PHY_CTRL (PL_OFFSET + 0x114) +#define PCIE_PHY_CTRL_DATA_LOC 0 +#define PCIE_PHY_CTRL_CAP_ADR_LOC 16 +#define PCIE_PHY_CTRL_CAP_DAT_LOC 17 +#define PCIE_PHY_CTRL_WR_LOC 18 +#define PCIE_PHY_CTRL_RD_LOC 19 + +#define PCIE_PHY_STAT (PL_OFFSET + 0x110) +#define PCIE_PHY_STAT_ACK_LOC 16 + +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) + +/* PHY registers (not memory-mapped) */ +#define PCIE_PHY_RX_ASIC_OUT 0x100D +#define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0) + +#define PHY_RX_OVRD_IN_LO 0x1005 +#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) +#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) + +static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val) +{ + struct dw_pcie *pci = imx6_pcie->pci; + u32 val; + u32 max_iterations = 10; + u32 wait_counter = 0; + + do { + val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); + val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; + wait_counter++; + + if (val == exp_val) + return 0; + + udelay(1); + } while (wait_counter < max_iterations); + + return -ETIMEDOUT; +} + +static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr) +{ + struct dw_pcie *pci = imx6_pcie->pci; + u32 val; + int ret; + + val = addr << PCIE_PHY_CTRL_DATA_LOC; + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); + + val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); + + ret = pcie_phy_poll_ack(imx6_pcie, 1); + if (ret) + return ret; + + val = addr << PCIE_PHY_CTRL_DATA_LOC; + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); + + return pcie_phy_poll_ack(imx6_pcie, 0); +} + +/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ +static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data) +{ + struct dw_pcie *pci = imx6_pcie->pci; + u32 val, phy_ctl; + int ret; + + ret = pcie_phy_wait_ack(imx6_pcie, addr); + if (ret) + return ret; + + /* assert Read signal */ + phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, phy_ctl); + + ret = pcie_phy_poll_ack(imx6_pcie, 1); + if (ret) + return ret; + + val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); + *data = val & 0xffff; + + /* deassert Read signal */ + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x00); + + return pcie_phy_poll_ack(imx6_pcie, 0); +} + +static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) +{ + struct dw_pcie *pci = imx6_pcie->pci; + u32 var; + int ret; + + /* write addr */ + /* cap addr */ + ret = pcie_phy_wait_ack(imx6_pcie, addr); + if (ret) + return ret; + + var = data << PCIE_PHY_CTRL_DATA_LOC; + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); + + /* capture data */ + var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); + + ret = pcie_phy_poll_ack(imx6_pcie, 1); + if (ret) + return ret; + + /* deassert cap data */ + var = data << PCIE_PHY_CTRL_DATA_LOC; + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); + + /* wait for ack de-assertion */ + ret = pcie_phy_poll_ack(imx6_pcie, 0); + if (ret) + return ret; + + /* assert wr signal */ + var = 0x1 << PCIE_PHY_CTRL_WR_LOC; + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); + + /* wait for ack */ + ret = pcie_phy_poll_ack(imx6_pcie, 1); + if (ret) + return ret; + + /* deassert wr signal */ + var = data << PCIE_PHY_CTRL_DATA_LOC; + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); + + /* wait for ack de-assertion */ + ret = pcie_phy_poll_ack(imx6_pcie, 0); + if (ret) + return ret; + + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x0); + + return 0; +} + +static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) +{ + u32 tmp; + + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); + tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | + PHY_RX_OVRD_IN_LO_RX_PLL_EN); + pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); + + usleep_range(2000, 3000); + + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); + tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | + PHY_RX_OVRD_IN_LO_RX_PLL_EN); + pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); +} + +/* Added for PCI abort handling */ +static int imx6q_pcie_abort_handler(unsigned long addr, + unsigned int fsr, struct pt_regs *regs) +{ + unsigned long pc = instruction_pointer(regs); + unsigned long instr = *(unsigned long *)pc; + int reg = (instr >> 12) & 15; + + /* + * If the instruction being executed was a read, + * make it look like it read all-ones. + */ + if ((instr & 0x0c100000) == 0x04100000) { + unsigned long val; + + if (instr & 0x00400000) + val = 255; + else + val = -1; + + regs->uregs[reg] = val; + regs->ARM_pc += 4; + return 0; + } + + if ((instr & 0x0e100090) == 0x00100090) { + regs->uregs[reg] = -1; + regs->ARM_pc += 4; + return 0; + } + + return 1; +} + +static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) +{ + struct device *dev = imx6_pcie->pci->dev; + + switch (imx6_pcie->variant) { + case IMX7D: + reset_control_assert(imx6_pcie->pciephy_reset); + reset_control_assert(imx6_pcie->apps_reset); + break; + case IMX6SX: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_TEST_POWERDOWN, + IMX6SX_GPR12_PCIE_TEST_POWERDOWN); + /* Force PCIe PHY reset */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, + IMX6SX_GPR5_PCIE_BTNRST_RESET, + IMX6SX_GPR5_PCIE_BTNRST_RESET); + break; + case IMX6QP: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_SW_RST, + IMX6Q_GPR1_PCIE_SW_RST); + break; + case IMX6Q: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); + break; + } + + if (imx6_pcie->vpcie && regulator_is_enabled(imx6_pcie->vpcie) > 0) { + int ret = regulator_disable(imx6_pcie->vpcie); + + if (ret) + dev_err(dev, "failed to disable vpcie regulator: %d\n", + ret); + } +} + +static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) +{ + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; + int ret = 0; + + switch (imx6_pcie->variant) { + case IMX6SX: + ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi); + if (ret) { + dev_err(dev, "unable to enable pcie_axi clock\n"); + break; + } + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0); + break; + case IMX6QP: /* FALLTHROUGH */ + case IMX6Q: + /* power up core phy and enable ref clock */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18); + /* + * the async reset input need ref clock to sync internally, + * when the ref clock comes after reset, internal synced + * reset time is too short, cannot meet the requirement. + * add one ~10us delay here. + */ + udelay(10); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); + break; + case IMX7D: + break; + } + + return ret; +} + +static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) +{ + u32 val; + unsigned int retries; + struct device *dev = imx6_pcie->pci->dev; + + for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) { + regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR22, &val); + + if (val & IMX7D_GPR22_PCIE_PHY_PLL_LOCKED) + return; + + usleep_range(PHY_PLL_LOCK_WAIT_USLEEP_MIN, + PHY_PLL_LOCK_WAIT_USLEEP_MAX); + } + + dev_err(dev, "PCIe PLL lock timeout\n"); +} + +static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) +{ + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; + int ret; + + if (imx6_pcie->vpcie && !regulator_is_enabled(imx6_pcie->vpcie)) { + ret = regulator_enable(imx6_pcie->vpcie); + if (ret) { + dev_err(dev, "failed to enable vpcie regulator: %d\n", + ret); + return; + } + } + + ret = clk_prepare_enable(imx6_pcie->pcie_phy); + if (ret) { + dev_err(dev, "unable to enable pcie_phy clock\n"); + goto err_pcie_phy; + } + + ret = clk_prepare_enable(imx6_pcie->pcie_bus); + if (ret) { + dev_err(dev, "unable to enable pcie_bus clock\n"); + goto err_pcie_bus; + } + + ret = clk_prepare_enable(imx6_pcie->pcie); + if (ret) { + dev_err(dev, "unable to enable pcie clock\n"); + goto err_pcie; + } + + ret = imx6_pcie_enable_ref_clk(imx6_pcie); + if (ret) { + dev_err(dev, "unable to enable pcie ref clock\n"); + goto err_ref_clk; + } + + /* allow the clocks to stabilize */ + usleep_range(200, 500); + + /* Some boards don't have PCIe reset GPIO. */ + if (gpio_is_valid(imx6_pcie->reset_gpio)) { + gpio_set_value_cansleep(imx6_pcie->reset_gpio, + imx6_pcie->gpio_active_high); + msleep(100); + gpio_set_value_cansleep(imx6_pcie->reset_gpio, + !imx6_pcie->gpio_active_high); + } + + switch (imx6_pcie->variant) { + case IMX7D: + reset_control_deassert(imx6_pcie->pciephy_reset); + imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); + break; + case IMX6SX: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, + IMX6SX_GPR5_PCIE_BTNRST_RESET, 0); + break; + case IMX6QP: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_SW_RST, 0); + + usleep_range(200, 500); + break; + case IMX6Q: /* Nothing to do */ + break; + } + + return; + +err_ref_clk: + clk_disable_unprepare(imx6_pcie->pcie); +err_pcie: + clk_disable_unprepare(imx6_pcie->pcie_bus); +err_pcie_bus: + clk_disable_unprepare(imx6_pcie->pcie_phy); +err_pcie_phy: + if (imx6_pcie->vpcie && regulator_is_enabled(imx6_pcie->vpcie) > 0) { + ret = regulator_disable(imx6_pcie->vpcie); + if (ret) + dev_err(dev, "failed to disable vpcie regulator: %d\n", + ret); + } +} + +static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) +{ + switch (imx6_pcie->variant) { + case IMX7D: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); + break; + case IMX6SX: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_RX_EQ_MASK, + IMX6SX_GPR12_PCIE_RX_EQ_2); + /* FALLTHROUGH */ + default: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); + + /* configure constant input signal to the pcie ctrl and phy */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_LOS_LEVEL, 9 << 4); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN1, + imx6_pcie->tx_deemph_gen1 << 0); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, + imx6_pcie->tx_deemph_gen2_3p5db << 6); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, + imx6_pcie->tx_deemph_gen2_6db << 12); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_SWING_FULL, + imx6_pcie->tx_swing_full << 18); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_SWING_LOW, + imx6_pcie->tx_swing_low << 25); + break; + } + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12); +} + +static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) +{ + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; + + /* check if the link is up or not */ + if (!dw_pcie_wait_for_link(pci)) + return 0; + + dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); + return -ETIMEDOUT; +} + +static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) +{ + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; + u32 tmp; + unsigned int retries; + + for (retries = 0; retries < 200; retries++) { + tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + /* Test if the speed change finished. */ + if (!(tmp & PORT_LOGIC_SPEED_CHANGE)) + return 0; + usleep_range(100, 1000); + } + + dev_err(dev, "Speed change timeout\n"); + return -EINVAL; +} + +static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) +{ + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; + u32 tmp; + int ret; + + /* + * Force Gen1 operation when starting the link. In case the link is + * started in Gen2 mode, there is a possibility the devices on the + * bus will not be detected at all. This happens with PCIe switches. + */ + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); + tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; + tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; + dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); + + /* Start LTSSM. */ + if (imx6_pcie->variant == IMX7D) + reset_control_deassert(imx6_pcie->apps_reset); + else + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); + + ret = imx6_pcie_wait_for_link(imx6_pcie); + if (ret) + goto err_reset_phy; + + if (imx6_pcie->link_gen == 2) { + /* Allow Gen2 mode after the link is up. */ + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); + tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; + tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; + dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); + + /* + * Start Directed Speed Change so the best possible + * speed both link partners support can be negotiated. + */ + tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); + + if (imx6_pcie->variant != IMX7D) { + /* + * On i.MX7, DIRECT_SPEED_CHANGE behaves differently + * from i.MX6 family when no link speed transition + * occurs and we go Gen1 -> yep, Gen1. The difference + * is that, in such case, it will not be cleared by HW + * which will cause the following code to report false + * failure. + */ + + ret = imx6_pcie_wait_for_speed_change(imx6_pcie); + if (ret) { + dev_err(dev, "Failed to bring link up!\n"); + goto err_reset_phy; + } + } + + /* Make sure link training is finished as well! */ + ret = imx6_pcie_wait_for_link(imx6_pcie); + if (ret) { + dev_err(dev, "Failed to bring link up!\n"); + goto err_reset_phy; + } + } else { + dev_info(dev, "Link: Gen2 disabled\n"); + } + + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR); + dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf); + return 0; + +err_reset_phy: + dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n", + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); + imx6_pcie_reset_phy(imx6_pcie); + return ret; +} + +static int imx6_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); + + imx6_pcie_assert_core_reset(imx6_pcie); + imx6_pcie_init_phy(imx6_pcie); + imx6_pcie_deassert_core_reset(imx6_pcie); + dw_pcie_setup_rc(pp); + imx6_pcie_establish_link(imx6_pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + + return 0; +} + +static int imx6_pcie_link_up(struct dw_pcie *pci) +{ + return dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1) & + PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; +} + +static const struct dw_pcie_host_ops imx6_pcie_host_ops = { + .host_init = imx6_pcie_host_init, +}; + +static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = imx6_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pp->msi_irq <= 0) { + dev_err(dev, "failed to get MSI irq\n"); + return -ENODEV; + } + } + + pp->root_bus_nr = -1; + pp->ops = &imx6_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = imx6_pcie_link_up, +}; + +static int imx6_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct imx6_pcie *imx6_pcie; + struct resource *dbi_base; + struct device_node *node = dev->of_node; + int ret; + + imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL); + if (!imx6_pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + imx6_pcie->pci = pci; + imx6_pcie->variant = + (enum imx6_pcie_variants)of_device_get_match_data(dev); + + dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pci->dbi_base = devm_ioremap_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + /* Fetch GPIOs */ + imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0); + imx6_pcie->gpio_active_high = of_property_read_bool(node, + "reset-gpio-active-high"); + if (gpio_is_valid(imx6_pcie->reset_gpio)) { + ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio, + imx6_pcie->gpio_active_high ? + GPIOF_OUT_INIT_HIGH : + GPIOF_OUT_INIT_LOW, + "PCIe reset"); + if (ret) { + dev_err(dev, "unable to get reset gpio\n"); + return ret; + } + } else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) { + return imx6_pcie->reset_gpio; + } + + /* Fetch clocks */ + imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy"); + if (IS_ERR(imx6_pcie->pcie_phy)) { + dev_err(dev, "pcie_phy clock source missing or invalid\n"); + return PTR_ERR(imx6_pcie->pcie_phy); + } + + imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus"); + if (IS_ERR(imx6_pcie->pcie_bus)) { + dev_err(dev, "pcie_bus clock source missing or invalid\n"); + return PTR_ERR(imx6_pcie->pcie_bus); + } + + imx6_pcie->pcie = devm_clk_get(dev, "pcie"); + if (IS_ERR(imx6_pcie->pcie)) { + dev_err(dev, "pcie clock source missing or invalid\n"); + return PTR_ERR(imx6_pcie->pcie); + } + + switch (imx6_pcie->variant) { + case IMX6SX: + imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, + "pcie_inbound_axi"); + if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { + dev_err(dev, "pcie_inbound_axi clock missing or invalid\n"); + return PTR_ERR(imx6_pcie->pcie_inbound_axi); + } + break; + case IMX7D: + imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev, + "pciephy"); + if (IS_ERR(imx6_pcie->pciephy_reset)) { + dev_err(dev, "Failed to get PCIEPHY reset control\n"); + return PTR_ERR(imx6_pcie->pciephy_reset); + } + + imx6_pcie->apps_reset = devm_reset_control_get_exclusive(dev, + "apps"); + if (IS_ERR(imx6_pcie->apps_reset)) { + dev_err(dev, "Failed to get PCIE APPS reset control\n"); + return PTR_ERR(imx6_pcie->apps_reset); + } + break; + default: + break; + } + + /* Grab GPR config register range */ + imx6_pcie->iomuxc_gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imx6_pcie->iomuxc_gpr)) { + dev_err(dev, "unable to find iomuxc registers\n"); + return PTR_ERR(imx6_pcie->iomuxc_gpr); + } + + /* Grab PCIe PHY Tx Settings */ + if (of_property_read_u32(node, "fsl,tx-deemph-gen1", + &imx6_pcie->tx_deemph_gen1)) + imx6_pcie->tx_deemph_gen1 = 0; + + if (of_property_read_u32(node, "fsl,tx-deemph-gen2-3p5db", + &imx6_pcie->tx_deemph_gen2_3p5db)) + imx6_pcie->tx_deemph_gen2_3p5db = 0; + + if (of_property_read_u32(node, "fsl,tx-deemph-gen2-6db", + &imx6_pcie->tx_deemph_gen2_6db)) + imx6_pcie->tx_deemph_gen2_6db = 20; + + if (of_property_read_u32(node, "fsl,tx-swing-full", + &imx6_pcie->tx_swing_full)) + imx6_pcie->tx_swing_full = 127; + + if (of_property_read_u32(node, "fsl,tx-swing-low", + &imx6_pcie->tx_swing_low)) + imx6_pcie->tx_swing_low = 127; + + /* Limit link speed */ + ret = of_property_read_u32(node, "fsl,max-link-speed", + &imx6_pcie->link_gen); + if (ret) + imx6_pcie->link_gen = 1; + + imx6_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie"); + if (IS_ERR(imx6_pcie->vpcie)) { + if (PTR_ERR(imx6_pcie->vpcie) == -EPROBE_DEFER) + return -EPROBE_DEFER; + imx6_pcie->vpcie = NULL; + } + + platform_set_drvdata(pdev, imx6_pcie); + + ret = imx6_add_pcie_port(imx6_pcie, pdev); + if (ret < 0) + return ret; + + return 0; +} + +static void imx6_pcie_shutdown(struct platform_device *pdev) +{ + struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev); + + /* bring down link, so bootloader gets clean state in case of reboot */ + imx6_pcie_assert_core_reset(imx6_pcie); +} + +static const struct of_device_id imx6_pcie_of_match[] = { + { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, }, + { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, }, + { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, + { .compatible = "fsl,imx7d-pcie", .data = (void *)IMX7D, }, + {}, +}; + +static struct platform_driver imx6_pcie_driver = { + .driver = { + .name = "imx6q-pcie", + .of_match_table = imx6_pcie_of_match, + .suppress_bind_attrs = true, + }, + .probe = imx6_pcie_probe, + .shutdown = imx6_pcie_shutdown, +}; + +static int __init imx6_pcie_init(void) +{ + /* + * Since probe() can be deferred we need to make sure that + * hook_fault_code is not called after __init memory is freed + * by kernel and since imx6q_pcie_abort_handler() is a no-op, + * we can install the handler here without risking it + * accessing some uninitialized driver state. + */ + hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0, + "external abort on non-linefetch"); + + return platform_driver_register(&imx6_pcie_driver); +} +device_initcall(imx6_pcie_init); diff --git a/drivers/pci/controller/dwc/pci-keystone-dw.c b/drivers/pci/controller/dwc/pci-keystone-dw.c new file mode 100644 index 000000000000..0682213328e9 --- /dev/null +++ b/drivers/pci/controller/dwc/pci-keystone-dw.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DesignWare application register space functions for Keystone PCI controller + * + * Copyright (C) 2013-2014 Texas Instruments., Ltd. + * http://www.ti.com + * + * Author: Murali Karicheri + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" +#include "pci-keystone.h" + +/* Application register defines */ +#define LTSSM_EN_VAL 1 +#define LTSSM_STATE_MASK 0x1f +#define LTSSM_STATE_L0 0x11 +#define DBI_CS2_EN_VAL 0x20 +#define OB_XLAT_EN_VAL 2 + +/* Application registers */ +#define CMD_STATUS 0x004 +#define CFG_SETUP 0x008 +#define OB_SIZE 0x030 +#define CFG_PCIM_WIN_SZ_IDX 3 +#define CFG_PCIM_WIN_CNT 32 +#define SPACE0_REMOTE_CFG_OFFSET 0x1000 +#define OB_OFFSET_INDEX(n) (0x200 + (8 * n)) +#define OB_OFFSET_HI(n) (0x204 + (8 * n)) + +/* IRQ register defines */ +#define IRQ_EOI 0x050 +#define IRQ_STATUS 0x184 +#define IRQ_ENABLE_SET 0x188 +#define IRQ_ENABLE_CLR 0x18c + +#define MSI_IRQ 0x054 +#define MSI0_IRQ_STATUS 0x104 +#define MSI0_IRQ_ENABLE_SET 0x108 +#define MSI0_IRQ_ENABLE_CLR 0x10c +#define IRQ_STATUS 0x184 +#define MSI_IRQ_OFFSET 4 + +/* Error IRQ bits */ +#define ERR_AER BIT(5) /* ECRC error */ +#define ERR_AXI BIT(4) /* AXI tag lookup fatal error */ +#define ERR_CORR BIT(3) /* Correctable error */ +#define ERR_NONFATAL BIT(2) /* Non-fatal error */ +#define ERR_FATAL BIT(1) /* Fatal error */ +#define ERR_SYS BIT(0) /* System (fatal, non-fatal, or correctable) */ +#define ERR_IRQ_ALL (ERR_AER | ERR_AXI | ERR_CORR | \ + ERR_NONFATAL | ERR_FATAL | ERR_SYS) +#define ERR_FATAL_IRQ (ERR_FATAL | ERR_AXI) +#define ERR_IRQ_STATUS_RAW 0x1c0 +#define ERR_IRQ_STATUS 0x1c4 +#define ERR_IRQ_ENABLE_SET 0x1c8 +#define ERR_IRQ_ENABLE_CLR 0x1cc + +/* Config space registers */ +#define DEBUG0 0x728 + +#define to_keystone_pcie(x) dev_get_drvdata((x)->dev) + +static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset, + u32 *bit_pos) +{ + *reg_offset = offset % 8; + *bit_pos = offset >> 3; +} + +phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); + + return ks_pcie->app.start + MSI_IRQ; +} + +static u32 ks_dw_app_readl(struct keystone_pcie *ks_pcie, u32 offset) +{ + return readl(ks_pcie->va_app_base + offset); +} + +static void ks_dw_app_writel(struct keystone_pcie *ks_pcie, u32 offset, u32 val) +{ + writel(val, ks_pcie->va_app_base + offset); +} + +void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset) +{ + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; + u32 pending, vector; + int src, virq; + + pending = ks_dw_app_readl(ks_pcie, MSI0_IRQ_STATUS + (offset << 4)); + + /* + * MSI0 status bit 0-3 shows vectors 0, 8, 16, 24, MSI1 status bit + * shows 1, 9, 17, 25 and so forth + */ + for (src = 0; src < 4; src++) { + if (BIT(src) & pending) { + vector = offset + (src << 3); + virq = irq_linear_revmap(pp->irq_domain, vector); + dev_dbg(dev, "irq: bit %d, vector %d, virq %d\n", + src, vector, virq); + generic_handle_irq(virq); + } + } +} + +void ks_dw_pcie_msi_irq_ack(int irq, struct pcie_port *pp) +{ + u32 reg_offset, bit_pos; + struct keystone_pcie *ks_pcie; + struct dw_pcie *pci; + + pci = to_dw_pcie_from_pp(pp); + ks_pcie = to_keystone_pcie(pci); + update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); + + ks_dw_app_writel(ks_pcie, MSI0_IRQ_STATUS + (reg_offset << 4), + BIT(bit_pos)); + ks_dw_app_writel(ks_pcie, IRQ_EOI, reg_offset + MSI_IRQ_OFFSET); +} + +void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) +{ + u32 reg_offset, bit_pos; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); + + update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); + ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_SET + (reg_offset << 4), + BIT(bit_pos)); +} + +void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) +{ + u32 reg_offset, bit_pos; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); + + update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); + ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_CLR + (reg_offset << 4), + BIT(bit_pos)); +} + +int ks_dw_pcie_msi_host_init(struct pcie_port *pp) +{ + return dw_pcie_allocate_domains(pp); +} + +void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie) +{ + int i; + + for (i = 0; i < PCI_NUM_INTX; i++) + ks_dw_app_writel(ks_pcie, IRQ_ENABLE_SET + (i << 4), 0x1); +} + +void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset) +{ + struct dw_pcie *pci = ks_pcie->pci; + struct device *dev = pci->dev; + u32 pending; + int virq; + + pending = ks_dw_app_readl(ks_pcie, IRQ_STATUS + (offset << 4)); + + if (BIT(0) & pending) { + virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, offset); + dev_dbg(dev, ": irq: irq_offset %d, virq %d\n", offset, virq); + generic_handle_irq(virq); + } + + /* EOI the INTx interrupt */ + ks_dw_app_writel(ks_pcie, IRQ_EOI, offset); +} + +void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie) +{ + ks_dw_app_writel(ks_pcie, ERR_IRQ_ENABLE_SET, ERR_IRQ_ALL); +} + +irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie) +{ + u32 status; + + status = ks_dw_app_readl(ks_pcie, ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL; + if (!status) + return IRQ_NONE; + + if (status & ERR_FATAL_IRQ) + dev_err(ks_pcie->pci->dev, "fatal error (status %#010x)\n", + status); + + /* Ack the IRQ; status bits are RW1C */ + ks_dw_app_writel(ks_pcie, ERR_IRQ_STATUS, status); + return IRQ_HANDLED; +} + +static void ks_dw_pcie_ack_legacy_irq(struct irq_data *d) +{ +} + +static void ks_dw_pcie_mask_legacy_irq(struct irq_data *d) +{ +} + +static void ks_dw_pcie_unmask_legacy_irq(struct irq_data *d) +{ +} + +static struct irq_chip ks_dw_pcie_legacy_irq_chip = { + .name = "Keystone-PCI-Legacy-IRQ", + .irq_ack = ks_dw_pcie_ack_legacy_irq, + .irq_mask = ks_dw_pcie_mask_legacy_irq, + .irq_unmask = ks_dw_pcie_unmask_legacy_irq, +}; + +static int ks_dw_pcie_init_legacy_irq_map(struct irq_domain *d, + unsigned int irq, irq_hw_number_t hw_irq) +{ + irq_set_chip_and_handler(irq, &ks_dw_pcie_legacy_irq_chip, + handle_level_irq); + irq_set_chip_data(irq, d->host_data); + + return 0; +} + +static const struct irq_domain_ops ks_dw_pcie_legacy_irq_domain_ops = { + .map = ks_dw_pcie_init_legacy_irq_map, + .xlate = irq_domain_xlate_onetwocell, +}; + +/** + * ks_dw_pcie_set_dbi_mode() - Set DBI mode to access overlaid BAR mask + * registers + * + * Since modification of dbi_cs2 involves different clock domain, read the + * status back to ensure the transition is complete. + */ +static void ks_dw_pcie_set_dbi_mode(struct keystone_pcie *ks_pcie) +{ + u32 val; + + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, DBI_CS2_EN_VAL | val); + + do { + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + } while (!(val & DBI_CS2_EN_VAL)); +} + +/** + * ks_dw_pcie_clear_dbi_mode() - Disable DBI mode + * + * Since modification of dbi_cs2 involves different clock domain, read the + * status back to ensure the transition is complete. + */ +static void ks_dw_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie) +{ + u32 val; + + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, ~DBI_CS2_EN_VAL & val); + + do { + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + } while (val & DBI_CS2_EN_VAL); +} + +void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) +{ + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + u32 start = pp->mem->start, end = pp->mem->end; + int i, tr_size; + u32 val; + + /* Disable BARs for inbound access */ + ks_dw_pcie_set_dbi_mode(ks_pcie); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0); + ks_dw_pcie_clear_dbi_mode(ks_pcie); + + /* Set outbound translation size per window division */ + ks_dw_app_writel(ks_pcie, OB_SIZE, CFG_PCIM_WIN_SZ_IDX & 0x7); + + tr_size = (1 << (CFG_PCIM_WIN_SZ_IDX & 0x7)) * SZ_1M; + + /* Using Direct 1:1 mapping of RC <-> PCI memory space */ + for (i = 0; (i < CFG_PCIM_WIN_CNT) && (start < end); i++) { + ks_dw_app_writel(ks_pcie, OB_OFFSET_INDEX(i), start | 1); + ks_dw_app_writel(ks_pcie, OB_OFFSET_HI(i), 0); + start += tr_size; + } + + /* Enable OB translation */ + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, OB_XLAT_EN_VAL | val); +} + +/** + * ks_pcie_cfg_setup() - Set up configuration space address for a device + * + * @ks_pcie: ptr to keystone_pcie structure + * @bus: Bus number the device is residing on + * @devfn: device, function number info + * + * Forms and returns the address of configuration space mapped in PCIESS + * address space 0. Also configures CFG_SETUP for remote configuration space + * access. + * + * The address space has two regions to access configuration - local and remote. + * We access local region for bus 0 (as RC is attached on bus 0) and remote + * region for others with TYPE 1 access when bus > 1. As for device on bus = 1, + * we will do TYPE 0 access as it will be on our secondary bus (logical). + * CFG_SETUP is needed only for remote configuration access. + */ +static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus, + unsigned int devfn) +{ + u8 device = PCI_SLOT(devfn), function = PCI_FUNC(devfn); + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + u32 regval; + + if (bus == 0) + return pci->dbi_base; + + regval = (bus << 16) | (device << 8) | function; + + /* + * Since Bus#1 will be a virtual bus, we need to have TYPE0 + * access only. + * TYPE 1 + */ + if (bus != 1) + regval |= BIT(24); + + ks_dw_app_writel(ks_pcie, CFG_SETUP, regval); + return pp->va_cfg0_base; +} + +int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); + u8 bus_num = bus->number; + void __iomem *addr; + + addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); + + return dw_pcie_read(addr + where, size, val); +} + +int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); + u8 bus_num = bus->number; + void __iomem *addr; + + addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); + + return dw_pcie_write(addr + where, size, val); +} + +/** + * ks_dw_pcie_v3_65_scan_bus() - keystone scan_bus post initialization + * + * This sets BAR0 to enable inbound access for MSI_IRQ register + */ +void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); + + /* Configure and set up BAR0 */ + ks_dw_pcie_set_dbi_mode(ks_pcie); + + /* Enable BAR0 */ + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 1); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, SZ_4K - 1); + + ks_dw_pcie_clear_dbi_mode(ks_pcie); + + /* + * For BAR0, just setting bus address for inbound writes (MSI) should + * be sufficient. Use physical address to avoid any conflicts. + */ + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, ks_pcie->app.start); +} + +/** + * ks_dw_pcie_link_up() - Check if link up + */ +int ks_dw_pcie_link_up(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, DEBUG0); + return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0; +} + +void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie) +{ + u32 val; + + /* Disable Link training */ + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + val &= ~LTSSM_EN_VAL; + ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); + + /* Initiate Link Training */ + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); +} + +/** + * ks_dw_pcie_host_init() - initialize host for v3_65 dw hardware + * + * Ioremap the register resources, initialize legacy irq domain + * and call dw_pcie_v3_65_host_init() API to initialize the Keystone + * PCI host controller. + */ +int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, + struct device_node *msi_intc_np) +{ + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + + /* Index 0 is the config reg. space address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pci->dbi_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + /* + * We set these same and is used in pcie rd/wr_other_conf + * functions + */ + pp->va_cfg0_base = pci->dbi_base + SPACE0_REMOTE_CFG_OFFSET; + pp->va_cfg1_base = pp->va_cfg0_base; + + /* Index 1 is the application reg. space address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + ks_pcie->va_app_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ks_pcie->va_app_base)) + return PTR_ERR(ks_pcie->va_app_base); + + ks_pcie->app = *res; + + /* Create legacy IRQ domain */ + ks_pcie->legacy_irq_domain = + irq_domain_add_linear(ks_pcie->legacy_intc_np, + PCI_NUM_INTX, + &ks_dw_pcie_legacy_irq_domain_ops, + NULL); + if (!ks_pcie->legacy_irq_domain) { + dev_err(dev, "Failed to add irq domain for legacy irqs\n"); + return -EINVAL; + } + + return dw_pcie_host_init(pp); +} diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c new file mode 100644 index 000000000000..3722a5f31e5e --- /dev/null +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -0,0 +1,457 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Texas Instruments Keystone SoCs + * + * Copyright (C) 2013-2014 Texas Instruments., Ltd. + * http://www.ti.com + * + * Author: Murali Karicheri + * Implementation based on pci-exynos.c and pcie-designware.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" +#include "pci-keystone.h" + +#define DRIVER_NAME "keystone-pcie" + +/* DEV_STAT_CTRL */ +#define PCIE_CAP_BASE 0x70 + +/* PCIE controller device IDs */ +#define PCIE_RC_K2HK 0xb008 +#define PCIE_RC_K2E 0xb009 +#define PCIE_RC_K2L 0xb00a + +#define to_keystone_pcie(x) dev_get_drvdata((x)->dev) + +static void quirk_limit_mrrs(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + struct pci_dev *bridge = bus->self; + static const struct pci_device_id rc_pci_devids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2HK), + .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2E), + .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2L), + .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, + { 0, }, + }; + + if (pci_is_root_bus(bus)) + return; + + /* look for the host bridge */ + while (!pci_is_root_bus(bus)) { + bridge = bus->self; + bus = bus->parent; + } + + if (bridge) { + /* + * Keystone PCI controller has a h/w limitation of + * 256 bytes maximum read request size. It can't handle + * anything higher than this. So force this limit on + * all downstream devices. + */ + if (pci_match_id(rc_pci_devids, bridge)) { + if (pcie_get_readrq(dev) > 256) { + dev_info(&dev->dev, "limiting MRRS to 256\n"); + pcie_set_readrq(dev, 256); + } + } + } +} +DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs); + +static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) +{ + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; + unsigned int retries; + + dw_pcie_setup_rc(pp); + + if (dw_pcie_link_up(pci)) { + dev_info(dev, "Link already up\n"); + return 0; + } + + /* check if the link is up or not */ + for (retries = 0; retries < 5; retries++) { + ks_dw_pcie_initiate_link_train(ks_pcie); + if (!dw_pcie_wait_for_link(pci)) + return 0; + } + + dev_err(dev, "phy link never came up\n"); + return -ETIMEDOUT; +} + +static void ks_pcie_msi_irq_handler(struct irq_desc *desc) +{ + unsigned int irq = irq_desc_get_irq(desc); + struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); + u32 offset = irq - ks_pcie->msi_host_irqs[0]; + struct dw_pcie *pci = ks_pcie->pci; + struct device *dev = pci->dev; + struct irq_chip *chip = irq_desc_get_chip(desc); + + dev_dbg(dev, "%s, irq %d\n", __func__, irq); + + /* + * The chained irq handler installation would have replaced normal + * interrupt driver handler so we need to take care of mask/unmask and + * ack operation. + */ + chained_irq_enter(chip, desc); + ks_dw_pcie_handle_msi_irq(ks_pcie, offset); + chained_irq_exit(chip, desc); +} + +/** + * ks_pcie_legacy_irq_handler() - Handle legacy interrupt + * @irq: IRQ line for legacy interrupts + * @desc: Pointer to irq descriptor + * + * Traverse through pending legacy interrupts and invoke handler for each. Also + * takes care of interrupt controller level mask/ack operation. + */ +static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) +{ + unsigned int irq = irq_desc_get_irq(desc); + struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); + struct dw_pcie *pci = ks_pcie->pci; + struct device *dev = pci->dev; + u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0]; + struct irq_chip *chip = irq_desc_get_chip(desc); + + dev_dbg(dev, ": Handling legacy irq %d\n", irq); + + /* + * The chained irq handler installation would have replaced normal + * interrupt driver handler so we need to take care of mask/unmask and + * ack operation. + */ + chained_irq_enter(chip, desc); + ks_dw_pcie_handle_legacy_irq(ks_pcie, irq_offset); + chained_irq_exit(chip, desc); +} + +static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie, + char *controller, int *num_irqs) +{ + int temp, max_host_irqs, legacy = 1, *host_irqs; + struct device *dev = ks_pcie->pci->dev; + struct device_node *np_pcie = dev->of_node, **np_temp; + + if (!strcmp(controller, "msi-interrupt-controller")) + legacy = 0; + + if (legacy) { + np_temp = &ks_pcie->legacy_intc_np; + max_host_irqs = PCI_NUM_INTX; + host_irqs = &ks_pcie->legacy_host_irqs[0]; + } else { + np_temp = &ks_pcie->msi_intc_np; + max_host_irqs = MAX_MSI_HOST_IRQS; + host_irqs = &ks_pcie->msi_host_irqs[0]; + } + + /* interrupt controller is in a child node */ + *np_temp = of_get_child_by_name(np_pcie, controller); + if (!(*np_temp)) { + dev_err(dev, "Node for %s is absent\n", controller); + return -EINVAL; + } + + temp = of_irq_count(*np_temp); + if (!temp) { + dev_err(dev, "No IRQ entries in %s\n", controller); + of_node_put(*np_temp); + return -EINVAL; + } + + if (temp > max_host_irqs) + dev_warn(dev, "Too many %s interrupts defined %u\n", + (legacy ? "legacy" : "MSI"), temp); + + /* + * support upto max_host_irqs. In dt from index 0 to 3 (legacy) or 0 to + * 7 (MSI) + */ + for (temp = 0; temp < max_host_irqs; temp++) { + host_irqs[temp] = irq_of_parse_and_map(*np_temp, temp); + if (!host_irqs[temp]) + break; + } + + of_node_put(*np_temp); + + if (temp) { + *num_irqs = temp; + return 0; + } + + return -EINVAL; +} + +static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie) +{ + int i; + + /* Legacy IRQ */ + for (i = 0; i < ks_pcie->num_legacy_host_irqs; i++) { + irq_set_chained_handler_and_data(ks_pcie->legacy_host_irqs[i], + ks_pcie_legacy_irq_handler, + ks_pcie); + } + ks_dw_pcie_enable_legacy_irqs(ks_pcie); + + /* MSI IRQ */ + if (IS_ENABLED(CONFIG_PCI_MSI)) { + for (i = 0; i < ks_pcie->num_msi_host_irqs; i++) { + irq_set_chained_handler_and_data(ks_pcie->msi_host_irqs[i], + ks_pcie_msi_irq_handler, + ks_pcie); + } + } + + if (ks_pcie->error_irq > 0) + ks_dw_pcie_enable_error_irq(ks_pcie); +} + +/* + * When a PCI device does not exist during config cycles, keystone host gets a + * bus error instead of returning 0xffffffff. This handler always returns 0 + * for this kind of faults. + */ +static int keystone_pcie_fault(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + unsigned long instr = *(unsigned long *) instruction_pointer(regs); + + if ((instr & 0x0e100090) == 0x00100090) { + int reg = (instr >> 12) & 15; + + regs->uregs[reg] = -1; + regs->ARM_pc += 4; + } + + return 0; +} + +static int __init ks_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); + u32 val; + + ks_pcie_establish_link(ks_pcie); + ks_dw_pcie_setup_rc_app_regs(ks_pcie); + ks_pcie_setup_interrupts(ks_pcie); + writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8), + pci->dbi_base + PCI_IO_BASE); + + /* update the Vendor ID */ + writew(ks_pcie->device_id, pci->dbi_base + PCI_DEVICE_ID); + + /* update the DEV_STAT_CTRL to publish right mrrs */ + val = readl(pci->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL); + val &= ~PCI_EXP_DEVCTL_READRQ; + /* set the mrrs to 256 bytes */ + val |= BIT(12); + writel(val, pci->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL); + + /* + * PCIe access errors that result into OCP errors are caught by ARM as + * "External aborts" + */ + hook_fault_code(17, keystone_pcie_fault, SIGBUS, 0, + "Asynchronous external abort"); + + return 0; +} + +static const struct dw_pcie_host_ops keystone_pcie_host_ops = { + .rd_other_conf = ks_dw_pcie_rd_other_conf, + .wr_other_conf = ks_dw_pcie_wr_other_conf, + .host_init = ks_pcie_host_init, + .msi_set_irq = ks_dw_pcie_msi_set_irq, + .msi_clear_irq = ks_dw_pcie_msi_clear_irq, + .get_msi_addr = ks_dw_pcie_get_msi_addr, + .msi_host_init = ks_dw_pcie_msi_host_init, + .msi_irq_ack = ks_dw_pcie_msi_irq_ack, + .scan_bus = ks_dw_pcie_v3_65_scan_bus, +}; + +static irqreturn_t pcie_err_irq_handler(int irq, void *priv) +{ + struct keystone_pcie *ks_pcie = priv; + + return ks_dw_pcie_handle_error_irq(ks_pcie); +} + +static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + ret = ks_pcie_get_irq_controller_info(ks_pcie, + "legacy-interrupt-controller", + &ks_pcie->num_legacy_host_irqs); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + ret = ks_pcie_get_irq_controller_info(ks_pcie, + "msi-interrupt-controller", + &ks_pcie->num_msi_host_irqs); + if (ret) + return ret; + } + + /* + * Index 0 is the platform interrupt for error interrupt + * from RC. This is optional. + */ + ks_pcie->error_irq = irq_of_parse_and_map(ks_pcie->np, 0); + if (ks_pcie->error_irq <= 0) + dev_info(dev, "no error IRQ defined\n"); + else { + ret = request_irq(ks_pcie->error_irq, pcie_err_irq_handler, + IRQF_SHARED, "pcie-error-irq", ks_pcie); + if (ret < 0) { + dev_err(dev, "failed to request error IRQ %d\n", + ks_pcie->error_irq); + return ret; + } + } + + pp->root_bus_nr = -1; + pp->ops = &keystone_pcie_host_ops; + ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct of_device_id ks_pcie_of_match[] = { + { + .type = "pci", + .compatible = "ti,keystone-pcie", + }, + { }, +}; + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = ks_dw_pcie_link_up, +}; + +static int __exit ks_pcie_remove(struct platform_device *pdev) +{ + struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev); + + clk_disable_unprepare(ks_pcie->clk); + + return 0; +} + +static int __init ks_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct keystone_pcie *ks_pcie; + struct resource *res; + void __iomem *reg_p; + struct phy *phy; + int ret; + + ks_pcie = devm_kzalloc(dev, sizeof(*ks_pcie), GFP_KERNEL); + if (!ks_pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + ks_pcie->pci = pci; + + /* initialize SerDes Phy if present */ + phy = devm_phy_get(dev, "pcie-phy"); + if (PTR_ERR_OR_ZERO(phy) == -EPROBE_DEFER) + return PTR_ERR(phy); + + if (!IS_ERR_OR_NULL(phy)) { + ret = phy_init(phy); + if (ret < 0) + return ret; + } + + /* index 2 is to read PCI DEVICE_ID */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + reg_p = devm_ioremap_resource(dev, res); + if (IS_ERR(reg_p)) + return PTR_ERR(reg_p); + ks_pcie->device_id = readl(reg_p) >> 16; + devm_iounmap(dev, reg_p); + devm_release_mem_region(dev, res->start, resource_size(res)); + + ks_pcie->np = dev->of_node; + platform_set_drvdata(pdev, ks_pcie); + ks_pcie->clk = devm_clk_get(dev, "pcie"); + if (IS_ERR(ks_pcie->clk)) { + dev_err(dev, "Failed to get pcie rc clock\n"); + return PTR_ERR(ks_pcie->clk); + } + ret = clk_prepare_enable(ks_pcie->clk); + if (ret) + return ret; + + platform_set_drvdata(pdev, ks_pcie); + + ret = ks_add_pcie_port(ks_pcie, pdev); + if (ret < 0) + goto fail_clk; + + return 0; +fail_clk: + clk_disable_unprepare(ks_pcie->clk); + + return ret; +} + +static struct platform_driver ks_pcie_driver __refdata = { + .probe = ks_pcie_probe, + .remove = __exit_p(ks_pcie_remove), + .driver = { + .name = "keystone-pcie", + .of_match_table = of_match_ptr(ks_pcie_of_match), + }, +}; +builtin_platform_driver(ks_pcie_driver); diff --git a/drivers/pci/controller/dwc/pci-keystone.h b/drivers/pci/controller/dwc/pci-keystone.h new file mode 100644 index 000000000000..8a13da391543 --- /dev/null +++ b/drivers/pci/controller/dwc/pci-keystone.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Keystone PCI Controller's common includes + * + * Copyright (C) 2013-2014 Texas Instruments., Ltd. + * http://www.ti.com + * + * Author: Murali Karicheri + */ + +#define MAX_MSI_HOST_IRQS 8 + +struct keystone_pcie { + struct dw_pcie *pci; + struct clk *clk; + /* PCI Device ID */ + u32 device_id; + int num_legacy_host_irqs; + int legacy_host_irqs[PCI_NUM_INTX]; + struct device_node *legacy_intc_np; + + int num_msi_host_irqs; + int msi_host_irqs[MAX_MSI_HOST_IRQS]; + struct device_node *msi_intc_np; + struct irq_domain *legacy_irq_domain; + struct device_node *np; + + int error_irq; + + /* Application register space */ + void __iomem *va_app_base; /* DT 1st resource */ + struct resource app; +}; + +/* Keystone DW specific MSI controller APIs/definitions */ +void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset); +phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp); + +/* Keystone specific PCI controller APIs */ +void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie); +void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset); +void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie); +irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie); +int ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, + struct device_node *msi_intc_np); +int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); +int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); +void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie); +void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie); +void ks_dw_pcie_msi_irq_ack(int i, struct pcie_port *pp); +void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); +void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); +void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); +int ks_dw_pcie_msi_host_init(struct pcie_port *pp); +int ks_dw_pcie_link_up(struct dw_pcie *pci); diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c new file mode 100644 index 000000000000..3724d3ef7008 --- /dev/null +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Freescale Layerscape SoCs + * + * Copyright (C) 2014 Freescale Semiconductor. + * + * Author: Minghuan Lian + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +/* PEX1/2 Misc Ports Status Register */ +#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4) +#define LTSSM_STATE_SHIFT 20 +#define LTSSM_STATE_MASK 0x3f +#define LTSSM_PCIE_L0 0x11 /* L0 state */ + +/* PEX Internal Configuration Registers */ +#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ +#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */ +#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */ + +#define PCIE_IATU_NUM 6 + +struct ls_pcie_drvdata { + u32 lut_offset; + u32 ltssm_shift; + u32 lut_dbg; + const struct dw_pcie_host_ops *ops; + const struct dw_pcie_ops *dw_pcie_ops; +}; + +struct ls_pcie { + struct dw_pcie *pci; + void __iomem *lut; + struct regmap *scfg; + const struct ls_pcie_drvdata *drvdata; + int index; +}; + +#define to_ls_pcie(x) dev_get_drvdata((x)->dev) + +static bool ls_pcie_is_bridge(struct ls_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + u32 header_type; + + header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE); + header_type &= 0x7f; + + return header_type == PCI_HEADER_TYPE_BRIDGE; +} + +/* Clear multi-function bit */ +static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + + iowrite8(PCI_HEADER_TYPE_BRIDGE, pci->dbi_base + PCI_HEADER_TYPE); +} + +/* Drop MSG TLP except for Vendor MSG */ +static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) +{ + u32 val; + struct dw_pcie *pci = pcie->pci; + + val = ioread32(pci->dbi_base + PCIE_STRFMR1); + val &= 0xDFFFFFFF; + iowrite32(val, pci->dbi_base + PCIE_STRFMR1); +} + +static void ls_pcie_disable_outbound_atus(struct ls_pcie *pcie) +{ + int i; + + for (i = 0; i < PCIE_IATU_NUM; i++) + dw_pcie_disable_atu(pcie->pci, DW_PCIE_REGION_OUTBOUND, i); +} + +static int ls1021_pcie_link_up(struct dw_pcie *pci) +{ + u32 state; + struct ls_pcie *pcie = to_ls_pcie(pci); + + if (!pcie->scfg) + return 0; + + regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); + state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; + + if (state < LTSSM_PCIE_L0) + return 0; + + return 1; +} + +static int ls_pcie_link_up(struct dw_pcie *pci) +{ + struct ls_pcie *pcie = to_ls_pcie(pci); + u32 state; + + state = (ioread32(pcie->lut + pcie->drvdata->lut_dbg) >> + pcie->drvdata->ltssm_shift) & + LTSSM_STATE_MASK; + + if (state < LTSSM_PCIE_L0) + return 0; + + return 1; +} + +/* Forward error response of outbound non-posted requests */ +static void ls_pcie_fix_error_response(struct ls_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + + iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR); +} + +static int ls_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); + + /* + * Disable outbound windows configured by the bootloader to avoid + * one transaction hitting multiple outbound windows. + * dw_pcie_setup_rc() will reconfigure the outbound windows. + */ + ls_pcie_disable_outbound_atus(pcie); + ls_pcie_fix_error_response(pcie); + + dw_pcie_dbi_ro_wr_en(pci); + ls_pcie_clear_multifunction(pcie); + dw_pcie_dbi_ro_wr_dis(pci); + + ls_pcie_drop_msg_tlp(pcie); + + dw_pcie_setup_rc(pp); + + return 0; +} + +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 *dev = pci->dev; + u32 index[2]; + int ret; + + pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, + "fsl,pcie-scfg"); + if (IS_ERR(pcie->scfg)) { + ret = PTR_ERR(pcie->scfg); + dev_err(dev, "No syscfg phandle specified\n"); + pcie->scfg = NULL; + return ret; + } + + if (of_property_read_u32_array(dev->of_node, + "fsl,pcie-scfg", index, 2)) { + pcie->scfg = NULL; + return -EINVAL; + } + pcie->index = index[1]; + + return ls_pcie_host_init(pp); +} + +static int ls_pcie_msi_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; + struct device_node *msi_node; + + /* + * The MSI domain is set by the generic of_msi_configure(). This + * .msi_host_init() function keeps us from doing the default MSI + * domain setup in dw_pcie_host_init() and also enforces the + * requirement that "msi-parent" exists. + */ + msi_node = of_parse_phandle(np, "msi-parent", 0); + if (!msi_node) { + dev_err(dev, "failed to find msi-parent\n"); + return -EINVAL; + } + + return 0; +} + +static const struct dw_pcie_host_ops ls1021_pcie_host_ops = { + .host_init = ls1021_pcie_host_init, + .msi_host_init = ls_pcie_msi_host_init, +}; + +static const struct dw_pcie_host_ops ls_pcie_host_ops = { + .host_init = ls_pcie_host_init, + .msi_host_init = ls_pcie_msi_host_init, +}; + +static const struct dw_pcie_ops dw_ls1021_pcie_ops = { + .link_up = ls1021_pcie_link_up, +}; + +static const struct dw_pcie_ops dw_ls_pcie_ops = { + .link_up = ls_pcie_link_up, +}; + +static struct ls_pcie_drvdata ls1021_drvdata = { + .ops = &ls1021_pcie_host_ops, + .dw_pcie_ops = &dw_ls1021_pcie_ops, +}; + +static struct ls_pcie_drvdata ls1043_drvdata = { + .lut_offset = 0x10000, + .ltssm_shift = 24, + .lut_dbg = 0x7fc, + .ops = &ls_pcie_host_ops, + .dw_pcie_ops = &dw_ls_pcie_ops, +}; + +static struct ls_pcie_drvdata ls1046_drvdata = { + .lut_offset = 0x80000, + .ltssm_shift = 24, + .lut_dbg = 0x407fc, + .ops = &ls_pcie_host_ops, + .dw_pcie_ops = &dw_ls_pcie_ops, +}; + +static struct ls_pcie_drvdata ls2080_drvdata = { + .lut_offset = 0x80000, + .ltssm_shift = 0, + .lut_dbg = 0x7fc, + .ops = &ls_pcie_host_ops, + .dw_pcie_ops = &dw_ls_pcie_ops, +}; + +static struct ls_pcie_drvdata ls2088_drvdata = { + .lut_offset = 0x80000, + .ltssm_shift = 0, + .lut_dbg = 0x407fc, + .ops = &ls_pcie_host_ops, + .dw_pcie_ops = &dw_ls_pcie_ops, +}; + +static const struct of_device_id ls_pcie_of_match[] = { + { .compatible = "fsl,ls1012a-pcie", .data = &ls1046_drvdata }, + { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata }, + { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata }, + { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata }, + { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata }, + { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata }, + { .compatible = "fsl,ls2088a-pcie", .data = &ls2088_drvdata }, + { .compatible = "fsl,ls1088a-pcie", .data = &ls2088_drvdata }, + { }, +}; + +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 *dev = pci->dev; + int ret; + + pp->ops = pcie->drvdata->ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int __init ls_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct ls_pcie *pcie; + struct resource *dbi_base; + int ret; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pcie->drvdata = of_device_get_match_data(dev); + + pci->dev = dev; + pci->ops = pcie->drvdata->dw_pcie_ops; + + pcie->pci = pci; + + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset; + + if (!ls_pcie_is_bridge(pcie)) + return -ENODEV; + + platform_set_drvdata(pdev, pcie); + + ret = ls_add_pcie_port(pcie); + if (ret < 0) + return ret; + + return 0; +} + +static struct platform_driver ls_pcie_driver = { + .driver = { + .name = "layerscape-pcie", + .of_match_table = ls_pcie_of_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver_probe(ls_pcie_driver, ls_pcie_probe); diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c new file mode 100644 index 000000000000..072fd7ecc29f --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Marvell Armada-8K SoCs + * + * Armada-8K PCIe Glue Layer Source Code + * + * Copyright (C) 2016 Marvell Technology Group Ltd. + * + * Author: Yehuda Yitshak + * Author: Shadi Ammouri + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +struct armada8k_pcie { + struct dw_pcie *pci; + struct clk *clk; + struct clk *clk_reg; +}; + +#define PCIE_VENDOR_REGS_OFFSET 0x8000 + +#define PCIE_GLOBAL_CONTROL_REG (PCIE_VENDOR_REGS_OFFSET + 0x0) +#define PCIE_APP_LTSSM_EN BIT(2) +#define PCIE_DEVICE_TYPE_SHIFT 4 +#define PCIE_DEVICE_TYPE_MASK 0xF +#define PCIE_DEVICE_TYPE_RC 0x4 /* Root complex */ + +#define PCIE_GLOBAL_STATUS_REG (PCIE_VENDOR_REGS_OFFSET + 0x8) +#define PCIE_GLB_STS_RDLH_LINK_UP BIT(1) +#define PCIE_GLB_STS_PHY_LINK_UP BIT(9) + +#define PCIE_GLOBAL_INT_CAUSE1_REG (PCIE_VENDOR_REGS_OFFSET + 0x1C) +#define PCIE_GLOBAL_INT_MASK1_REG (PCIE_VENDOR_REGS_OFFSET + 0x20) +#define PCIE_INT_A_ASSERT_MASK BIT(9) +#define PCIE_INT_B_ASSERT_MASK BIT(10) +#define PCIE_INT_C_ASSERT_MASK BIT(11) +#define PCIE_INT_D_ASSERT_MASK BIT(12) + +#define PCIE_ARCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x50) +#define PCIE_AWCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x54) +#define PCIE_ARUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x5C) +#define PCIE_AWUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x60) +/* + * AR/AW Cache defauls: Normal memory, Write-Back, Read / Write + * allocate + */ +#define ARCACHE_DEFAULT_VALUE 0x3511 +#define AWCACHE_DEFAULT_VALUE 0x5311 + +#define DOMAIN_OUTER_SHAREABLE 0x2 +#define AX_USER_DOMAIN_MASK 0x3 +#define AX_USER_DOMAIN_SHIFT 4 + +#define to_armada8k_pcie(x) dev_get_drvdata((x)->dev) + +static int armada8k_pcie_link_up(struct dw_pcie *pci) +{ + u32 reg; + u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP; + + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_STATUS_REG); + + if ((reg & mask) == mask) + return 1; + + dev_dbg(pci->dev, "No link detected (Global-Status: 0x%08x).\n", reg); + return 0; +} + +static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + u32 reg; + + if (!dw_pcie_link_up(pci)) { + /* Disable LTSSM state machine to enable configuration */ + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); + reg &= ~(PCIE_APP_LTSSM_EN); + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); + } + + /* Set the device to root complex mode */ + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); + reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT); + reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT; + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); + + /* Set the PCIe master AxCache attributes */ + dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE); + dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE); + + /* Set the PCIe master AxDomain attributes */ + reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG); + reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); + reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; + dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg); + + reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG); + reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); + reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; + dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg); + + /* Enable INT A-D interrupts */ + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG); + reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK | + PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK; + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg); + + if (!dw_pcie_link_up(pci)) { + /* Configuration done. Start LTSSM */ + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); + reg |= PCIE_APP_LTSSM_EN; + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); + } + + /* Wait until the link becomes active again */ + if (dw_pcie_wait_for_link(pci)) + dev_err(pci->dev, "Link not up after reconfiguration\n"); +} + +static int armada8k_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct armada8k_pcie *pcie = to_armada8k_pcie(pci); + + dw_pcie_setup_rc(pp); + armada8k_pcie_establish_link(pcie); + + return 0; +} + +static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) +{ + struct armada8k_pcie *pcie = arg; + struct dw_pcie *pci = pcie->pci; + u32 val; + + /* + * Interrupts are directly handled by the device driver of the + * PCI device. However, they are also latched into the PCIe + * controller, so we simply discard them. + */ + val = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG); + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG, val); + + return IRQ_HANDLED; +} + +static const struct dw_pcie_host_ops armada8k_pcie_host_ops = { + .host_init = armada8k_pcie_host_init, +}; + +static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + pp->root_bus_nr = -1; + pp->ops = &armada8k_pcie_host_ops; + + pp->irq = platform_get_irq(pdev, 0); + if (pp->irq < 0) { + dev_err(dev, "failed to get irq for port\n"); + return pp->irq; + } + + ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler, + IRQF_SHARED, "armada8k-pcie", pcie); + if (ret) { + dev_err(dev, "failed to request irq %d\n", pp->irq); + return ret; + } + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = armada8k_pcie_link_up, +}; + +static int armada8k_pcie_probe(struct platform_device *pdev) +{ + struct dw_pcie *pci; + struct armada8k_pcie *pcie; + struct device *dev = &pdev->dev; + struct resource *base; + int ret; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + pcie->pci = pci; + + pcie->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pcie->clk)) + return PTR_ERR(pcie->clk); + + ret = clk_prepare_enable(pcie->clk); + if (ret) + return ret; + + pcie->clk_reg = devm_clk_get(dev, "reg"); + if (pcie->clk_reg == ERR_PTR(-EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto fail; + } + if (!IS_ERR(pcie->clk_reg)) { + ret = clk_prepare_enable(pcie->clk_reg); + if (ret) + goto fail_clkreg; + } + + /* Get the dw-pcie unit configuration/control registers base. */ + base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); + pci->dbi_base = devm_pci_remap_cfg_resource(dev, base); + if (IS_ERR(pci->dbi_base)) { + dev_err(dev, "couldn't remap regs base %p\n", base); + ret = PTR_ERR(pci->dbi_base); + goto fail_clkreg; + } + + platform_set_drvdata(pdev, pcie); + + ret = armada8k_add_pcie_port(pcie, pdev); + if (ret) + goto fail_clkreg; + + return 0; + +fail_clkreg: + clk_disable_unprepare(pcie->clk_reg); +fail: + clk_disable_unprepare(pcie->clk); + + return ret; +} + +static const struct of_device_id armada8k_pcie_of_match[] = { + { .compatible = "marvell,armada8k-pcie", }, + {}, +}; + +static struct platform_driver armada8k_pcie_driver = { + .probe = armada8k_pcie_probe, + .driver = { + .name = "armada8k-pcie", + .of_match_table = of_match_ptr(armada8k_pcie_of_match), + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(armada8k_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c new file mode 100644 index 000000000000..321b56cfd5d0 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Axis ARTPEC-6 SoC + * + * Author: Niklas Cassel + * + * Based on work done by Phil Edworthy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define to_artpec6_pcie(x) dev_get_drvdata((x)->dev) + +enum artpec_pcie_variants { + ARTPEC6, + ARTPEC7, +}; + +struct artpec6_pcie { + struct dw_pcie *pci; + struct regmap *regmap; /* DT axis,syscon-pcie */ + void __iomem *phy_base; /* DT phy */ + enum artpec_pcie_variants variant; + enum dw_pcie_device_mode mode; +}; + +struct artpec_pcie_of_data { + enum artpec_pcie_variants variant; + enum dw_pcie_device_mode mode; +}; + +static const struct of_device_id artpec6_pcie_of_match[]; + +/* PCIe Port Logic registers (memory-mapped) */ +#define PL_OFFSET 0x700 + +#define ACK_F_ASPM_CTRL_OFF (PL_OFFSET + 0xc) +#define ACK_N_FTS_MASK GENMASK(15, 8) +#define ACK_N_FTS(x) (((x) << 8) & ACK_N_FTS_MASK) + +#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0) +#define FAST_TRAINING_SEQ(x) (((x) << 0) & FAST_TRAINING_SEQ_MASK) + +/* ARTPEC-6 specific registers */ +#define PCIECFG 0x18 +#define PCIECFG_DBG_OEN BIT(24) +#define PCIECFG_CORE_RESET_REQ BIT(21) +#define PCIECFG_LTSSM_ENABLE BIT(20) +#define PCIECFG_DEVICE_TYPE_MASK GENMASK(19, 16) +#define PCIECFG_CLKREQ_B BIT(11) +#define PCIECFG_REFCLK_ENABLE BIT(10) +#define PCIECFG_PLL_ENABLE BIT(9) +#define PCIECFG_PCLK_ENABLE BIT(8) +#define PCIECFG_RISRCREN BIT(4) +#define PCIECFG_MODE_TX_DRV_EN BIT(3) +#define PCIECFG_CISRREN BIT(2) +#define PCIECFG_MACRO_ENABLE BIT(0) +/* ARTPEC-7 specific fields */ +#define PCIECFG_REFCLKSEL BIT(23) +#define PCIECFG_NOC_RESET BIT(3) + +#define PCIESTAT 0x1c +/* ARTPEC-7 specific fields */ +#define PCIESTAT_EXTREFCLK BIT(3) + +#define NOCCFG 0x40 +#define NOCCFG_ENABLE_CLK_PCIE BIT(4) +#define NOCCFG_POWER_PCIE_IDLEACK BIT(3) +#define NOCCFG_POWER_PCIE_IDLE BIT(2) +#define NOCCFG_POWER_PCIE_IDLEREQ BIT(1) + +#define PHY_STATUS 0x118 +#define PHY_COSPLLLOCK BIT(0) + +#define PHY_TX_ASIC_OUT 0x4040 +#define PHY_TX_ASIC_OUT_TX_ACK BIT(0) + +#define PHY_RX_ASIC_OUT 0x405c +#define PHY_RX_ASIC_OUT_ACK BIT(0) + +static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset) +{ + u32 val; + + regmap_read(artpec6_pcie->regmap, offset, &val); + return val; +} + +static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u32 val) +{ + regmap_write(artpec6_pcie->regmap, offset, val); +} + +static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) +{ + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); + struct pcie_port *pp = &pci->pp; + struct dw_pcie_ep *ep = &pci->ep; + + switch (artpec6_pcie->mode) { + case DW_PCIE_RC_TYPE: + return pci_addr - pp->cfg0_base; + case DW_PCIE_EP_TYPE: + return pci_addr - ep->phys_base; + default: + dev_err(pci->dev, "UNKNOWN device type\n"); + } + return pci_addr; +} + +static int artpec6_pcie_establish_link(struct dw_pcie *pci) +{ + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); + u32 val; + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val |= PCIECFG_LTSSM_ENABLE; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + + return 0; +} + +static void artpec6_pcie_stop_link(struct dw_pcie *pci) +{ + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); + u32 val; + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val &= ~PCIECFG_LTSSM_ENABLE; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup, + .start_link = artpec6_pcie_establish_link, + .stop_link = artpec6_pcie_stop_link, +}; + +static void artpec6_pcie_wait_for_phy_a6(struct artpec6_pcie *artpec6_pcie) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + struct device *dev = pci->dev; + u32 val; + unsigned int retries; + + retries = 50; + do { + usleep_range(1000, 2000); + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + retries--; + } while (retries && + (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE))); + if (!retries) + dev_err(dev, "PCIe clock manager did not leave idle state\n"); + + retries = 50; + do { + usleep_range(1000, 2000); + val = readl(artpec6_pcie->phy_base + PHY_STATUS); + retries--; + } while (retries && !(val & PHY_COSPLLLOCK)); + if (!retries) + dev_err(dev, "PHY PLL did not lock\n"); +} + +static void artpec6_pcie_wait_for_phy_a7(struct artpec6_pcie *artpec6_pcie) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + struct device *dev = pci->dev; + u32 val; + u16 phy_status_tx, phy_status_rx; + unsigned int retries; + + retries = 50; + do { + usleep_range(1000, 2000); + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + retries--; + } while (retries && + (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE))); + if (!retries) + dev_err(dev, "PCIe clock manager did not leave idle state\n"); + + retries = 50; + do { + usleep_range(1000, 2000); + phy_status_tx = readw(artpec6_pcie->phy_base + PHY_TX_ASIC_OUT); + phy_status_rx = readw(artpec6_pcie->phy_base + PHY_RX_ASIC_OUT); + retries--; + } while (retries && ((phy_status_tx & PHY_TX_ASIC_OUT_TX_ACK) || + (phy_status_rx & PHY_RX_ASIC_OUT_ACK))); + if (!retries) + dev_err(dev, "PHY did not enter Pn state\n"); +} + +static void artpec6_pcie_wait_for_phy(struct artpec6_pcie *artpec6_pcie) +{ + switch (artpec6_pcie->variant) { + case ARTPEC6: + artpec6_pcie_wait_for_phy_a6(artpec6_pcie); + break; + case ARTPEC7: + artpec6_pcie_wait_for_phy_a7(artpec6_pcie); + break; + } +} + +static void artpec6_pcie_init_phy_a6(struct artpec6_pcie *artpec6_pcie) +{ + u32 val; + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */ + PCIECFG_MODE_TX_DRV_EN | + PCIECFG_CISRREN | /* Reference clock term. 100 Ohm */ + PCIECFG_MACRO_ENABLE; + val |= PCIECFG_REFCLK_ENABLE; + val &= ~PCIECFG_DBG_OEN; + val &= ~PCIECFG_CLKREQ_B; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + usleep_range(5000, 6000); + + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + val |= NOCCFG_ENABLE_CLK_PCIE; + artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); + usleep_range(20, 30); + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val |= PCIECFG_PCLK_ENABLE | PCIECFG_PLL_ENABLE; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + usleep_range(6000, 7000); + + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + val &= ~NOCCFG_POWER_PCIE_IDLEREQ; + artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); +} + +static void artpec6_pcie_init_phy_a7(struct artpec6_pcie *artpec6_pcie) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + u32 val; + bool extrefclk; + + /* Check if external reference clock is connected */ + val = artpec6_pcie_readl(artpec6_pcie, PCIESTAT); + extrefclk = !!(val & PCIESTAT_EXTREFCLK); + dev_dbg(pci->dev, "Using reference clock: %s\n", + extrefclk ? "external" : "internal"); + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */ + PCIECFG_PCLK_ENABLE; + if (extrefclk) + val |= PCIECFG_REFCLKSEL; + else + val &= ~PCIECFG_REFCLKSEL; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + usleep_range(10, 20); + + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + val |= NOCCFG_ENABLE_CLK_PCIE; + artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); + usleep_range(20, 30); + + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); + val &= ~NOCCFG_POWER_PCIE_IDLEREQ; + artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); +} + +static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) +{ + switch (artpec6_pcie->variant) { + case ARTPEC6: + artpec6_pcie_init_phy_a6(artpec6_pcie); + break; + case ARTPEC7: + artpec6_pcie_init_phy_a7(artpec6_pcie); + break; + } +} + +static void artpec6_pcie_set_nfts(struct artpec6_pcie *artpec6_pcie) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + u32 val; + + if (artpec6_pcie->variant != ARTPEC7) + return; + + /* + * Increase the N_FTS (Number of Fast Training Sequences) + * to be transmitted when transitioning from L0s to L0. + */ + val = dw_pcie_readl_dbi(pci, ACK_F_ASPM_CTRL_OFF); + val &= ~ACK_N_FTS_MASK; + val |= ACK_N_FTS(180); + dw_pcie_writel_dbi(pci, ACK_F_ASPM_CTRL_OFF, val); + + /* + * Set the Number of Fast Training Sequences that the core + * advertises as its N_FTS during Gen2 or Gen3 link training. + */ + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~FAST_TRAINING_SEQ_MASK; + val |= FAST_TRAINING_SEQ(180); + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); +} + +static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie) +{ + u32 val; + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + switch (artpec6_pcie->variant) { + case ARTPEC6: + val |= PCIECFG_CORE_RESET_REQ; + break; + case ARTPEC7: + val &= ~PCIECFG_NOC_RESET; + break; + } + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); +} + +static void artpec6_pcie_deassert_core_reset(struct artpec6_pcie *artpec6_pcie) +{ + u32 val; + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + switch (artpec6_pcie->variant) { + case ARTPEC6: + val &= ~PCIECFG_CORE_RESET_REQ; + break; + case ARTPEC7: + val |= PCIECFG_NOC_RESET; + break; + } + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + usleep_range(100, 200); +} + +static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + struct pcie_port *pp = &pci->pp; + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); +} + +static int artpec6_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); + + artpec6_pcie_assert_core_reset(artpec6_pcie); + artpec6_pcie_init_phy(artpec6_pcie); + artpec6_pcie_deassert_core_reset(artpec6_pcie); + artpec6_pcie_wait_for_phy(artpec6_pcie); + artpec6_pcie_set_nfts(artpec6_pcie); + dw_pcie_setup_rc(pp); + artpec6_pcie_establish_link(pci); + dw_pcie_wait_for_link(pci); + artpec6_pcie_enable_interrupts(artpec6_pcie); + + return 0; +} + +static const struct dw_pcie_host_ops artpec6_pcie_host_ops = { + .host_init = artpec6_pcie_host_init, +}; + +static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = artpec6_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; + int ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pp->msi_irq < 0) { + dev_err(dev, "failed to get MSI irq\n"); + return pp->msi_irq; + } + } + + pp->root_bus_nr = -1; + pp->ops = &artpec6_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); + enum pci_barno bar; + + artpec6_pcie_assert_core_reset(artpec6_pcie); + artpec6_pcie_init_phy(artpec6_pcie); + artpec6_pcie_deassert_core_reset(artpec6_pcie); + artpec6_pcie_wait_for_phy(artpec6_pcie); + artpec6_pcie_set_nfts(artpec6_pcie); + + for (bar = BAR_0; bar <= BAR_5; bar++) + dw_pcie_ep_reset_bar(pci, bar); +} + +static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, + enum pci_epc_irq_type type, u8 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + switch (type) { + case PCI_EPC_IRQ_LEGACY: + dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); + return -EINVAL; + case PCI_EPC_IRQ_MSI: + return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); + default: + dev_err(pci->dev, "UNKNOWN IRQ type\n"); + } + + return 0; +} + +static struct dw_pcie_ep_ops pcie_ep_ops = { + .ep_init = artpec6_pcie_ep_init, + .raise_irq = artpec6_pcie_raise_irq, +}; + +static int artpec6_add_pcie_ep(struct artpec6_pcie *artpec6_pcie, + struct platform_device *pdev) +{ + int ret; + struct dw_pcie_ep *ep; + struct resource *res; + struct device *dev = &pdev->dev; + struct dw_pcie *pci = artpec6_pcie->pci; + + ep = &pci->ep; + ep->ops = &pcie_ep_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); + pci->dbi_base2 = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base2)) + return PTR_ERR(pci->dbi_base2); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); + if (!res) + return -EINVAL; + + ep->phys_base = res->start; + ep->addr_size = resource_size(res); + + ret = dw_pcie_ep_init(ep); + if (ret) { + dev_err(dev, "failed to initialize endpoint\n"); + return ret; + } + + return 0; +} + +static int artpec6_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct artpec6_pcie *artpec6_pcie; + struct resource *dbi_base; + struct resource *phy_base; + int ret; + const struct of_device_id *match; + const struct artpec_pcie_of_data *data; + enum artpec_pcie_variants variant; + enum dw_pcie_device_mode mode; + + match = of_match_device(artpec6_pcie_of_match, dev); + if (!match) + return -EINVAL; + + data = (struct artpec_pcie_of_data *)match->data; + variant = (enum artpec_pcie_variants)data->variant; + mode = (enum dw_pcie_device_mode)data->mode; + + artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL); + if (!artpec6_pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + artpec6_pcie->pci = pci; + artpec6_pcie->variant = variant; + artpec6_pcie->mode = mode; + + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_ioremap_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + phy_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + artpec6_pcie->phy_base = devm_ioremap_resource(dev, phy_base); + if (IS_ERR(artpec6_pcie->phy_base)) + return PTR_ERR(artpec6_pcie->phy_base); + + artpec6_pcie->regmap = + syscon_regmap_lookup_by_phandle(dev->of_node, + "axis,syscon-pcie"); + if (IS_ERR(artpec6_pcie->regmap)) + return PTR_ERR(artpec6_pcie->regmap); + + platform_set_drvdata(pdev, artpec6_pcie); + + switch (artpec6_pcie->mode) { + case DW_PCIE_RC_TYPE: + if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_HOST)) + return -ENODEV; + + ret = artpec6_add_pcie_port(artpec6_pcie, pdev); + if (ret < 0) + return ret; + break; + case DW_PCIE_EP_TYPE: { + u32 val; + + if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_EP)) + return -ENODEV; + + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); + val &= ~PCIECFG_DEVICE_TYPE_MASK; + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); + ret = artpec6_add_pcie_ep(artpec6_pcie, pdev); + if (ret < 0) + return ret; + break; + } + default: + dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode); + } + + return 0; +} + +static const struct artpec_pcie_of_data artpec6_pcie_rc_of_data = { + .variant = ARTPEC6, + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct artpec_pcie_of_data artpec6_pcie_ep_of_data = { + .variant = ARTPEC6, + .mode = DW_PCIE_EP_TYPE, +}; + +static const struct artpec_pcie_of_data artpec7_pcie_rc_of_data = { + .variant = ARTPEC7, + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct artpec_pcie_of_data artpec7_pcie_ep_of_data = { + .variant = ARTPEC7, + .mode = DW_PCIE_EP_TYPE, +}; + +static const struct of_device_id artpec6_pcie_of_match[] = { + { + .compatible = "axis,artpec6-pcie", + .data = &artpec6_pcie_rc_of_data, + }, + { + .compatible = "axis,artpec6-pcie-ep", + .data = &artpec6_pcie_ep_of_data, + }, + { + .compatible = "axis,artpec7-pcie", + .data = &artpec7_pcie_rc_of_data, + }, + { + .compatible = "axis,artpec7-pcie-ep", + .data = &artpec7_pcie_ep_of_data, + }, + {}, +}; + +static struct platform_driver artpec6_pcie_driver = { + .probe = artpec6_pcie_probe, + .driver = { + .name = "artpec6-pcie", + .of_match_table = artpec6_pcie_of_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(artpec6_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c new file mode 100644 index 000000000000..1eec4415a77f --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Synopsys DesignWare PCIe Endpoint controller driver + * + * Copyright (C) 2017 Texas Instruments + * Author: Kishon Vijay Abraham I + */ + +#include + +#include "pcie-designware.h" +#include +#include + +void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) +{ + struct pci_epc *epc = ep->epc; + + pci_epc_linkup(epc); +} + +static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, + int flags) +{ + u32 reg; + + reg = PCI_BASE_ADDRESS_0 + (4 * bar); + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi2(pci, reg, 0x0); + dw_pcie_writel_dbi(pci, reg, 0x0); + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_writel_dbi2(pci, reg + 4, 0x0); + dw_pcie_writel_dbi(pci, reg + 4, 0x0); + } + dw_pcie_dbi_ro_wr_dis(pci); +} + +void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +{ + __dw_pcie_ep_reset_bar(pci, bar, 0); +} + +static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, + struct pci_epf_header *hdr) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, hdr->vendorid); + dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, hdr->deviceid); + dw_pcie_writeb_dbi(pci, PCI_REVISION_ID, hdr->revid); + dw_pcie_writeb_dbi(pci, PCI_CLASS_PROG, hdr->progif_code); + dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, + hdr->subclass_code | hdr->baseclass_code << 8); + dw_pcie_writeb_dbi(pci, PCI_CACHE_LINE_SIZE, + hdr->cache_line_size); + dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_VENDOR_ID, + hdr->subsys_vendor_id); + dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_ID, hdr->subsys_id); + dw_pcie_writeb_dbi(pci, PCI_INTERRUPT_PIN, + hdr->interrupt_pin); + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} + +static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar, + dma_addr_t cpu_addr, + enum dw_pcie_as_type as_type) +{ + int ret; + u32 free_win; + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + free_win = find_first_zero_bit(ep->ib_window_map, ep->num_ib_windows); + if (free_win >= ep->num_ib_windows) { + dev_err(pci->dev, "No free inbound window\n"); + return -EINVAL; + } + + ret = dw_pcie_prog_inbound_atu(pci, free_win, bar, cpu_addr, + as_type); + if (ret < 0) { + dev_err(pci->dev, "Failed to program IB window\n"); + return ret; + } + + ep->bar_to_atu[bar] = free_win; + set_bit(free_win, ep->ib_window_map); + + return 0; +} + +static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, + u64 pci_addr, size_t size) +{ + u32 free_win; + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows); + if (free_win >= ep->num_ob_windows) { + dev_err(pci->dev, "No free outbound window\n"); + return -EINVAL; + } + + dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM, + phys_addr, pci_addr, size); + + set_bit(free_win, ep->ob_window_map); + ep->outbound_addr[free_win] = phys_addr; + + return 0; +} + +static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + u32 atu_index = ep->bar_to_atu[bar]; + + __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags); + + dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); + clear_bit(atu_index, ep->ib_window_map); +} + +static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar) +{ + int ret; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + size_t size = epf_bar->size; + int flags = epf_bar->flags; + enum dw_pcie_as_type as_type; + u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); + + if (!(flags & PCI_BASE_ADDRESS_SPACE)) + as_type = DW_PCIE_AS_MEM; + else + as_type = DW_PCIE_AS_IO; + + ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type); + if (ret) + return ret; + + dw_pcie_dbi_ro_wr_en(pci); + + dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); + dw_pcie_writel_dbi(pci, reg, flags); + + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1)); + dw_pcie_writel_dbi(pci, reg + 4, 0); + } + + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} + +static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr, + u32 *atu_index) +{ + u32 index; + + for (index = 0; index < ep->num_ob_windows; index++) { + if (ep->outbound_addr[index] != addr) + continue; + *atu_index = index; + return 0; + } + + return -EINVAL; +} + +static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, + phys_addr_t addr) +{ + int ret; + u32 atu_index; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + ret = dw_pcie_find_index(ep, addr, &atu_index); + if (ret < 0) + return; + + dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND); + clear_bit(atu_index, ep->ob_window_map); +} + +static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, + phys_addr_t addr, + u64 pci_addr, size_t size) +{ + int ret; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + ret = dw_pcie_ep_outbound_atu(ep, addr, pci_addr, size); + if (ret) { + dev_err(pci->dev, "Failed to enable address\n"); + return ret; + } + + return 0; +} + +static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no) +{ + int val; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); + if (!(val & MSI_CAP_MSI_EN_MASK)) + return -EINVAL; + + val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; + return val; +} + +static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) +{ + int val; + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); + val &= ~MSI_CAP_MMC_MASK; + val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK; + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val); + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} + +static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, + enum pci_epc_irq_type type, u8 interrupt_num) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + + if (!ep->ops->raise_irq) + return -EINVAL; + + return ep->ops->raise_irq(ep, func_no, type, interrupt_num); +} + +static void dw_pcie_ep_stop(struct pci_epc *epc) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + if (!pci->ops->stop_link) + return; + + pci->ops->stop_link(pci); +} + +static int dw_pcie_ep_start(struct pci_epc *epc) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + if (!pci->ops->start_link) + return -EINVAL; + + return pci->ops->start_link(pci); +} + +static const struct pci_epc_ops epc_ops = { + .write_header = dw_pcie_ep_write_header, + .set_bar = dw_pcie_ep_set_bar, + .clear_bar = dw_pcie_ep_clear_bar, + .map_addr = dw_pcie_ep_map_addr, + .unmap_addr = dw_pcie_ep_unmap_addr, + .set_msi = dw_pcie_ep_set_msi, + .get_msi = dw_pcie_ep_get_msi, + .raise_irq = dw_pcie_ep_raise_irq, + .start = dw_pcie_ep_start, + .stop = dw_pcie_ep_stop, +}; + +int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, + u8 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct pci_epc *epc = ep->epc; + u16 msg_ctrl, msg_data; + u32 msg_addr_lower, msg_addr_upper; + u64 msg_addr; + bool has_upper; + int ret; + + /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ + msg_ctrl = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); + has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); + msg_addr_lower = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32); + if (has_upper) { + msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32); + msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64); + } else { + msg_addr_upper = 0; + msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_32); + } + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; + ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, + epc->mem->page_size); + if (ret) + return ret; + + writel(msg_data | (interrupt_num - 1), ep->msi_mem); + + dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys); + + return 0; +} + +void dw_pcie_ep_exit(struct dw_pcie_ep *ep) +{ + struct pci_epc *epc = ep->epc; + + pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem, + epc->mem->page_size); + + pci_epc_mem_exit(epc); +} + +int dw_pcie_ep_init(struct dw_pcie_ep *ep) +{ + int ret; + void *addr; + struct pci_epc *epc; + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; + + if (!pci->dbi_base || !pci->dbi_base2) { + dev_err(dev, "dbi_base/dbi_base2 is not populated\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows); + if (ret < 0) { + dev_err(dev, "Unable to read *num-ib-windows* property\n"); + return ret; + } + if (ep->num_ib_windows > MAX_IATU_IN) { + dev_err(dev, "Invalid *num-ib-windows*\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows); + if (ret < 0) { + dev_err(dev, "Unable to read *num-ob-windows* property\n"); + return ret; + } + if (ep->num_ob_windows > MAX_IATU_OUT) { + dev_err(dev, "Invalid *num-ob-windows*\n"); + return -EINVAL; + } + + ep->ib_window_map = devm_kzalloc(dev, sizeof(long) * + BITS_TO_LONGS(ep->num_ib_windows), + GFP_KERNEL); + if (!ep->ib_window_map) + return -ENOMEM; + + ep->ob_window_map = devm_kzalloc(dev, sizeof(long) * + BITS_TO_LONGS(ep->num_ob_windows), + GFP_KERNEL); + if (!ep->ob_window_map) + return -ENOMEM; + + addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows, + GFP_KERNEL); + if (!addr) + return -ENOMEM; + ep->outbound_addr = addr; + + if (ep->ops->ep_init) + ep->ops->ep_init(ep); + + epc = devm_pci_epc_create(dev, &epc_ops); + if (IS_ERR(epc)) { + dev_err(dev, "Failed to create epc device\n"); + return PTR_ERR(epc); + } + + ret = of_property_read_u8(np, "max-functions", &epc->max_functions); + if (ret < 0) + epc->max_functions = 1; + + ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size, + ep->page_size); + if (ret < 0) { + dev_err(dev, "Failed to initialize address space\n"); + return ret; + } + + ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, + epc->mem->page_size); + if (!ep->msi_mem) { + dev_err(dev, "Failed to reserve memory for MSI\n"); + return -ENOMEM; + } + + epc->features = EPC_FEATURE_NO_LINKUP_NOTIFIER; + EPC_FEATURE_SET_BAR(epc->features, BAR_0); + + ep->epc = epc; + epc_set_drvdata(epc, ep); + dw_pcie_setup(pci); + + return 0; +} diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c new file mode 100644 index 000000000000..781aa03aeede --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -0,0 +1,722 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synopsys DesignWare PCIe host controller driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han + */ + +#include +#include +#include +#include +#include +#include + +#include "../../pci.h" +#include "pcie-designware.h" + +static struct pci_ops dw_pcie_ops; + +static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + struct dw_pcie *pci; + + if (pp->ops->rd_own_conf) + return pp->ops->rd_own_conf(pp, where, size, val); + + pci = to_dw_pcie_from_pp(pp); + return dw_pcie_read(pci->dbi_base + where, size, val); +} + +static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) +{ + struct dw_pcie *pci; + + if (pp->ops->wr_own_conf) + return pp->ops->wr_own_conf(pp, where, size, val); + + pci = to_dw_pcie_from_pp(pp); + return dw_pcie_write(pci->dbi_base + where, size, val); +} + +static void dw_msi_ack_irq(struct irq_data *d) +{ + irq_chip_ack_parent(d); +} + +static void dw_msi_mask_irq(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void dw_msi_unmask_irq(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip dw_pcie_msi_irq_chip = { + .name = "PCI-MSI", + .irq_ack = dw_msi_ack_irq, + .irq_mask = dw_msi_mask_irq, + .irq_unmask = dw_msi_unmask_irq, +}; + +static struct msi_domain_info dw_pcie_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), + .chip = &dw_pcie_msi_irq_chip, +}; + +/* MSI int handler */ +irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) +{ + int i, pos, irq; + u32 val, num_ctrls; + irqreturn_t ret = IRQ_NONE; + + num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; + + for (i = 0; i < num_ctrls; i++) { + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + + (i * MSI_REG_CTRL_BLOCK_SIZE), + 4, &val); + if (!val) + continue; + + ret = IRQ_HANDLED; + pos = 0; + while ((pos = find_next_bit((unsigned long *) &val, + MAX_MSI_IRQS_PER_CTRL, + pos)) != MAX_MSI_IRQS_PER_CTRL) { + irq = irq_find_mapping(pp->irq_domain, + (i * MAX_MSI_IRQS_PER_CTRL) + + pos); + generic_handle_irq(irq); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + + (i * MSI_REG_CTRL_BLOCK_SIZE), + 4, 1 << pos); + pos++; + } + } + + return ret; +} + +/* Chained MSI interrupt service routine */ +static void dw_chained_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct pcie_port *pp; + + chained_irq_enter(chip, desc); + + pp = irq_desc_get_handler_data(desc); + dw_handle_msi_irq(pp); + + chained_irq_exit(chip, desc); +} + +static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + u64 msi_target; + + if (pp->ops->get_msi_addr) + msi_target = pp->ops->get_msi_addr(pp); + else + msi_target = (u64)pp->msi_data; + + msg->address_lo = lower_32_bits(msi_target); + msg->address_hi = upper_32_bits(msi_target); + + if (pp->ops->get_msi_data) + msg->data = pp->ops->get_msi_data(pp, data->hwirq); + else + msg->data = data->hwirq; + + dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); +} + +static int dw_pci_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static void dw_pci_bottom_mask(struct irq_data *data) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned int res, bit, ctrl; + unsigned long flags; + + raw_spin_lock_irqsave(&pp->lock, flags); + + if (pp->ops->msi_clear_irq) { + pp->ops->msi_clear_irq(pp, data->hwirq); + } else { + ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL; + res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; + bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; + + pp->irq_status[ctrl] &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, + pp->irq_status[ctrl]); + } + + raw_spin_unlock_irqrestore(&pp->lock, flags); +} + +static void dw_pci_bottom_unmask(struct irq_data *data) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned int res, bit, ctrl; + unsigned long flags; + + raw_spin_lock_irqsave(&pp->lock, flags); + + if (pp->ops->msi_set_irq) { + pp->ops->msi_set_irq(pp, data->hwirq); + } else { + ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL; + res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; + bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; + + pp->irq_status[ctrl] |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, + pp->irq_status[ctrl]); + } + + raw_spin_unlock_irqrestore(&pp->lock, flags); +} + +static void dw_pci_bottom_ack(struct irq_data *d) +{ + struct msi_desc *msi = irq_data_get_msi_desc(d); + struct pcie_port *pp; + + pp = msi_desc_to_pci_sysdata(msi); + + if (pp->ops->msi_irq_ack) + pp->ops->msi_irq_ack(d->hwirq, pp); +} + +static struct irq_chip dw_pci_msi_bottom_irq_chip = { + .name = "DWPCI-MSI", + .irq_ack = dw_pci_bottom_ack, + .irq_compose_msi_msg = dw_pci_setup_msi_msg, + .irq_set_affinity = dw_pci_msi_set_affinity, + .irq_mask = dw_pci_bottom_mask, + .irq_unmask = dw_pci_bottom_unmask, +}; + +static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) +{ + struct pcie_port *pp = domain->host_data; + unsigned long flags; + u32 i; + int bit; + + raw_spin_lock_irqsave(&pp->lock, flags); + + bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors, + order_base_2(nr_irqs)); + + raw_spin_unlock_irqrestore(&pp->lock, flags); + + if (bit < 0) + return -ENOSPC; + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, bit + i, + &dw_pci_msi_bottom_irq_chip, + pp, handle_edge_irq, + NULL, NULL); + + return 0; +} + +static void dw_pcie_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned long flags; + + raw_spin_lock_irqsave(&pp->lock, flags); + + bitmap_release_region(pp->msi_irq_in_use, data->hwirq, + order_base_2(nr_irqs)); + + raw_spin_unlock_irqrestore(&pp->lock, flags); +} + +static const struct irq_domain_ops dw_pcie_msi_domain_ops = { + .alloc = dw_pcie_irq_domain_alloc, + .free = dw_pcie_irq_domain_free, +}; + +int dw_pcie_allocate_domains(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); + + pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, + &dw_pcie_msi_domain_ops, pp); + if (!pp->irq_domain) { + dev_err(pci->dev, "Failed to create IRQ domain\n"); + return -ENOMEM; + } + + pp->msi_domain = pci_msi_create_irq_domain(fwnode, + &dw_pcie_msi_domain_info, + pp->irq_domain); + if (!pp->msi_domain) { + dev_err(pci->dev, "Failed to create MSI domain\n"); + irq_domain_remove(pp->irq_domain); + return -ENOMEM; + } + + return 0; +} + +void dw_pcie_free_msi(struct pcie_port *pp) +{ + irq_set_chained_handler(pp->msi_irq, NULL); + irq_set_handler_data(pp->msi_irq, NULL); + + irq_domain_remove(pp->msi_domain); + irq_domain_remove(pp->irq_domain); +} + +void dw_pcie_msi_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct page *page; + u64 msi_target; + + page = alloc_page(GFP_KERNEL); + pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, pp->msi_data)) { + dev_err(dev, "Failed to map MSI data\n"); + __free_page(page); + return; + } + msi_target = (u64)pp->msi_data; + + /* Program the msi_data */ + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, + lower_32_bits(msi_target)); + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, + upper_32_bits(msi_target)); +} + +int dw_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; + struct platform_device *pdev = to_platform_device(dev); + struct resource_entry *win, *tmp; + struct pci_bus *bus, *child; + struct pci_host_bridge *bridge; + struct resource *cfg_res; + int ret; + + raw_spin_lock_init(&pci->pp.lock); + + cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (cfg_res) { + pp->cfg0_size = resource_size(cfg_res) >> 1; + pp->cfg1_size = resource_size(cfg_res) >> 1; + pp->cfg0_base = cfg_res->start; + pp->cfg1_base = cfg_res->start + pp->cfg0_size; + } else if (!pp->va_cfg0_base) { + dev_err(dev, "Missing *config* reg space\n"); + } + + bridge = pci_alloc_host_bridge(0); + if (!bridge) + return -ENOMEM; + + ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, + &bridge->windows, &pp->io_base); + if (ret) + return ret; + + ret = devm_request_pci_bus_resources(dev, &bridge->windows); + if (ret) + goto error; + + /* Get the I/O and memory ranges from DT */ + resource_list_for_each_entry_safe(win, tmp, &bridge->windows) { + switch (resource_type(win->res)) { + case IORESOURCE_IO: + ret = pci_remap_iospace(win->res, pp->io_base); + if (ret) { + dev_warn(dev, "Error %d: failed to map resource %pR\n", + ret, win->res); + resource_list_destroy_entry(win); + } else { + pp->io = win->res; + pp->io->name = "I/O"; + pp->io_size = resource_size(pp->io); + pp->io_bus_addr = pp->io->start - win->offset; + } + break; + case IORESOURCE_MEM: + pp->mem = win->res; + pp->mem->name = "MEM"; + pp->mem_size = resource_size(pp->mem); + pp->mem_bus_addr = pp->mem->start - win->offset; + break; + case 0: + pp->cfg = win->res; + pp->cfg0_size = resource_size(pp->cfg) >> 1; + pp->cfg1_size = resource_size(pp->cfg) >> 1; + pp->cfg0_base = pp->cfg->start; + pp->cfg1_base = pp->cfg->start + pp->cfg0_size; + break; + case IORESOURCE_BUS: + pp->busn = win->res; + break; + } + } + + if (!pci->dbi_base) { + pci->dbi_base = devm_pci_remap_cfgspace(dev, + pp->cfg->start, + resource_size(pp->cfg)); + if (!pci->dbi_base) { + dev_err(dev, "Error with ioremap\n"); + ret = -ENOMEM; + goto error; + } + } + + pp->mem_base = pp->mem->start; + + if (!pp->va_cfg0_base) { + pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, + pp->cfg0_base, pp->cfg0_size); + if (!pp->va_cfg0_base) { + dev_err(dev, "Error with ioremap in function\n"); + ret = -ENOMEM; + goto error; + } + } + + if (!pp->va_cfg1_base) { + pp->va_cfg1_base = devm_pci_remap_cfgspace(dev, + pp->cfg1_base, + pp->cfg1_size); + if (!pp->va_cfg1_base) { + dev_err(dev, "Error with ioremap\n"); + ret = -ENOMEM; + goto error; + } + } + + ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport); + if (ret) + pci->num_viewport = 2; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + /* + * If a specific SoC driver needs to change the + * default number of vectors, it needs to implement + * the set_num_vectors callback. + */ + if (!pp->ops->set_num_vectors) { + pp->num_vectors = MSI_DEF_NUM_VECTORS; + } else { + pp->ops->set_num_vectors(pp); + + if (pp->num_vectors > MAX_MSI_IRQS || + pp->num_vectors == 0) { + dev_err(dev, + "Invalid number of vectors\n"); + goto error; + } + } + + if (!pp->ops->msi_host_init) { + ret = dw_pcie_allocate_domains(pp); + if (ret) + goto error; + + if (pp->msi_irq) + irq_set_chained_handler_and_data(pp->msi_irq, + dw_chained_msi_isr, + pp); + } else { + ret = pp->ops->msi_host_init(pp); + if (ret < 0) + goto error; + } + } + + if (pp->ops->host_init) { + ret = pp->ops->host_init(pp); + if (ret) + goto error; + } + + pp->root_bus_nr = pp->busn->start; + + bridge->dev.parent = dev; + bridge->sysdata = pp; + bridge->busnr = pp->root_bus_nr; + bridge->ops = &dw_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret) + goto error; + + bus = bridge->bus; + + if (pp->ops->scan_bus) + pp->ops->scan_bus(pp); + + 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; + +error: + pci_free_host_bridge(bridge); + return ret; +} + +static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, + u32 devfn, int where, int size, u32 *val) +{ + int ret, type; + u32 busdev, cfg_size; + u64 cpu_addr; + void __iomem *va_cfg_base; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + if (pp->ops->rd_other_conf) + return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val); + + busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | + PCIE_ATU_FUNC(PCI_FUNC(devfn)); + + if (bus->parent->number == pp->root_bus_nr) { + type = PCIE_ATU_TYPE_CFG0; + cpu_addr = pp->cfg0_base; + cfg_size = pp->cfg0_size; + va_cfg_base = pp->va_cfg0_base; + } else { + type = PCIE_ATU_TYPE_CFG1; + cpu_addr = pp->cfg1_base; + cfg_size = pp->cfg1_size; + va_cfg_base = pp->va_cfg1_base; + } + + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + type, cpu_addr, + busdev, cfg_size); + ret = dw_pcie_read(va_cfg_base + where, size, val); + if (pci->num_viewport <= 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); + + return ret; +} + +static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, + u32 devfn, int where, int size, u32 val) +{ + int ret, type; + u32 busdev, cfg_size; + u64 cpu_addr; + void __iomem *va_cfg_base; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + if (pp->ops->wr_other_conf) + return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val); + + busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | + PCIE_ATU_FUNC(PCI_FUNC(devfn)); + + if (bus->parent->number == pp->root_bus_nr) { + type = PCIE_ATU_TYPE_CFG0; + cpu_addr = pp->cfg0_base; + cfg_size = pp->cfg0_size; + va_cfg_base = pp->va_cfg0_base; + } else { + type = PCIE_ATU_TYPE_CFG1; + cpu_addr = pp->cfg1_base; + cfg_size = pp->cfg1_size; + va_cfg_base = pp->va_cfg1_base; + } + + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + type, cpu_addr, + busdev, cfg_size); + ret = dw_pcie_write(va_cfg_base + where, size, val); + if (pci->num_viewport <= 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); + + return ret; +} + +static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, + int dev) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + /* If there is no link, then there is no device */ + if (bus->number != pp->root_bus_nr) { + if (!dw_pcie_link_up(pci)) + return 0; + } + + /* Access only one slot on each root port */ + if (bus->number == pp->root_bus_nr && dev > 0) + return 0; + + return 1; +} + +static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + struct pcie_port *pp = bus->sysdata; + + if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (bus->number == pp->root_bus_nr) + return dw_pcie_rd_own_conf(pp, where, size, val); + + return dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); +} + +static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct pcie_port *pp = bus->sysdata; + + if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (bus->number == pp->root_bus_nr) + return dw_pcie_wr_own_conf(pp, where, size, val); + + return dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); +} + +static struct pci_ops dw_pcie_ops = { + .read = dw_pcie_rd_conf, + .write = dw_pcie_wr_conf, +}; + +static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT); + if (val == 0xffffffff) + return 1; + + return 0; +} + +void dw_pcie_setup_rc(struct pcie_port *pp) +{ + u32 val, ctrl, num_ctrls; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + dw_pcie_setup(pci); + + num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; + + /* Initialize IRQ Status array */ + for (ctrl = 0; ctrl < num_ctrls; ctrl++) + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), + 4, &pp->irq_status[ctrl]); + + /* Setup RC BARs */ + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); + + /* Setup interrupt pins */ + dw_pcie_dbi_ro_wr_en(pci); + val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE); + val &= 0xffff00ff; + val |= 0x00000100; + dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val); + dw_pcie_dbi_ro_wr_dis(pci); + + /* Setup bus numbers */ + val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS); + val &= 0xff000000; + val |= 0x00ff0100; + dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val); + + /* Setup command register */ + val = dw_pcie_readl_dbi(pci, PCI_COMMAND); + val &= 0xffff0000; + val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_SERR; + dw_pcie_writel_dbi(pci, PCI_COMMAND, val); + + /* + * If the platform provides ->rd_other_conf, it means the platform + * uses its own address translation component rather than ATU, so + * we should not program the ATU here. + */ + if (!pp->ops->rd_other_conf) { + /* Get iATU unroll support */ + pci->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pci); + dev_dbg(pci->dev, "iATU unroll: %s\n", + pci->iatu_unroll_enabled ? "enabled" : "disabled"); + + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_MEM, pp->mem_base, + pp->mem_bus_addr, pp->mem_size); + if (pci->num_viewport > 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); + } + + dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); + + /* Enable write permission for the DBI read-only register */ + dw_pcie_dbi_ro_wr_en(pci); + /* Program correct class for RC */ + dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI); + /* Better disable write permission right after the update */ + dw_pcie_dbi_ro_wr_dis(pci); + + dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val); + val |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); +} diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c new file mode 100644 index 000000000000..5937fed4c938 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe RC driver for Synopsys DesignWare Core + * + * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com) + * + * Authors: Joao Pinto + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +struct dw_plat_pcie { + struct dw_pcie *pci; + struct regmap *regmap; + enum dw_pcie_device_mode mode; +}; + +struct dw_plat_pcie_of_data { + enum dw_pcie_device_mode mode; +}; + +static const struct of_device_id dw_plat_pcie_of_match[]; + +static int dw_plat_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + dw_pcie_setup_rc(pp); + dw_pcie_wait_for_link(pci); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + + return 0; +} + +static void dw_plat_set_num_vectors(struct pcie_port *pp) +{ + pp->num_vectors = MAX_MSI_IRQS; +} + +static const struct dw_pcie_host_ops dw_plat_pcie_host_ops = { + .host_init = dw_plat_pcie_host_init, + .set_num_vectors = dw_plat_set_num_vectors, +}; + +static int dw_plat_pcie_establish_link(struct dw_pcie *pci) +{ + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = dw_plat_pcie_establish_link, +}; + +static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar; + + for (bar = BAR_0; bar <= BAR_5; bar++) + dw_pcie_ep_reset_bar(pci, bar); +} + +static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, + enum pci_epc_irq_type type, + u8 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + switch (type) { + case PCI_EPC_IRQ_LEGACY: + dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); + return -EINVAL; + case PCI_EPC_IRQ_MSI: + return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); + default: + dev_err(pci->dev, "UNKNOWN IRQ type\n"); + } + + return 0; +} + +static struct dw_pcie_ep_ops pcie_ep_ops = { + .ep_init = dw_plat_pcie_ep_init, + .raise_irq = dw_plat_pcie_ep_raise_irq, +}; + +static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = dw_plat_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + pp->irq = platform_get_irq(pdev, 1); + if (pp->irq < 0) + return pp->irq; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (pp->msi_irq < 0) + return pp->msi_irq; + } + + pp->root_bus_nr = -1; + pp->ops = &dw_plat_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int dw_plat_add_pcie_ep(struct dw_plat_pcie *dw_plat_pcie, + struct platform_device *pdev) +{ + int ret; + struct dw_pcie_ep *ep; + struct resource *res; + struct device *dev = &pdev->dev; + struct dw_pcie *pci = dw_plat_pcie->pci; + + ep = &pci->ep; + ep->ops = &pcie_ep_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); + pci->dbi_base2 = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base2)) + return PTR_ERR(pci->dbi_base2); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); + if (!res) + return -EINVAL; + + ep->phys_base = res->start; + ep->addr_size = resource_size(res); + + ret = dw_pcie_ep_init(ep); + if (ret) { + dev_err(dev, "Failed to initialize endpoint\n"); + return ret; + } + return 0; +} + +static int dw_plat_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_plat_pcie *dw_plat_pcie; + struct dw_pcie *pci; + struct resource *res; /* Resource from DT */ + int ret; + const struct of_device_id *match; + const struct dw_plat_pcie_of_data *data; + enum dw_pcie_device_mode mode; + + match = of_match_device(dw_plat_pcie_of_match, dev); + if (!match) + return -EINVAL; + + data = (struct dw_plat_pcie_of_data *)match->data; + mode = (enum dw_pcie_device_mode)data->mode; + + dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL); + if (!dw_plat_pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + dw_plat_pcie->pci = pci; + dw_plat_pcie->mode = mode; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + platform_set_drvdata(pdev, dw_plat_pcie); + + switch (dw_plat_pcie->mode) { + case DW_PCIE_RC_TYPE: + if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_HOST)) + return -ENODEV; + + ret = dw_plat_add_pcie_port(dw_plat_pcie, pdev); + if (ret < 0) + return ret; + break; + case DW_PCIE_EP_TYPE: + if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_EP)) + return -ENODEV; + + ret = dw_plat_add_pcie_ep(dw_plat_pcie, pdev); + if (ret < 0) + return ret; + break; + default: + dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode); + } + + return 0; +} + +static const struct dw_plat_pcie_of_data dw_plat_pcie_rc_of_data = { + .mode = DW_PCIE_RC_TYPE, +}; + +static const struct dw_plat_pcie_of_data dw_plat_pcie_ep_of_data = { + .mode = DW_PCIE_EP_TYPE, +}; + +static const struct of_device_id dw_plat_pcie_of_match[] = { + { + .compatible = "snps,dw-pcie", + .data = &dw_plat_pcie_rc_of_data, + }, + { + .compatible = "snps,dw-pcie-ep", + .data = &dw_plat_pcie_ep_of_data, + }, + {}, +}; + +static struct platform_driver dw_plat_pcie_driver = { + .driver = { + .name = "dw-pcie", + .of_match_table = dw_plat_pcie_of_match, + .suppress_bind_attrs = true, + }, + .probe = dw_plat_pcie_probe, +}; +builtin_platform_driver(dw_plat_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c new file mode 100644 index 000000000000..778c4f76a884 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synopsys DesignWare PCIe host controller driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han + */ + +#include +#include +#include + +#include "pcie-designware.h" + +/* PCIe Port Logic registers */ +#define PLR_OFFSET 0x700 +#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) +#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4) +#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29) + +int dw_pcie_read(void __iomem *addr, int size, u32 *val) +{ + if ((uintptr_t)addr & (size - 1)) { + *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; +} + +int dw_pcie_write(void __iomem *addr, int size, u32 val) +{ + if ((uintptr_t)addr & (size - 1)) + 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; +} + +u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size) +{ + int ret; + u32 val; + + if (pci->ops->read_dbi) + return pci->ops->read_dbi(pci, base, reg, size); + + ret = dw_pcie_read(base + reg, size, &val); + if (ret) + dev_err(pci->dev, "Read DBI address failed\n"); + + return val; +} + +void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size, u32 val) +{ + int ret; + + if (pci->ops->write_dbi) { + pci->ops->write_dbi(pci, base, reg, size, val); + return; + } + + ret = dw_pcie_write(base + reg, size, val); + if (ret) + dev_err(pci->dev, "Write DBI address failed\n"); +} + +static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg) +{ + u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + + return dw_pcie_readl_dbi(pci, offset + reg); +} + +static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg, + u32 val) +{ + u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + + dw_pcie_writel_dbi(pci, offset + reg, val); +} + +static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index, + int type, u64 cpu_addr, + u64 pci_addr, u32 size) +{ + u32 retries, val; + + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, + type); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, + PCIE_ATU_ENABLE); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { + val = dw_pcie_readl_ob_unroll(pci, index, + PCIE_ATU_UNR_REGION_CTRL2); + if (val & PCIE_ATU_ENABLE) + return; + + usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); + } + dev_err(pci->dev, "Outbound iATU is not being enabled\n"); +} + +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, + u64 cpu_addr, u64 pci_addr, u32 size) +{ + u32 retries, val; + + if (pci->ops->cpu_addr_fixup) + cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr); + + if (pci->iatu_unroll_enabled) { + dw_pcie_prog_outbound_atu_unroll(pci, index, type, cpu_addr, + pci_addr, size); + return; + } + + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, + PCIE_ATU_REGION_OUTBOUND | index); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { + val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); + if (val & PCIE_ATU_ENABLE) + return; + + usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); + } + dev_err(pci->dev, "Outbound iATU is not being enabled\n"); +} + +static u32 dw_pcie_readl_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg) +{ + u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); + + return dw_pcie_readl_dbi(pci, offset + reg); +} + +static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg, + u32 val) +{ + u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); + + dw_pcie_writel_dbi(pci, offset + reg, val); +} + +static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index, + int bar, u64 cpu_addr, + enum dw_pcie_as_type as_type) +{ + int type; + u32 retries, val; + + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, + lower_32_bits(cpu_addr)); + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, + upper_32_bits(cpu_addr)); + + switch (as_type) { + case DW_PCIE_AS_MEM: + type = PCIE_ATU_TYPE_MEM; + break; + case DW_PCIE_AS_IO: + type = PCIE_ATU_TYPE_IO; + break; + default: + return -EINVAL; + } + + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, type); + dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, + PCIE_ATU_ENABLE | + PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { + val = dw_pcie_readl_ib_unroll(pci, index, + PCIE_ATU_UNR_REGION_CTRL2); + if (val & PCIE_ATU_ENABLE) + return 0; + + usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); + } + dev_err(pci->dev, "Inbound iATU is not being enabled\n"); + + return -EBUSY; +} + +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar, + u64 cpu_addr, enum dw_pcie_as_type as_type) +{ + int type; + u32 retries, val; + + if (pci->iatu_unroll_enabled) + return dw_pcie_prog_inbound_atu_unroll(pci, index, bar, + cpu_addr, as_type); + + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | + index); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr)); + + switch (as_type) { + case DW_PCIE_AS_MEM: + type = PCIE_ATU_TYPE_MEM; + break; + case DW_PCIE_AS_IO: + type = PCIE_ATU_TYPE_IO; + break; + default: + return -EINVAL; + } + + dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE + | PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { + val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); + if (val & PCIE_ATU_ENABLE) + return 0; + + usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); + } + dev_err(pci->dev, "Inbound iATU is not being enabled\n"); + + return -EBUSY; +} + +void dw_pcie_disable_atu(struct dw_pcie *pci, int index, + enum dw_pcie_region_type type) +{ + int region; + + switch (type) { + case DW_PCIE_REGION_INBOUND: + region = PCIE_ATU_REGION_INBOUND; + break; + case DW_PCIE_REGION_OUTBOUND: + region = PCIE_ATU_REGION_OUTBOUND; + break; + default: + return; + } + + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~PCIE_ATU_ENABLE); +} + +int dw_pcie_wait_for_link(struct dw_pcie *pci) +{ + int retries; + + /* Check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (dw_pcie_link_up(pci)) { + dev_info(pci->dev, "Link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + dev_err(pci->dev, "Phy link never came up\n"); + + return -ETIMEDOUT; +} + +int dw_pcie_link_up(struct dw_pcie *pci) +{ + u32 val; + + if (pci->ops->link_up) + return pci->ops->link_up(pci); + + val = readl(pci->dbi_base + PCIE_PHY_DEBUG_R1); + return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) && + (!(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING))); +} + +void dw_pcie_setup(struct dw_pcie *pci) +{ + int ret; + u32 val; + u32 lanes; + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; + + ret = of_property_read_u32(np, "num-lanes", &lanes); + if (ret) + lanes = 0; + + /* Set the number of lanes */ + val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); + val &= ~PORT_LINK_MODE_MASK; + switch (lanes) { + case 1: + val |= PORT_LINK_MODE_1_LANES; + break; + case 2: + val |= PORT_LINK_MODE_2_LANES; + break; + case 4: + val |= PORT_LINK_MODE_4_LANES; + break; + case 8: + val |= PORT_LINK_MODE_8_LANES; + break; + default: + dev_err(pci->dev, "num-lanes %u: invalid value\n", lanes); + return; + } + dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); + + /* Set link width speed control register */ + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_LINK_WIDTH_MASK; + switch (lanes) { + case 1: + val |= PORT_LOGIC_LINK_WIDTH_1_LANES; + break; + case 2: + val |= PORT_LOGIC_LINK_WIDTH_2_LANES; + break; + case 4: + val |= PORT_LOGIC_LINK_WIDTH_4_LANES; + break; + case 8: + val |= PORT_LOGIC_LINK_WIDTH_8_LANES; + break; + } + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); +} diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h new file mode 100644 index 000000000000..bee4e2535a61 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -0,0 +1,387 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Synopsys DesignWare PCIe host controller driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han + */ + +#ifndef _PCIE_DESIGNWARE_H +#define _PCIE_DESIGNWARE_H + +#include +#include +#include +#include + +#include +#include + +/* Parameters for the waiting for link up routine */ +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP_MIN 90000 +#define LINK_WAIT_USLEEP_MAX 100000 + +/* Parameters for the waiting for iATU enabled routine */ +#define LINK_WAIT_MAX_IATU_RETRIES 5 +#define LINK_WAIT_IATU_MIN 9000 +#define LINK_WAIT_IATU_MAX 10000 + +/* Synopsys-specific PCIe configuration registers */ +#define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_MODE_MASK (0x3f << 16) +#define PORT_LINK_MODE_1_LANES (0x1 << 16) +#define PORT_LINK_MODE_2_LANES (0x3 << 16) +#define PORT_LINK_MODE_4_LANES (0x7 << 16) +#define PORT_LINK_MODE_8_LANES (0xf << 16) + +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) +#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8) +#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) +#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) +#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) +#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8) + +#define PCIE_MSI_ADDR_LO 0x820 +#define PCIE_MSI_ADDR_HI 0x824 +#define PCIE_MSI_INTR0_ENABLE 0x828 +#define PCIE_MSI_INTR0_MASK 0x82C +#define PCIE_MSI_INTR0_STATUS 0x830 + +#define PCIE_ATU_VIEWPORT 0x900 +#define PCIE_ATU_REGION_INBOUND (0x1 << 31) +#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) +#define PCIE_ATU_REGION_INDEX2 (0x2 << 0) +#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) +#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) +#define PCIE_ATU_CR1 0x904 +#define PCIE_ATU_TYPE_MEM (0x0 << 0) +#define PCIE_ATU_TYPE_IO (0x2 << 0) +#define PCIE_ATU_TYPE_CFG0 (0x4 << 0) +#define PCIE_ATU_TYPE_CFG1 (0x5 << 0) +#define PCIE_ATU_CR2 0x908 +#define PCIE_ATU_ENABLE (0x1 << 31) +#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) +#define PCIE_ATU_LOWER_BASE 0x90C +#define PCIE_ATU_UPPER_BASE 0x910 +#define PCIE_ATU_LIMIT 0x914 +#define PCIE_ATU_LOWER_TARGET 0x918 +#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) +#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) +#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) +#define PCIE_ATU_UPPER_TARGET 0x91C + +#define PCIE_MISC_CONTROL_1_OFF 0x8BC +#define PCIE_DBI_RO_WR_EN (0x1 << 0) + +/* + * iATU Unroll-specific register definitions + * From 4.80 core version the address translation will be made by unroll + */ +#define PCIE_ATU_UNR_REGION_CTRL1 0x00 +#define PCIE_ATU_UNR_REGION_CTRL2 0x04 +#define PCIE_ATU_UNR_LOWER_BASE 0x08 +#define PCIE_ATU_UNR_UPPER_BASE 0x0C +#define PCIE_ATU_UNR_LIMIT 0x10 +#define PCIE_ATU_UNR_LOWER_TARGET 0x14 +#define PCIE_ATU_UNR_UPPER_TARGET 0x18 + +/* Register address builder */ +#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \ + ((0x3 << 20) | ((region) << 9)) + +#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \ + ((0x3 << 20) | ((region) << 9) | (0x1 << 8)) + +#define MSI_MESSAGE_CONTROL 0x52 +#define MSI_CAP_MMC_SHIFT 1 +#define MSI_CAP_MMC_MASK (7 << MSI_CAP_MMC_SHIFT) +#define MSI_CAP_MME_SHIFT 4 +#define MSI_CAP_MSI_EN_MASK 0x1 +#define MSI_CAP_MME_MASK (7 << MSI_CAP_MME_SHIFT) +#define MSI_MESSAGE_ADDR_L32 0x54 +#define MSI_MESSAGE_ADDR_U32 0x58 +#define MSI_MESSAGE_DATA_32 0x58 +#define MSI_MESSAGE_DATA_64 0x5C + +#define MAX_MSI_IRQS 256 +#define MAX_MSI_IRQS_PER_CTRL 32 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL) +#define MSI_REG_CTRL_BLOCK_SIZE 12 +#define MSI_DEF_NUM_VECTORS 32 + +/* Maximum number of inbound/outbound iATUs */ +#define MAX_IATU_IN 256 +#define MAX_IATU_OUT 256 + +struct pcie_port; +struct dw_pcie; +struct dw_pcie_ep; + +enum dw_pcie_region_type { + DW_PCIE_REGION_UNKNOWN, + DW_PCIE_REGION_INBOUND, + DW_PCIE_REGION_OUTBOUND, +}; + +enum dw_pcie_device_mode { + DW_PCIE_UNKNOWN_TYPE, + DW_PCIE_EP_TYPE, + DW_PCIE_LEG_EP_TYPE, + DW_PCIE_RC_TYPE, +}; + +struct dw_pcie_host_ops { + int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); + int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); + int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); + int (*host_init)(struct pcie_port *pp); + void (*msi_set_irq)(struct pcie_port *pp, int irq); + void (*msi_clear_irq)(struct pcie_port *pp, int irq); + phys_addr_t (*get_msi_addr)(struct pcie_port *pp); + u32 (*get_msi_data)(struct pcie_port *pp, int pos); + void (*scan_bus)(struct pcie_port *pp); + void (*set_num_vectors)(struct pcie_port *pp); + int (*msi_host_init)(struct pcie_port *pp); + void (*msi_irq_ack)(int irq, struct pcie_port *pp); +}; + +struct pcie_port { + u8 root_bus_nr; + u64 cfg0_base; + void __iomem *va_cfg0_base; + u32 cfg0_size; + u64 cfg1_base; + void __iomem *va_cfg1_base; + u32 cfg1_size; + resource_size_t io_base; + phys_addr_t io_bus_addr; + u32 io_size; + u64 mem_base; + phys_addr_t mem_bus_addr; + u32 mem_size; + struct resource *cfg; + struct resource *io; + struct resource *mem; + struct resource *busn; + int irq; + const struct dw_pcie_host_ops *ops; + int msi_irq; + struct irq_domain *irq_domain; + struct irq_domain *msi_domain; + dma_addr_t msi_data; + u32 num_vectors; + u32 irq_status[MAX_MSI_CTRLS]; + raw_spinlock_t lock; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); +}; + +enum dw_pcie_as_type { + DW_PCIE_AS_UNKNOWN, + DW_PCIE_AS_MEM, + DW_PCIE_AS_IO, +}; + +struct dw_pcie_ep_ops { + void (*ep_init)(struct dw_pcie_ep *ep); + int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, + enum pci_epc_irq_type type, u8 interrupt_num); +}; + +struct dw_pcie_ep { + struct pci_epc *epc; + struct dw_pcie_ep_ops *ops; + phys_addr_t phys_base; + size_t addr_size; + size_t page_size; + u8 bar_to_atu[6]; + phys_addr_t *outbound_addr; + unsigned long *ib_window_map; + unsigned long *ob_window_map; + u32 num_ib_windows; + u32 num_ob_windows; + void __iomem *msi_mem; + phys_addr_t msi_mem_phys; +}; + +struct dw_pcie_ops { + u64 (*cpu_addr_fixup)(struct dw_pcie *pcie, u64 cpu_addr); + u32 (*read_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, + size_t size); + void (*write_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, + size_t size, u32 val); + int (*link_up)(struct dw_pcie *pcie); + int (*start_link)(struct dw_pcie *pcie); + void (*stop_link)(struct dw_pcie *pcie); +}; + +struct dw_pcie { + struct device *dev; + void __iomem *dbi_base; + void __iomem *dbi_base2; + u32 num_viewport; + u8 iatu_unroll_enabled; + struct pcie_port pp; + struct dw_pcie_ep ep; + const struct dw_pcie_ops *ops; +}; + +#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) + +#define to_dw_pcie_from_ep(endpoint) \ + container_of((endpoint), struct dw_pcie, ep) + +int dw_pcie_read(void __iomem *addr, int size, u32 *val); +int dw_pcie_write(void __iomem *addr, int size, u32 val); + +u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size); +void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size, u32 val); +int dw_pcie_link_up(struct dw_pcie *pci); +int dw_pcie_wait_for_link(struct dw_pcie *pci); +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, + int type, u64 cpu_addr, u64 pci_addr, + u32 size); +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar, + u64 cpu_addr, enum dw_pcie_as_type as_type); +void dw_pcie_disable_atu(struct dw_pcie *pci, int index, + enum dw_pcie_region_type type); +void dw_pcie_setup(struct dw_pcie *pci); + +static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) +{ + __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x4, val); +} + +static inline u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) +{ + return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x4); +} + +static inline void dw_pcie_writew_dbi(struct dw_pcie *pci, u32 reg, u16 val) +{ + __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x2, val); +} + +static inline u16 dw_pcie_readw_dbi(struct dw_pcie *pci, u32 reg) +{ + return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x2); +} + +static inline void dw_pcie_writeb_dbi(struct dw_pcie *pci, u32 reg, u8 val) +{ + __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x1, val); +} + +static inline u8 dw_pcie_readb_dbi(struct dw_pcie *pci, u32 reg) +{ + return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x1); +} + +static inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, u32 reg, u32 val) +{ + __dw_pcie_write_dbi(pci, pci->dbi_base2, reg, 0x4, val); +} + +static inline u32 dw_pcie_readl_dbi2(struct dw_pcie *pci, u32 reg) +{ + return __dw_pcie_read_dbi(pci, pci->dbi_base2, reg, 0x4); +} + +static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci) +{ + u32 reg; + u32 val; + + reg = PCIE_MISC_CONTROL_1_OFF; + val = dw_pcie_readl_dbi(pci, reg); + val |= PCIE_DBI_RO_WR_EN; + dw_pcie_writel_dbi(pci, reg, val); +} + +static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci) +{ + u32 reg; + u32 val; + + reg = PCIE_MISC_CONTROL_1_OFF; + val = dw_pcie_readl_dbi(pci, reg); + val &= ~PCIE_DBI_RO_WR_EN; + dw_pcie_writel_dbi(pci, reg, val); +} + +#ifdef CONFIG_PCIE_DW_HOST +irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); +void dw_pcie_msi_init(struct pcie_port *pp); +void dw_pcie_free_msi(struct pcie_port *pp); +void dw_pcie_setup_rc(struct pcie_port *pp); +int dw_pcie_host_init(struct pcie_port *pp); +int dw_pcie_allocate_domains(struct pcie_port *pp); +#else +static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) +{ + return IRQ_NONE; +} + +static inline void dw_pcie_msi_init(struct pcie_port *pp) +{ +} + +static inline void dw_pcie_free_msi(struct pcie_port *pp) +{ +} + +static inline void dw_pcie_setup_rc(struct pcie_port *pp) +{ +} + +static inline int dw_pcie_host_init(struct pcie_port *pp) +{ + return 0; +} + +static inline int dw_pcie_allocate_domains(struct pcie_port *pp) +{ + return 0; +} +#endif + +#ifdef CONFIG_PCIE_DW_EP +void dw_pcie_ep_linkup(struct dw_pcie_ep *ep); +int dw_pcie_ep_init(struct dw_pcie_ep *ep); +void dw_pcie_ep_exit(struct dw_pcie_ep *ep); +int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, + u8 interrupt_num); +void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); +#else +static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) +{ +} + +static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep) +{ + return 0; +} + +static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep) +{ +} + +static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, + u8 interrupt_num) +{ + return 0; +} + +static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +{ +} +#endif +#endif /* _PCIE_DESIGNWARE_H */ diff --git a/drivers/pci/controller/dwc/pcie-hisi.c b/drivers/pci/controller/dwc/pcie-hisi.c new file mode 100644 index 000000000000..6d9e1b2b8f7b --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-hisi.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for HiSilicon SoCs + * + * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com + * + * Authors: Zhou Wang + * Dacai Zhu + * Gabriele Paoloni + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../pci.h" + +#if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) + +static int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + struct pci_config_window *cfg = bus->sysdata; + int dev = PCI_SLOT(devfn); + + if (bus->number == cfg->busr.start) { + /* access only one slot on each root port */ + if (dev > 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return pci_generic_config_read32(bus, devfn, where, + size, val); + } + + return pci_generic_config_read(bus, devfn, where, size, val); +} + +static int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct pci_config_window *cfg = bus->sysdata; + int dev = PCI_SLOT(devfn); + + if (bus->number == cfg->busr.start) { + /* access only one slot on each root port */ + if (dev > 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return pci_generic_config_write32(bus, devfn, where, + size, val); + } + + return pci_generic_config_write(bus, devfn, where, size, val); +} + +static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct pci_config_window *cfg = bus->sysdata; + void __iomem *reg_base = cfg->priv; + + if (bus->number == cfg->busr.start) + return reg_base + where; + else + return pci_ecam_map_bus(bus, devfn, where); +} + +#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) + +static int hisi_pcie_init(struct pci_config_window *cfg) +{ + struct device *dev = cfg->parent; + struct acpi_device *adev = to_acpi_device(dev); + struct acpi_pci_root *root = acpi_driver_data(adev); + struct resource *res; + void __iomem *reg_base; + int ret; + + /* + * Retrieve RC base and size from a HISI0081 device with _UID + * matching our segment. + */ + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + + ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res); + if (ret) { + dev_err(dev, "can't get rc base address\n"); + return -ENOMEM; + } + + reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); + if (!reg_base) + return -ENOMEM; + + cfg->priv = reg_base; + return 0; +} + +struct pci_ecam_ops hisi_pcie_ops = { + .bus_shift = 20, + .init = hisi_pcie_init, + .pci_ops = { + .map_bus = hisi_pcie_map_bus, + .read = hisi_pcie_rd_conf, + .write = hisi_pcie_wr_conf, + } +}; + +#endif + +#ifdef CONFIG_PCI_HISI + +#include "pcie-designware.h" + +#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 +#define PCIE_HIP06_CTRL_OFF 0x1000 +#define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c) +#define PCIE_LTSSM_LINKUP_STATE 0x11 +#define PCIE_LTSSM_STATE_MASK 0x3F + +#define to_hisi_pcie(x) dev_get_drvdata((x)->dev) + +struct hisi_pcie; + +struct pcie_soc_ops { + int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie); +}; + +struct hisi_pcie { + struct dw_pcie *pci; + struct regmap *subctrl; + u32 port_id; + const struct pcie_soc_ops *soc_ops; +}; + +/* HipXX PCIe host only supports 32-bit config access */ +static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size, + u32 *val) +{ + u32 reg; + u32 reg_val; + void *walker = ®_val; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + walker += (where & 0x3); + reg = where & ~0x3; + reg_val = dw_pcie_readl_dbi(pci, reg); + + if (size == 1) + *val = *(u8 __force *) walker; + else if (size == 2) + *val = *(u16 __force *) walker; + else if (size == 4) + *val = reg_val; + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +/* HipXX PCIe host only supports 32-bit config access */ +static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, + u32 val) +{ + u32 reg_val; + u32 reg; + void *walker = ®_val; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + walker += (where & 0x3); + reg = where & ~0x3; + if (size == 4) + dw_pcie_writel_dbi(pci, reg, val); + else if (size == 2) { + reg_val = dw_pcie_readl_dbi(pci, reg); + *(u16 __force *) walker = val; + dw_pcie_writel_dbi(pci, reg, reg_val); + } else if (size == 1) { + reg_val = dw_pcie_readl_dbi(pci, reg); + *(u8 __force *) walker = val; + dw_pcie_writel_dbi(pci, reg, reg_val); + } else + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie) +{ + u32 val; + + regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG + + 0x100 * hisi_pcie->port_id, &val); + + return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); +} + +static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie) +{ + struct dw_pcie *pci = hisi_pcie->pci; + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_SYS_STATE4); + + return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); +} + +static int hisi_pcie_link_up(struct dw_pcie *pci) +{ + struct hisi_pcie *hisi_pcie = to_hisi_pcie(pci); + + return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie); +} + +static const struct dw_pcie_host_ops hisi_pcie_host_ops = { + .rd_own_conf = hisi_pcie_cfg_read, + .wr_own_conf = hisi_pcie_cfg_write, +}; + +static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = hisi_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + u32 port_id; + + if (of_property_read_u32(dev->of_node, "port-id", &port_id)) { + dev_err(dev, "failed to read port-id\n"); + return -EINVAL; + } + if (port_id > 3) { + dev_err(dev, "Invalid port-id: %d\n", port_id); + return -EINVAL; + } + hisi_pcie->port_id = port_id; + + pp->ops = &hisi_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = hisi_pcie_link_up, +}; + +static int hisi_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct hisi_pcie *hisi_pcie; + struct resource *reg; + int ret; + + hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL); + if (!hisi_pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + hisi_pcie->pci = pci; + + hisi_pcie->soc_ops = of_device_get_match_data(dev); + + hisi_pcie->subctrl = + syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); + if (IS_ERR(hisi_pcie->subctrl)) { + dev_err(dev, "cannot get subctrl base\n"); + return PTR_ERR(hisi_pcie->subctrl); + } + + reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); + pci->dbi_base = devm_pci_remap_cfg_resource(dev, reg); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + platform_set_drvdata(pdev, hisi_pcie); + + ret = hisi_add_pcie_port(hisi_pcie, pdev); + if (ret) + return ret; + + return 0; +} + +static struct pcie_soc_ops hip05_ops = { + &hisi_pcie_link_up_hip05 +}; + +static struct pcie_soc_ops hip06_ops = { + &hisi_pcie_link_up_hip06 +}; + +static const struct of_device_id hisi_pcie_of_match[] = { + { + .compatible = "hisilicon,hip05-pcie", + .data = (void *) &hip05_ops, + }, + { + .compatible = "hisilicon,hip06-pcie", + .data = (void *) &hip06_ops, + }, + {}, +}; + +static struct platform_driver hisi_pcie_driver = { + .probe = hisi_pcie_probe, + .driver = { + .name = "hisi-pcie", + .of_match_table = hisi_pcie_of_match, + .suppress_bind_attrs = true, + }, +}; +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; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + void __iomem *reg_base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "missing \"reg[1]\"property\n"); + return -EINVAL; + } + + reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); + if (!reg_base) + return -ENOMEM; + + cfg->priv = reg_base; + return 0; +} + +struct pci_ecam_ops hisi_pcie_platform_ops = { + .bus_shift = 20, + .init = hisi_pcie_platform_init, + .pci_ops = { + .map_bus = hisi_pcie_map_bus, + .read = hisi_pcie_rd_conf, + .write = hisi_pcie_wr_conf, + } +}; + +static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { + { + .compatible = "hisilicon,hip06-pcie-ecam", + .data = (void *) &hisi_pcie_platform_ops, + }, + { + .compatible = "hisilicon,hip07-pcie-ecam", + .data = (void *) &hisi_pcie_platform_ops, + }, + {}, +}; + +static struct platform_driver hisi_pcie_almost_ecam_driver = { + .probe = hisi_pcie_almost_ecam_probe, + .driver = { + .name = "hisi-pcie-almost-ecam", + .of_match_table = hisi_pcie_almost_ecam_of_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(hisi_pcie_almost_ecam_driver); + +#endif +#endif diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c new file mode 100644 index 000000000000..3611d6ce9a92 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for HiSilicon STB SoCs + * + * Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com + * + * Authors: Ruqiang Ju + * Jianguo Sun + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define to_histb_pcie(x) dev_get_drvdata((x)->dev) + +#define PCIE_SYS_CTRL0 0x0000 +#define PCIE_SYS_CTRL1 0x0004 +#define PCIE_SYS_CTRL7 0x001C +#define PCIE_SYS_CTRL13 0x0034 +#define PCIE_SYS_CTRL15 0x003C +#define PCIE_SYS_CTRL16 0x0040 +#define PCIE_SYS_CTRL17 0x0044 + +#define PCIE_SYS_STAT0 0x0100 +#define PCIE_SYS_STAT4 0x0110 + +#define PCIE_RDLH_LINK_UP BIT(5) +#define PCIE_XMLH_LINK_UP BIT(15) +#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21) +#define PCIE_APP_LTSSM_ENABLE BIT(11) + +#define PCIE_DEVICE_TYPE_MASK GENMASK(31, 28) +#define PCIE_WM_EP 0 +#define PCIE_WM_LEGACY BIT(1) +#define PCIE_WM_RC BIT(30) + +#define PCIE_LTSSM_STATE_MASK GENMASK(5, 0) +#define PCIE_LTSSM_STATE_ACTIVE 0x11 + +struct histb_pcie { + struct dw_pcie *pci; + struct clk *aux_clk; + struct clk *pipe_clk; + struct clk *sys_clk; + struct clk *bus_clk; + struct phy *phy; + struct reset_control *soft_reset; + struct reset_control *sys_reset; + struct reset_control *bus_reset; + void __iomem *ctrl; + int reset_gpio; + struct regulator *vpcie; +}; + +static u32 histb_pcie_readl(struct histb_pcie *histb_pcie, u32 reg) +{ + return readl(histb_pcie->ctrl + reg); +} + +static void histb_pcie_writel(struct histb_pcie *histb_pcie, u32 reg, u32 val) +{ + writel(val, histb_pcie->ctrl + reg); +} + +static void histb_pcie_dbi_w_mode(struct pcie_port *pp, bool enable) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct histb_pcie *hipcie = to_histb_pcie(pci); + u32 val; + + val = histb_pcie_readl(hipcie, PCIE_SYS_CTRL0); + if (enable) + val |= PCIE_ELBI_SLV_DBI_ENABLE; + else + val &= ~PCIE_ELBI_SLV_DBI_ENABLE; + histb_pcie_writel(hipcie, PCIE_SYS_CTRL0, val); +} + +static void histb_pcie_dbi_r_mode(struct pcie_port *pp, bool enable) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct histb_pcie *hipcie = to_histb_pcie(pci); + u32 val; + + val = histb_pcie_readl(hipcie, PCIE_SYS_CTRL1); + if (enable) + val |= PCIE_ELBI_SLV_DBI_ENABLE; + else + val &= ~PCIE_ELBI_SLV_DBI_ENABLE; + histb_pcie_writel(hipcie, PCIE_SYS_CTRL1, val); +} + +static u32 histb_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size) +{ + u32 val; + + histb_pcie_dbi_r_mode(&pci->pp, true); + dw_pcie_read(base + reg, size, &val); + histb_pcie_dbi_r_mode(&pci->pp, false); + + return val; +} + +static void histb_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size, u32 val) +{ + histb_pcie_dbi_w_mode(&pci->pp, true); + dw_pcie_write(base + reg, size, val); + histb_pcie_dbi_w_mode(&pci->pp, false); +} + +static int histb_pcie_rd_own_conf(struct pcie_port *pp, int where, + int size, u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int ret; + + histb_pcie_dbi_r_mode(pp, true); + ret = dw_pcie_read(pci->dbi_base + where, size, val); + histb_pcie_dbi_r_mode(pp, false); + + return ret; +} + +static int histb_pcie_wr_own_conf(struct pcie_port *pp, int where, + int size, u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int ret; + + histb_pcie_dbi_w_mode(pp, true); + ret = dw_pcie_write(pci->dbi_base + where, size, val); + histb_pcie_dbi_w_mode(pp, false); + + return ret; +} + +static int histb_pcie_link_up(struct dw_pcie *pci) +{ + struct histb_pcie *hipcie = to_histb_pcie(pci); + u32 regval; + u32 status; + + regval = histb_pcie_readl(hipcie, PCIE_SYS_STAT0); + status = histb_pcie_readl(hipcie, PCIE_SYS_STAT4); + status &= PCIE_LTSSM_STATE_MASK; + if ((regval & PCIE_XMLH_LINK_UP) && (regval & PCIE_RDLH_LINK_UP) && + (status == PCIE_LTSSM_STATE_ACTIVE)) + return 1; + + return 0; +} + +static int histb_pcie_establish_link(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct histb_pcie *hipcie = to_histb_pcie(pci); + u32 regval; + + if (dw_pcie_link_up(pci)) { + dev_info(pci->dev, "Link already up\n"); + return 0; + } + + /* PCIe RC work mode */ + regval = histb_pcie_readl(hipcie, PCIE_SYS_CTRL0); + regval &= ~PCIE_DEVICE_TYPE_MASK; + regval |= PCIE_WM_RC; + histb_pcie_writel(hipcie, PCIE_SYS_CTRL0, regval); + + /* setup root complex */ + dw_pcie_setup_rc(pp); + + /* assert LTSSM enable */ + regval = histb_pcie_readl(hipcie, PCIE_SYS_CTRL7); + regval |= PCIE_APP_LTSSM_ENABLE; + histb_pcie_writel(hipcie, PCIE_SYS_CTRL7, regval); + + return dw_pcie_wait_for_link(pci); +} + +static int histb_pcie_host_init(struct pcie_port *pp) +{ + histb_pcie_establish_link(pp); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + + return 0; +} + +static struct dw_pcie_host_ops histb_pcie_host_ops = { + .rd_own_conf = histb_pcie_rd_own_conf, + .wr_own_conf = histb_pcie_wr_own_conf, + .host_init = histb_pcie_host_init, +}; + +static void histb_pcie_host_disable(struct histb_pcie *hipcie) +{ + reset_control_assert(hipcie->soft_reset); + reset_control_assert(hipcie->sys_reset); + reset_control_assert(hipcie->bus_reset); + + clk_disable_unprepare(hipcie->aux_clk); + clk_disable_unprepare(hipcie->pipe_clk); + clk_disable_unprepare(hipcie->sys_clk); + clk_disable_unprepare(hipcie->bus_clk); + + if (gpio_is_valid(hipcie->reset_gpio)) + gpio_set_value_cansleep(hipcie->reset_gpio, 0); + + if (hipcie->vpcie) + regulator_disable(hipcie->vpcie); +} + +static int histb_pcie_host_enable(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct histb_pcie *hipcie = to_histb_pcie(pci); + struct device *dev = pci->dev; + int ret; + + /* power on PCIe device if have */ + if (hipcie->vpcie) { + ret = regulator_enable(hipcie->vpcie); + if (ret) { + dev_err(dev, "failed to enable regulator: %d\n", ret); + return ret; + } + } + + if (gpio_is_valid(hipcie->reset_gpio)) + gpio_set_value_cansleep(hipcie->reset_gpio, 1); + + ret = clk_prepare_enable(hipcie->bus_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable bus clk\n"); + goto err_bus_clk; + } + + ret = clk_prepare_enable(hipcie->sys_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable sys clk\n"); + goto err_sys_clk; + } + + ret = clk_prepare_enable(hipcie->pipe_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable pipe clk\n"); + goto err_pipe_clk; + } + + ret = clk_prepare_enable(hipcie->aux_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable aux clk\n"); + goto err_aux_clk; + } + + reset_control_assert(hipcie->soft_reset); + reset_control_deassert(hipcie->soft_reset); + + reset_control_assert(hipcie->sys_reset); + reset_control_deassert(hipcie->sys_reset); + + reset_control_assert(hipcie->bus_reset); + reset_control_deassert(hipcie->bus_reset); + + return 0; + +err_aux_clk: + clk_disable_unprepare(hipcie->pipe_clk); +err_pipe_clk: + clk_disable_unprepare(hipcie->sys_clk); +err_sys_clk: + clk_disable_unprepare(hipcie->bus_clk); +err_bus_clk: + if (hipcie->vpcie) + regulator_disable(hipcie->vpcie); + + return ret; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .read_dbi = histb_pcie_read_dbi, + .write_dbi = histb_pcie_write_dbi, + .link_up = histb_pcie_link_up, +}; + +static int histb_pcie_probe(struct platform_device *pdev) +{ + struct histb_pcie *hipcie; + struct dw_pcie *pci; + struct pcie_port *pp; + struct resource *res; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + enum of_gpio_flags of_flags; + unsigned long flag = GPIOF_DIR_OUT; + int ret; + + hipcie = devm_kzalloc(dev, sizeof(*hipcie), GFP_KERNEL); + if (!hipcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + hipcie->pci = pci; + pp = &pci->pp; + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); + hipcie->ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(hipcie->ctrl)) { + dev_err(dev, "cannot get control reg base\n"); + return PTR_ERR(hipcie->ctrl); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc-dbi"); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) { + dev_err(dev, "cannot get rc-dbi base\n"); + return PTR_ERR(pci->dbi_base); + } + + hipcie->vpcie = devm_regulator_get_optional(dev, "vpcie"); + if (IS_ERR(hipcie->vpcie)) { + if (PTR_ERR(hipcie->vpcie) == -EPROBE_DEFER) + return -EPROBE_DEFER; + hipcie->vpcie = NULL; + } + + hipcie->reset_gpio = of_get_named_gpio_flags(np, + "reset-gpios", 0, &of_flags); + if (of_flags & OF_GPIO_ACTIVE_LOW) + flag |= GPIOF_ACTIVE_LOW; + if (gpio_is_valid(hipcie->reset_gpio)) { + ret = devm_gpio_request_one(dev, hipcie->reset_gpio, + flag, "PCIe device power control"); + if (ret) { + dev_err(dev, "unable to request gpio\n"); + return ret; + } + } + + hipcie->aux_clk = devm_clk_get(dev, "aux"); + if (IS_ERR(hipcie->aux_clk)) { + dev_err(dev, "Failed to get PCIe aux clk\n"); + return PTR_ERR(hipcie->aux_clk); + } + + hipcie->pipe_clk = devm_clk_get(dev, "pipe"); + if (IS_ERR(hipcie->pipe_clk)) { + dev_err(dev, "Failed to get PCIe pipe clk\n"); + return PTR_ERR(hipcie->pipe_clk); + } + + hipcie->sys_clk = devm_clk_get(dev, "sys"); + if (IS_ERR(hipcie->sys_clk)) { + dev_err(dev, "Failed to get PCIEe sys clk\n"); + return PTR_ERR(hipcie->sys_clk); + } + + hipcie->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(hipcie->bus_clk)) { + dev_err(dev, "Failed to get PCIe bus clk\n"); + return PTR_ERR(hipcie->bus_clk); + } + + hipcie->soft_reset = devm_reset_control_get(dev, "soft"); + if (IS_ERR(hipcie->soft_reset)) { + dev_err(dev, "couldn't get soft reset\n"); + return PTR_ERR(hipcie->soft_reset); + } + + hipcie->sys_reset = devm_reset_control_get(dev, "sys"); + if (IS_ERR(hipcie->sys_reset)) { + dev_err(dev, "couldn't get sys reset\n"); + return PTR_ERR(hipcie->sys_reset); + } + + hipcie->bus_reset = devm_reset_control_get(dev, "bus"); + if (IS_ERR(hipcie->bus_reset)) { + dev_err(dev, "couldn't get bus reset\n"); + return PTR_ERR(hipcie->bus_reset); + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pp->msi_irq < 0) { + dev_err(dev, "Failed to get MSI IRQ\n"); + return pp->msi_irq; + } + } + + hipcie->phy = devm_phy_get(dev, "phy"); + if (IS_ERR(hipcie->phy)) { + dev_info(dev, "no pcie-phy found\n"); + hipcie->phy = NULL; + /* fall through here! + * if no pcie-phy found, phy init + * should be done under boot! + */ + } else { + phy_init(hipcie->phy); + } + + pp->root_bus_nr = -1; + pp->ops = &histb_pcie_host_ops; + + platform_set_drvdata(pdev, hipcie); + + ret = histb_pcie_host_enable(pp); + if (ret) { + dev_err(dev, "failed to enable host\n"); + return ret; + } + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int histb_pcie_remove(struct platform_device *pdev) +{ + struct histb_pcie *hipcie = platform_get_drvdata(pdev); + + histb_pcie_host_disable(hipcie); + + if (hipcie->phy) + phy_exit(hipcie->phy); + + return 0; +} + +static const struct of_device_id histb_pcie_of_match[] = { + { .compatible = "hisilicon,hi3798cv200-pcie", }, + {}, +}; +MODULE_DEVICE_TABLE(of, histb_pcie_of_match); + +static struct platform_driver histb_pcie_platform_driver = { + .probe = histb_pcie_probe, + .remove = histb_pcie_remove, + .driver = { + .name = "histb-pcie", + .of_match_table = histb_pcie_of_match, + }, +}; +module_platform_driver(histb_pcie_platform_driver); + +MODULE_DESCRIPTION("HiSilicon STB PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c new file mode 100644 index 000000000000..d2970a009eb5 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Kirin Phone SoCs + * + * Copyright (C) 2017 Hilisicon Electronics Co., Ltd. + * http://www.huawei.com + * + * Author: Xiaowei Song + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcie-designware.h" + +#define to_kirin_pcie(x) dev_get_drvdata((x)->dev) + +#define REF_CLK_FREQ 100000000 + +/* PCIe ELBI registers */ +#define SOC_PCIECTRL_CTRL0_ADDR 0x000 +#define SOC_PCIECTRL_CTRL1_ADDR 0x004 +#define SOC_PCIEPHY_CTRL2_ADDR 0x008 +#define SOC_PCIEPHY_CTRL3_ADDR 0x00c +#define PCIE_ELBI_SLV_DBI_ENABLE (0x1 << 21) + +/* info located in APB */ +#define PCIE_APP_LTSSM_ENABLE 0x01c +#define PCIE_APB_PHY_CTRL0 0x0 +#define PCIE_APB_PHY_CTRL1 0x4 +#define PCIE_APB_PHY_STATUS0 0x400 +#define PCIE_LINKUP_ENABLE (0x8020) +#define PCIE_LTSSM_ENABLE_BIT (0x1 << 11) +#define PIPE_CLK_STABLE (0x1 << 19) +#define PHY_REF_PAD_BIT (0x1 << 8) +#define PHY_PWR_DOWN_BIT (0x1 << 22) +#define PHY_RST_ACK_BIT (0x1 << 16) + +/* info located in sysctrl */ +#define SCTRL_PCIE_CMOS_OFFSET 0x60 +#define SCTRL_PCIE_CMOS_BIT 0x10 +#define SCTRL_PCIE_ISO_OFFSET 0x44 +#define SCTRL_PCIE_ISO_BIT 0x30 +#define SCTRL_PCIE_HPCLK_OFFSET 0x190 +#define SCTRL_PCIE_HPCLK_BIT 0x184000 +#define SCTRL_PCIE_OE_OFFSET 0x14a +#define PCIE_DEBOUNCE_PARAM 0xF0F400 +#define PCIE_OE_BYPASS (0x3 << 28) + +/* peri_crg ctrl */ +#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88 +#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000 + +/* Time for delay */ +#define REF_2_PERST_MIN 20000 +#define REF_2_PERST_MAX 25000 +#define PERST_2_ACCESS_MIN 10000 +#define PERST_2_ACCESS_MAX 12000 +#define LINK_WAIT_MIN 900 +#define LINK_WAIT_MAX 1000 +#define PIPE_CLK_WAIT_MIN 550 +#define PIPE_CLK_WAIT_MAX 600 +#define TIME_CMOS_MIN 100 +#define TIME_CMOS_MAX 105 +#define TIME_PHY_PD_MIN 10 +#define TIME_PHY_PD_MAX 11 + +struct kirin_pcie { + struct dw_pcie *pci; + void __iomem *apb_base; + void __iomem *phy_base; + struct regmap *crgctrl; + struct regmap *sysctrl; + struct clk *apb_sys_clk; + struct clk *apb_phy_clk; + struct clk *phy_ref_clk; + struct clk *pcie_aclk; + struct clk *pcie_aux_clk; + int gpio_id_reset; +}; + +/* Registers in PCIeCTRL */ +static inline void kirin_apb_ctrl_writel(struct kirin_pcie *kirin_pcie, + u32 val, u32 reg) +{ + writel(val, kirin_pcie->apb_base + reg); +} + +static inline u32 kirin_apb_ctrl_readl(struct kirin_pcie *kirin_pcie, u32 reg) +{ + return readl(kirin_pcie->apb_base + reg); +} + +/* Registers in PCIePHY */ +static inline void kirin_apb_phy_writel(struct kirin_pcie *kirin_pcie, + u32 val, u32 reg) +{ + writel(val, kirin_pcie->phy_base + reg); +} + +static inline u32 kirin_apb_phy_readl(struct kirin_pcie *kirin_pcie, u32 reg) +{ + return readl(kirin_pcie->phy_base + reg); +} + +static long kirin_pcie_get_clk(struct kirin_pcie *kirin_pcie, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + kirin_pcie->phy_ref_clk = devm_clk_get(dev, "pcie_phy_ref"); + if (IS_ERR(kirin_pcie->phy_ref_clk)) + return PTR_ERR(kirin_pcie->phy_ref_clk); + + kirin_pcie->pcie_aux_clk = devm_clk_get(dev, "pcie_aux"); + if (IS_ERR(kirin_pcie->pcie_aux_clk)) + return PTR_ERR(kirin_pcie->pcie_aux_clk); + + kirin_pcie->apb_phy_clk = devm_clk_get(dev, "pcie_apb_phy"); + if (IS_ERR(kirin_pcie->apb_phy_clk)) + return PTR_ERR(kirin_pcie->apb_phy_clk); + + kirin_pcie->apb_sys_clk = devm_clk_get(dev, "pcie_apb_sys"); + if (IS_ERR(kirin_pcie->apb_sys_clk)) + return PTR_ERR(kirin_pcie->apb_sys_clk); + + kirin_pcie->pcie_aclk = devm_clk_get(dev, "pcie_aclk"); + if (IS_ERR(kirin_pcie->pcie_aclk)) + return PTR_ERR(kirin_pcie->pcie_aclk); + + return 0; +} + +static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *apb; + struct resource *phy; + struct resource *dbi; + + apb = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb"); + kirin_pcie->apb_base = devm_ioremap_resource(dev, apb); + if (IS_ERR(kirin_pcie->apb_base)) + return PTR_ERR(kirin_pcie->apb_base); + + phy = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + kirin_pcie->phy_base = devm_ioremap_resource(dev, phy); + if (IS_ERR(kirin_pcie->phy_base)) + return PTR_ERR(kirin_pcie->phy_base); + + dbi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + kirin_pcie->pci->dbi_base = devm_ioremap_resource(dev, dbi); + if (IS_ERR(kirin_pcie->pci->dbi_base)) + return PTR_ERR(kirin_pcie->pci->dbi_base); + + kirin_pcie->crgctrl = + syscon_regmap_lookup_by_compatible("hisilicon,hi3660-crgctrl"); + if (IS_ERR(kirin_pcie->crgctrl)) + return PTR_ERR(kirin_pcie->crgctrl); + + kirin_pcie->sysctrl = + syscon_regmap_lookup_by_compatible("hisilicon,hi3660-sctrl"); + if (IS_ERR(kirin_pcie->sysctrl)) + return PTR_ERR(kirin_pcie->sysctrl); + + return 0; +} + +static int kirin_pcie_phy_init(struct kirin_pcie *kirin_pcie) +{ + struct device *dev = kirin_pcie->pci->dev; + u32 reg_val; + + reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_CTRL1); + reg_val &= ~PHY_REF_PAD_BIT; + kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL1); + + reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_CTRL0); + reg_val &= ~PHY_PWR_DOWN_BIT; + kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL0); + usleep_range(TIME_PHY_PD_MIN, TIME_PHY_PD_MAX); + + reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_CTRL1); + reg_val &= ~PHY_RST_ACK_BIT; + kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL1); + + usleep_range(PIPE_CLK_WAIT_MIN, PIPE_CLK_WAIT_MAX); + reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_STATUS0); + if (reg_val & PIPE_CLK_STABLE) { + dev_err(dev, "PIPE clk is not stable\n"); + return -EINVAL; + } + + return 0; +} + +static void kirin_pcie_oe_enable(struct kirin_pcie *kirin_pcie) +{ + u32 val; + + regmap_read(kirin_pcie->sysctrl, SCTRL_PCIE_OE_OFFSET, &val); + val |= PCIE_DEBOUNCE_PARAM; + val &= ~PCIE_OE_BYPASS; + regmap_write(kirin_pcie->sysctrl, SCTRL_PCIE_OE_OFFSET, val); +} + +static int kirin_pcie_clk_ctrl(struct kirin_pcie *kirin_pcie, bool enable) +{ + int ret = 0; + + if (!enable) + goto close_clk; + + ret = clk_set_rate(kirin_pcie->phy_ref_clk, REF_CLK_FREQ); + if (ret) + return ret; + + ret = clk_prepare_enable(kirin_pcie->phy_ref_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(kirin_pcie->apb_sys_clk); + if (ret) + goto apb_sys_fail; + + ret = clk_prepare_enable(kirin_pcie->apb_phy_clk); + if (ret) + goto apb_phy_fail; + + ret = clk_prepare_enable(kirin_pcie->pcie_aclk); + if (ret) + goto aclk_fail; + + ret = clk_prepare_enable(kirin_pcie->pcie_aux_clk); + if (ret) + goto aux_clk_fail; + + return 0; + +close_clk: + clk_disable_unprepare(kirin_pcie->pcie_aux_clk); +aux_clk_fail: + clk_disable_unprepare(kirin_pcie->pcie_aclk); +aclk_fail: + clk_disable_unprepare(kirin_pcie->apb_phy_clk); +apb_phy_fail: + clk_disable_unprepare(kirin_pcie->apb_sys_clk); +apb_sys_fail: + clk_disable_unprepare(kirin_pcie->phy_ref_clk); + + return ret; +} + +static int kirin_pcie_power_on(struct kirin_pcie *kirin_pcie) +{ + int ret; + + /* Power supply for Host */ + regmap_write(kirin_pcie->sysctrl, + SCTRL_PCIE_CMOS_OFFSET, SCTRL_PCIE_CMOS_BIT); + usleep_range(TIME_CMOS_MIN, TIME_CMOS_MAX); + kirin_pcie_oe_enable(kirin_pcie); + + ret = kirin_pcie_clk_ctrl(kirin_pcie, true); + if (ret) + return ret; + + /* ISO disable, PCIeCtrl, PHY assert and clk gate clear */ + regmap_write(kirin_pcie->sysctrl, + SCTRL_PCIE_ISO_OFFSET, SCTRL_PCIE_ISO_BIT); + regmap_write(kirin_pcie->crgctrl, + CRGCTRL_PCIE_ASSERT_OFFSET, CRGCTRL_PCIE_ASSERT_BIT); + regmap_write(kirin_pcie->sysctrl, + SCTRL_PCIE_HPCLK_OFFSET, SCTRL_PCIE_HPCLK_BIT); + + ret = kirin_pcie_phy_init(kirin_pcie); + if (ret) + goto close_clk; + + /* perst assert Endpoint */ + if (!gpio_request(kirin_pcie->gpio_id_reset, "pcie_perst")) { + usleep_range(REF_2_PERST_MIN, REF_2_PERST_MAX); + ret = gpio_direction_output(kirin_pcie->gpio_id_reset, 1); + if (ret) + goto close_clk; + usleep_range(PERST_2_ACCESS_MIN, PERST_2_ACCESS_MAX); + + return 0; + } + +close_clk: + kirin_pcie_clk_ctrl(kirin_pcie, false); + return ret; +} + +static void kirin_pcie_sideband_dbi_w_mode(struct kirin_pcie *kirin_pcie, + bool on) +{ + u32 val; + + val = kirin_apb_ctrl_readl(kirin_pcie, SOC_PCIECTRL_CTRL0_ADDR); + if (on) + val = val | PCIE_ELBI_SLV_DBI_ENABLE; + else + val = val & ~PCIE_ELBI_SLV_DBI_ENABLE; + + kirin_apb_ctrl_writel(kirin_pcie, val, SOC_PCIECTRL_CTRL0_ADDR); +} + +static void kirin_pcie_sideband_dbi_r_mode(struct kirin_pcie *kirin_pcie, + bool on) +{ + u32 val; + + val = kirin_apb_ctrl_readl(kirin_pcie, SOC_PCIECTRL_CTRL1_ADDR); + if (on) + val = val | PCIE_ELBI_SLV_DBI_ENABLE; + else + val = val & ~PCIE_ELBI_SLV_DBI_ENABLE; + + kirin_apb_ctrl_writel(kirin_pcie, val, SOC_PCIECTRL_CTRL1_ADDR); +} + +static int kirin_pcie_rd_own_conf(struct pcie_port *pp, + int where, int size, u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); + int ret; + + kirin_pcie_sideband_dbi_r_mode(kirin_pcie, true); + ret = dw_pcie_read(pci->dbi_base + where, size, val); + kirin_pcie_sideband_dbi_r_mode(kirin_pcie, false); + + return ret; +} + +static int kirin_pcie_wr_own_conf(struct pcie_port *pp, + int where, int size, u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); + int ret; + + kirin_pcie_sideband_dbi_w_mode(kirin_pcie, true); + ret = dw_pcie_write(pci->dbi_base + where, size, val); + kirin_pcie_sideband_dbi_w_mode(kirin_pcie, false); + + return ret; +} + +static u32 kirin_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size) +{ + struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); + u32 ret; + + kirin_pcie_sideband_dbi_r_mode(kirin_pcie, true); + dw_pcie_read(base + reg, size, &ret); + kirin_pcie_sideband_dbi_r_mode(kirin_pcie, false); + + return ret; +} + +static void kirin_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size, u32 val) +{ + struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); + + kirin_pcie_sideband_dbi_w_mode(kirin_pcie, true); + dw_pcie_write(base + reg, size, val); + kirin_pcie_sideband_dbi_w_mode(kirin_pcie, false); +} + +static int kirin_pcie_link_up(struct dw_pcie *pci) +{ + struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); + u32 val = kirin_apb_ctrl_readl(kirin_pcie, PCIE_APB_PHY_STATUS0); + + if ((val & PCIE_LINKUP_ENABLE) == PCIE_LINKUP_ENABLE) + return 1; + + return 0; +} + +static int kirin_pcie_establish_link(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); + struct device *dev = kirin_pcie->pci->dev; + int count = 0; + + if (kirin_pcie_link_up(pci)) + return 0; + + dw_pcie_setup_rc(pp); + + /* assert LTSSM enable */ + kirin_apb_ctrl_writel(kirin_pcie, PCIE_LTSSM_ENABLE_BIT, + PCIE_APP_LTSSM_ENABLE); + + /* check if the link is up or not */ + while (!kirin_pcie_link_up(pci)) { + usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); + count++; + if (count == 1000) { + dev_err(dev, "Link Fail\n"); + return -EINVAL; + } + } + + return 0; +} + +static int kirin_pcie_host_init(struct pcie_port *pp) +{ + kirin_pcie_establish_link(pp); + + return 0; +} + +static struct dw_pcie_ops kirin_dw_pcie_ops = { + .read_dbi = kirin_pcie_read_dbi, + .write_dbi = kirin_pcie_write_dbi, + .link_up = kirin_pcie_link_up, +}; + +static const struct dw_pcie_host_ops kirin_pcie_host_ops = { + .rd_own_conf = kirin_pcie_rd_own_conf, + .wr_own_conf = kirin_pcie_wr_own_conf, + .host_init = kirin_pcie_host_init, +}; + +static int __init kirin_add_pcie_port(struct dw_pcie *pci, + struct platform_device *pdev) +{ + pci->pp.ops = &kirin_pcie_host_ops; + + return dw_pcie_host_init(&pci->pp); +} + +static int kirin_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct kirin_pcie *kirin_pcie; + struct dw_pcie *pci; + int ret; + + if (!dev->of_node) { + dev_err(dev, "NULL node\n"); + return -EINVAL; + } + + kirin_pcie = devm_kzalloc(dev, sizeof(struct kirin_pcie), GFP_KERNEL); + if (!kirin_pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &kirin_dw_pcie_ops; + kirin_pcie->pci = pci; + + ret = kirin_pcie_get_clk(kirin_pcie, pdev); + if (ret) + return ret; + + ret = kirin_pcie_get_resource(kirin_pcie, pdev); + if (ret) + return ret; + + kirin_pcie->gpio_id_reset = of_get_named_gpio(dev->of_node, + "reset-gpios", 0); + if (kirin_pcie->gpio_id_reset < 0) + return -ENODEV; + + ret = kirin_pcie_power_on(kirin_pcie); + if (ret) + return ret; + + platform_set_drvdata(pdev, kirin_pcie); + + return kirin_add_pcie_port(pci, pdev); +} + +static const struct of_device_id kirin_pcie_match[] = { + { .compatible = "hisilicon,kirin960-pcie" }, + {}, +}; + +static struct platform_driver kirin_pcie_driver = { + .probe = kirin_pcie_probe, + .driver = { + .name = "kirin-pcie", + .of_match_table = kirin_pcie_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(kirin_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c new file mode 100644 index 000000000000..a1d0198081a6 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -0,0 +1,1299 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm PCIe root complex driver + * + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright 2015 Linaro Limited. + * + * Author: Stanimir Varbanov + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define PCIE20_PARF_SYS_CTRL 0x00 +#define MST_WAKEUP_EN BIT(13) +#define SLV_WAKEUP_EN BIT(12) +#define MSTR_ACLK_CGC_DIS BIT(10) +#define SLV_ACLK_CGC_DIS BIT(9) +#define CORE_CLK_CGC_DIS BIT(6) +#define AUX_PWR_DET BIT(4) +#define L23_CLK_RMV_DIS BIT(2) +#define L1_CLK_RMV_DIS BIT(1) + +#define PCIE20_COMMAND_STATUS 0x04 +#define CMD_BME_VAL 0x4 +#define PCIE20_DEVICE_CONTROL2_STATUS2 0x98 +#define PCIE_CAP_CPL_TIMEOUT_DISABLE 0x10 + +#define PCIE20_PARF_PHY_CTRL 0x40 +#define PCIE20_PARF_PHY_REFCLK 0x4C +#define PCIE20_PARF_DBI_BASE_ADDR 0x168 +#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C +#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174 +#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 +#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 +#define PCIE20_PARF_LTSSM 0x1B0 +#define PCIE20_PARF_SID_OFFSET 0x234 +#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C + +#define PCIE20_ELBI_SYS_CTRL 0x04 +#define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) + +#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 +#define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 +#define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_4K 0x5 +#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c +#define CFG_BRIDGE_SB_INIT BIT(0) + +#define PCIE20_CAP 0x70 +#define PCIE20_CAP_LINK_CAPABILITIES (PCIE20_CAP + 0xC) +#define PCIE20_CAP_ACTIVE_STATE_LINK_PM_SUPPORT (BIT(10) | BIT(11)) +#define PCIE20_CAP_LINK_1 (PCIE20_CAP + 0x14) +#define PCIE_CAP_LINK1_VAL 0x2FD7F + +#define PCIE20_PARF_Q2A_FLUSH 0x1AC + +#define PCIE20_MISC_CONTROL_1_REG 0x8BC +#define DBI_RO_WR_EN 1 + +#define PERST_DELAY_US 1000 + +#define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 +#define SLV_ADDR_SPACE_SZ 0x10000000 + +#define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 +struct qcom_pcie_resources_2_1_0 { + struct clk *iface_clk; + struct clk *core_clk; + struct clk *phy_clk; + struct reset_control *pci_reset; + struct reset_control *axi_reset; + struct reset_control *ahb_reset; + struct reset_control *por_reset; + struct reset_control *phy_reset; + struct regulator_bulk_data supplies[QCOM_PCIE_2_1_0_MAX_SUPPLY]; +}; + +struct qcom_pcie_resources_1_0_0 { + struct clk *iface; + struct clk *aux; + struct clk *master_bus; + struct clk *slave_bus; + struct reset_control *core; + struct regulator *vdda; +}; + +#define QCOM_PCIE_2_3_2_MAX_SUPPLY 2 +struct qcom_pcie_resources_2_3_2 { + struct clk *aux_clk; + struct clk *master_clk; + struct clk *slave_clk; + struct clk *cfg_clk; + struct clk *pipe_clk; + struct regulator_bulk_data supplies[QCOM_PCIE_2_3_2_MAX_SUPPLY]; +}; + +struct qcom_pcie_resources_2_4_0 { + struct clk *aux_clk; + struct clk *master_clk; + struct clk *slave_clk; + struct reset_control *axi_m_reset; + struct reset_control *axi_s_reset; + struct reset_control *pipe_reset; + struct reset_control *axi_m_vmid_reset; + struct reset_control *axi_s_xpu_reset; + struct reset_control *parf_reset; + struct reset_control *phy_reset; + struct reset_control *axi_m_sticky_reset; + struct reset_control *pipe_sticky_reset; + struct reset_control *pwr_reset; + struct reset_control *ahb_reset; + struct reset_control *phy_ahb_reset; +}; + +struct qcom_pcie_resources_2_3_3 { + struct clk *iface; + struct clk *axi_m_clk; + struct clk *axi_s_clk; + struct clk *ahb_clk; + struct clk *aux_clk; + struct reset_control *rst[7]; +}; + +union qcom_pcie_resources { + struct qcom_pcie_resources_1_0_0 v1_0_0; + struct qcom_pcie_resources_2_1_0 v2_1_0; + struct qcom_pcie_resources_2_3_2 v2_3_2; + struct qcom_pcie_resources_2_3_3 v2_3_3; + struct qcom_pcie_resources_2_4_0 v2_4_0; +}; + +struct qcom_pcie; + +struct qcom_pcie_ops { + int (*get_resources)(struct qcom_pcie *pcie); + int (*init)(struct qcom_pcie *pcie); + int (*post_init)(struct qcom_pcie *pcie); + void (*deinit)(struct qcom_pcie *pcie); + void (*post_deinit)(struct qcom_pcie *pcie); + void (*ltssm_enable)(struct qcom_pcie *pcie); +}; + +struct qcom_pcie { + struct dw_pcie *pci; + void __iomem *parf; /* DT parf */ + void __iomem *elbi; /* DT elbi */ + union qcom_pcie_resources res; + struct phy *phy; + struct gpio_desc *reset; + const struct qcom_pcie_ops *ops; +}; + +#define to_qcom_pcie(x) dev_get_drvdata((x)->dev) + +static void qcom_ep_reset_assert(struct qcom_pcie *pcie) +{ + gpiod_set_value_cansleep(pcie->reset, 1); + usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); +} + +static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) +{ + gpiod_set_value_cansleep(pcie->reset, 0); + usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); +} + +static int qcom_pcie_establish_link(struct qcom_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + + if (dw_pcie_link_up(pci)) + return 0; + + /* Enable Link Training state machine */ + if (pcie->ops->ltssm_enable) + pcie->ops->ltssm_enable(pcie); + + return dw_pcie_wait_for_link(pci); +} + +static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) +{ + u32 val; + + /* enable link training */ + val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL); + val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE; + writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL); +} + +static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int ret; + + res->supplies[0].supply = "vdda"; + res->supplies[1].supply = "vdda_phy"; + res->supplies[2].supply = "vdda_refclk"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), + res->supplies); + if (ret) + return ret; + + res->iface_clk = devm_clk_get(dev, "iface"); + if (IS_ERR(res->iface_clk)) + return PTR_ERR(res->iface_clk); + + res->core_clk = devm_clk_get(dev, "core"); + if (IS_ERR(res->core_clk)) + return PTR_ERR(res->core_clk); + + res->phy_clk = devm_clk_get(dev, "phy"); + if (IS_ERR(res->phy_clk)) + return PTR_ERR(res->phy_clk); + + res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); + if (IS_ERR(res->pci_reset)) + return PTR_ERR(res->pci_reset); + + res->axi_reset = devm_reset_control_get_exclusive(dev, "axi"); + if (IS_ERR(res->axi_reset)) + return PTR_ERR(res->axi_reset); + + res->ahb_reset = devm_reset_control_get_exclusive(dev, "ahb"); + if (IS_ERR(res->ahb_reset)) + return PTR_ERR(res->ahb_reset); + + res->por_reset = devm_reset_control_get_exclusive(dev, "por"); + if (IS_ERR(res->por_reset)) + return PTR_ERR(res->por_reset); + + res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); + return PTR_ERR_OR_ZERO(res->phy_reset); +} + +static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; + + reset_control_assert(res->pci_reset); + reset_control_assert(res->axi_reset); + reset_control_assert(res->ahb_reset); + reset_control_assert(res->por_reset); + reset_control_assert(res->pci_reset); + clk_disable_unprepare(res->iface_clk); + clk_disable_unprepare(res->core_clk); + clk_disable_unprepare(res->phy_clk); + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); +} + +static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + u32 val; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); + if (ret < 0) { + dev_err(dev, "cannot enable regulators\n"); + return ret; + } + + ret = reset_control_assert(res->ahb_reset); + if (ret) { + dev_err(dev, "cannot assert ahb reset\n"); + goto err_assert_ahb; + } + + ret = clk_prepare_enable(res->iface_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable iface clock\n"); + goto err_assert_ahb; + } + + ret = clk_prepare_enable(res->phy_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable phy clock\n"); + goto err_clk_phy; + } + + ret = clk_prepare_enable(res->core_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable core clock\n"); + goto err_clk_core; + } + + ret = reset_control_deassert(res->ahb_reset); + if (ret) { + dev_err(dev, "cannot deassert ahb reset\n"); + goto err_deassert_ahb; + } + + /* enable PCIe clocks and resets */ + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~BIT(0); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + + /* enable external reference clock */ + val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); + val |= BIT(16); + writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); + + ret = reset_control_deassert(res->phy_reset); + if (ret) { + dev_err(dev, "cannot deassert phy reset\n"); + return ret; + } + + ret = reset_control_deassert(res->pci_reset); + if (ret) { + dev_err(dev, "cannot deassert pci reset\n"); + return ret; + } + + ret = reset_control_deassert(res->por_reset); + if (ret) { + dev_err(dev, "cannot deassert por reset\n"); + return ret; + } + + ret = reset_control_deassert(res->axi_reset); + if (ret) { + dev_err(dev, "cannot deassert axi reset\n"); + return ret; + } + + /* wait for clock acquisition */ + usleep_range(1000, 1500); + + + /* Set the Max TLP size to 2K, instead of using default of 4K */ + writel(CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K, + pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); + writel(CFG_BRIDGE_SB_INIT, + pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); + + return 0; + +err_deassert_ahb: + clk_disable_unprepare(res->core_clk); +err_clk_core: + clk_disable_unprepare(res->phy_clk); +err_clk_phy: + clk_disable_unprepare(res->iface_clk); +err_assert_ahb: + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); + + return ret; +} + +static int qcom_pcie_get_resources_1_0_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_1_0_0 *res = &pcie->res.v1_0_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + + res->vdda = devm_regulator_get(dev, "vdda"); + if (IS_ERR(res->vdda)) + return PTR_ERR(res->vdda); + + res->iface = devm_clk_get(dev, "iface"); + if (IS_ERR(res->iface)) + return PTR_ERR(res->iface); + + res->aux = devm_clk_get(dev, "aux"); + if (IS_ERR(res->aux)) + return PTR_ERR(res->aux); + + res->master_bus = devm_clk_get(dev, "master_bus"); + if (IS_ERR(res->master_bus)) + return PTR_ERR(res->master_bus); + + res->slave_bus = devm_clk_get(dev, "slave_bus"); + if (IS_ERR(res->slave_bus)) + return PTR_ERR(res->slave_bus); + + res->core = devm_reset_control_get_exclusive(dev, "core"); + return PTR_ERR_OR_ZERO(res->core); +} + +static void qcom_pcie_deinit_1_0_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_1_0_0 *res = &pcie->res.v1_0_0; + + reset_control_assert(res->core); + clk_disable_unprepare(res->slave_bus); + clk_disable_unprepare(res->master_bus); + clk_disable_unprepare(res->iface); + clk_disable_unprepare(res->aux); + regulator_disable(res->vdda); +} + +static int qcom_pcie_init_1_0_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_1_0_0 *res = &pcie->res.v1_0_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int ret; + + ret = reset_control_deassert(res->core); + if (ret) { + dev_err(dev, "cannot deassert core reset\n"); + return ret; + } + + ret = clk_prepare_enable(res->aux); + if (ret) { + dev_err(dev, "cannot prepare/enable aux clock\n"); + goto err_res; + } + + ret = clk_prepare_enable(res->iface); + if (ret) { + dev_err(dev, "cannot prepare/enable iface clock\n"); + goto err_aux; + } + + ret = clk_prepare_enable(res->master_bus); + if (ret) { + dev_err(dev, "cannot prepare/enable master_bus clock\n"); + goto err_iface; + } + + ret = clk_prepare_enable(res->slave_bus); + if (ret) { + dev_err(dev, "cannot prepare/enable slave_bus clock\n"); + goto err_master; + } + + ret = regulator_enable(res->vdda); + if (ret) { + dev_err(dev, "cannot enable vdda regulator\n"); + goto err_slave; + } + + /* change DBI base address */ + writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + u32 val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + + val |= BIT(31); + writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); + } + + return 0; +err_slave: + clk_disable_unprepare(res->slave_bus); +err_master: + clk_disable_unprepare(res->master_bus); +err_iface: + clk_disable_unprepare(res->iface); +err_aux: + clk_disable_unprepare(res->aux); +err_res: + reset_control_assert(res->core); + + return ret; +} + +static void qcom_pcie_2_3_2_ltssm_enable(struct qcom_pcie *pcie) +{ + u32 val; + + /* enable link training */ + val = readl(pcie->parf + PCIE20_PARF_LTSSM); + val |= BIT(8); + writel(val, pcie->parf + PCIE20_PARF_LTSSM); +} + +static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int ret; + + res->supplies[0].supply = "vdda"; + res->supplies[1].supply = "vddpe-3v3"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), + res->supplies); + if (ret) + return ret; + + res->aux_clk = devm_clk_get(dev, "aux"); + if (IS_ERR(res->aux_clk)) + return PTR_ERR(res->aux_clk); + + res->cfg_clk = devm_clk_get(dev, "cfg"); + if (IS_ERR(res->cfg_clk)) + return PTR_ERR(res->cfg_clk); + + res->master_clk = devm_clk_get(dev, "bus_master"); + if (IS_ERR(res->master_clk)) + return PTR_ERR(res->master_clk); + + res->slave_clk = devm_clk_get(dev, "bus_slave"); + if (IS_ERR(res->slave_clk)) + return PTR_ERR(res->slave_clk); + + res->pipe_clk = devm_clk_get(dev, "pipe"); + return PTR_ERR_OR_ZERO(res->pipe_clk); +} + +static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; + + clk_disable_unprepare(res->slave_clk); + clk_disable_unprepare(res->master_clk); + clk_disable_unprepare(res->cfg_clk); + clk_disable_unprepare(res->aux_clk); + + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); +} + +static void qcom_pcie_post_deinit_2_3_2(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; + + clk_disable_unprepare(res->pipe_clk); +} + +static int qcom_pcie_init_2_3_2(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + u32 val; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); + if (ret < 0) { + dev_err(dev, "cannot enable regulators\n"); + return ret; + } + + ret = clk_prepare_enable(res->aux_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable aux clock\n"); + goto err_aux_clk; + } + + ret = clk_prepare_enable(res->cfg_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable cfg clock\n"); + goto err_cfg_clk; + } + + ret = clk_prepare_enable(res->master_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable master clock\n"); + goto err_master_clk; + } + + ret = clk_prepare_enable(res->slave_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable slave clock\n"); + goto err_slave_clk; + } + + /* enable PCIe clocks and resets */ + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~BIT(0); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + + /* change DBI base address */ + writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + + /* MAC PHY_POWERDOWN MUX DISABLE */ + val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); + val &= ~BIT(29); + writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); + + val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + val |= BIT(4); + writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + + val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + val |= BIT(31); + writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + + return 0; + +err_slave_clk: + clk_disable_unprepare(res->master_clk); +err_master_clk: + clk_disable_unprepare(res->cfg_clk); +err_cfg_clk: + clk_disable_unprepare(res->aux_clk); + +err_aux_clk: + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); + + return ret; +} + +static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int ret; + + ret = clk_prepare_enable(res->pipe_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable pipe clock\n"); + return ret; + } + + return 0; +} + +static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + + res->aux_clk = devm_clk_get(dev, "aux"); + if (IS_ERR(res->aux_clk)) + return PTR_ERR(res->aux_clk); + + res->master_clk = devm_clk_get(dev, "master_bus"); + if (IS_ERR(res->master_clk)) + return PTR_ERR(res->master_clk); + + res->slave_clk = devm_clk_get(dev, "slave_bus"); + if (IS_ERR(res->slave_clk)) + return PTR_ERR(res->slave_clk); + + res->axi_m_reset = devm_reset_control_get_exclusive(dev, "axi_m"); + if (IS_ERR(res->axi_m_reset)) + return PTR_ERR(res->axi_m_reset); + + res->axi_s_reset = devm_reset_control_get_exclusive(dev, "axi_s"); + if (IS_ERR(res->axi_s_reset)) + return PTR_ERR(res->axi_s_reset); + + res->pipe_reset = devm_reset_control_get_exclusive(dev, "pipe"); + if (IS_ERR(res->pipe_reset)) + return PTR_ERR(res->pipe_reset); + + res->axi_m_vmid_reset = devm_reset_control_get_exclusive(dev, + "axi_m_vmid"); + if (IS_ERR(res->axi_m_vmid_reset)) + return PTR_ERR(res->axi_m_vmid_reset); + + res->axi_s_xpu_reset = devm_reset_control_get_exclusive(dev, + "axi_s_xpu"); + if (IS_ERR(res->axi_s_xpu_reset)) + return PTR_ERR(res->axi_s_xpu_reset); + + res->parf_reset = devm_reset_control_get_exclusive(dev, "parf"); + if (IS_ERR(res->parf_reset)) + return PTR_ERR(res->parf_reset); + + res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); + if (IS_ERR(res->phy_reset)) + return PTR_ERR(res->phy_reset); + + res->axi_m_sticky_reset = devm_reset_control_get_exclusive(dev, + "axi_m_sticky"); + if (IS_ERR(res->axi_m_sticky_reset)) + return PTR_ERR(res->axi_m_sticky_reset); + + res->pipe_sticky_reset = devm_reset_control_get_exclusive(dev, + "pipe_sticky"); + if (IS_ERR(res->pipe_sticky_reset)) + return PTR_ERR(res->pipe_sticky_reset); + + res->pwr_reset = devm_reset_control_get_exclusive(dev, "pwr"); + if (IS_ERR(res->pwr_reset)) + return PTR_ERR(res->pwr_reset); + + res->ahb_reset = devm_reset_control_get_exclusive(dev, "ahb"); + if (IS_ERR(res->ahb_reset)) + return PTR_ERR(res->ahb_reset); + + res->phy_ahb_reset = devm_reset_control_get_exclusive(dev, "phy_ahb"); + if (IS_ERR(res->phy_ahb_reset)) + return PTR_ERR(res->phy_ahb_reset); + + return 0; +} + +static void qcom_pcie_deinit_2_4_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; + + reset_control_assert(res->axi_m_reset); + reset_control_assert(res->axi_s_reset); + reset_control_assert(res->pipe_reset); + reset_control_assert(res->pipe_sticky_reset); + reset_control_assert(res->phy_reset); + reset_control_assert(res->phy_ahb_reset); + reset_control_assert(res->axi_m_sticky_reset); + reset_control_assert(res->pwr_reset); + reset_control_assert(res->ahb_reset); + clk_disable_unprepare(res->aux_clk); + clk_disable_unprepare(res->master_clk); + clk_disable_unprepare(res->slave_clk); +} + +static int qcom_pcie_init_2_4_0(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + u32 val; + int ret; + + ret = reset_control_assert(res->axi_m_reset); + if (ret) { + dev_err(dev, "cannot assert axi master reset\n"); + return ret; + } + + ret = reset_control_assert(res->axi_s_reset); + if (ret) { + dev_err(dev, "cannot assert axi slave reset\n"); + return ret; + } + + usleep_range(10000, 12000); + + ret = reset_control_assert(res->pipe_reset); + if (ret) { + dev_err(dev, "cannot assert pipe reset\n"); + return ret; + } + + ret = reset_control_assert(res->pipe_sticky_reset); + if (ret) { + dev_err(dev, "cannot assert pipe sticky reset\n"); + return ret; + } + + ret = reset_control_assert(res->phy_reset); + if (ret) { + dev_err(dev, "cannot assert phy reset\n"); + return ret; + } + + ret = reset_control_assert(res->phy_ahb_reset); + if (ret) { + dev_err(dev, "cannot assert phy ahb reset\n"); + return ret; + } + + usleep_range(10000, 12000); + + ret = reset_control_assert(res->axi_m_sticky_reset); + if (ret) { + dev_err(dev, "cannot assert axi master sticky reset\n"); + return ret; + } + + ret = reset_control_assert(res->pwr_reset); + if (ret) { + dev_err(dev, "cannot assert power reset\n"); + return ret; + } + + ret = reset_control_assert(res->ahb_reset); + if (ret) { + dev_err(dev, "cannot assert ahb reset\n"); + return ret; + } + + usleep_range(10000, 12000); + + ret = reset_control_deassert(res->phy_ahb_reset); + if (ret) { + dev_err(dev, "cannot deassert phy ahb reset\n"); + return ret; + } + + ret = reset_control_deassert(res->phy_reset); + if (ret) { + dev_err(dev, "cannot deassert phy reset\n"); + goto err_rst_phy; + } + + ret = reset_control_deassert(res->pipe_reset); + if (ret) { + dev_err(dev, "cannot deassert pipe reset\n"); + goto err_rst_pipe; + } + + ret = reset_control_deassert(res->pipe_sticky_reset); + if (ret) { + dev_err(dev, "cannot deassert pipe sticky reset\n"); + goto err_rst_pipe_sticky; + } + + usleep_range(10000, 12000); + + ret = reset_control_deassert(res->axi_m_reset); + if (ret) { + dev_err(dev, "cannot deassert axi master reset\n"); + goto err_rst_axi_m; + } + + ret = reset_control_deassert(res->axi_m_sticky_reset); + if (ret) { + dev_err(dev, "cannot deassert axi master sticky reset\n"); + goto err_rst_axi_m_sticky; + } + + ret = reset_control_deassert(res->axi_s_reset); + if (ret) { + dev_err(dev, "cannot deassert axi slave reset\n"); + goto err_rst_axi_s; + } + + ret = reset_control_deassert(res->pwr_reset); + if (ret) { + dev_err(dev, "cannot deassert power reset\n"); + goto err_rst_pwr; + } + + ret = reset_control_deassert(res->ahb_reset); + if (ret) { + dev_err(dev, "cannot deassert ahb reset\n"); + goto err_rst_ahb; + } + + usleep_range(10000, 12000); + + ret = clk_prepare_enable(res->aux_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable iface clock\n"); + goto err_clk_aux; + } + + ret = clk_prepare_enable(res->master_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable core clock\n"); + goto err_clk_axi_m; + } + + ret = clk_prepare_enable(res->slave_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable phy clock\n"); + goto err_clk_axi_s; + } + + /* enable PCIe clocks and resets */ + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~BIT(0); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + + /* change DBI base address */ + writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + + /* MAC PHY_POWERDOWN MUX DISABLE */ + val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); + val &= ~BIT(29); + writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); + + val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + val |= BIT(4); + writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); + + val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + val |= BIT(31); + writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); + + return 0; + +err_clk_axi_s: + clk_disable_unprepare(res->master_clk); +err_clk_axi_m: + clk_disable_unprepare(res->aux_clk); +err_clk_aux: + reset_control_assert(res->ahb_reset); +err_rst_ahb: + reset_control_assert(res->pwr_reset); +err_rst_pwr: + reset_control_assert(res->axi_s_reset); +err_rst_axi_s: + reset_control_assert(res->axi_m_sticky_reset); +err_rst_axi_m_sticky: + reset_control_assert(res->axi_m_reset); +err_rst_axi_m: + reset_control_assert(res->pipe_sticky_reset); +err_rst_pipe_sticky: + reset_control_assert(res->pipe_reset); +err_rst_pipe: + reset_control_assert(res->phy_reset); +err_rst_phy: + reset_control_assert(res->phy_ahb_reset); + return ret; +} + +static int qcom_pcie_get_resources_2_3_3(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int i; + const char *rst_names[] = { "axi_m", "axi_s", "pipe", + "axi_m_sticky", "sticky", + "ahb", "sleep", }; + + res->iface = devm_clk_get(dev, "iface"); + if (IS_ERR(res->iface)) + return PTR_ERR(res->iface); + + res->axi_m_clk = devm_clk_get(dev, "axi_m"); + if (IS_ERR(res->axi_m_clk)) + return PTR_ERR(res->axi_m_clk); + + res->axi_s_clk = devm_clk_get(dev, "axi_s"); + if (IS_ERR(res->axi_s_clk)) + return PTR_ERR(res->axi_s_clk); + + res->ahb_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(res->ahb_clk)) + return PTR_ERR(res->ahb_clk); + + res->aux_clk = devm_clk_get(dev, "aux"); + if (IS_ERR(res->aux_clk)) + return PTR_ERR(res->aux_clk); + + for (i = 0; i < ARRAY_SIZE(rst_names); i++) { + res->rst[i] = devm_reset_control_get(dev, rst_names[i]); + if (IS_ERR(res->rst[i])) + return PTR_ERR(res->rst[i]); + } + + return 0; +} + +static void qcom_pcie_deinit_2_3_3(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; + + clk_disable_unprepare(res->iface); + clk_disable_unprepare(res->axi_m_clk); + clk_disable_unprepare(res->axi_s_clk); + clk_disable_unprepare(res->ahb_clk); + clk_disable_unprepare(res->aux_clk); +} + +static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + int i, ret; + u32 val; + + for (i = 0; i < ARRAY_SIZE(res->rst); i++) { + ret = reset_control_assert(res->rst[i]); + if (ret) { + dev_err(dev, "reset #%d assert failed (%d)\n", i, ret); + return ret; + } + } + + usleep_range(2000, 2500); + + for (i = 0; i < ARRAY_SIZE(res->rst); i++) { + ret = reset_control_deassert(res->rst[i]); + if (ret) { + dev_err(dev, "reset #%d deassert failed (%d)\n", i, + ret); + return ret; + } + } + + /* + * Don't have a way to see if the reset has completed. + * Wait for some time. + */ + usleep_range(2000, 2500); + + ret = clk_prepare_enable(res->iface); + if (ret) { + dev_err(dev, "cannot prepare/enable core clock\n"); + goto err_clk_iface; + } + + ret = clk_prepare_enable(res->axi_m_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable core clock\n"); + goto err_clk_axi_m; + } + + ret = clk_prepare_enable(res->axi_s_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable axi slave clock\n"); + goto err_clk_axi_s; + } + + ret = clk_prepare_enable(res->ahb_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable ahb clock\n"); + goto err_clk_ahb; + } + + ret = clk_prepare_enable(res->aux_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable aux clock\n"); + goto err_clk_aux; + } + + writel(SLV_ADDR_SPACE_SZ, + pcie->parf + PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE); + + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~BIT(0); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + + writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); + + writel(MST_WAKEUP_EN | SLV_WAKEUP_EN | MSTR_ACLK_CGC_DIS + | SLV_ACLK_CGC_DIS | CORE_CLK_CGC_DIS | + AUX_PWR_DET | L23_CLK_RMV_DIS | L1_CLK_RMV_DIS, + pcie->parf + PCIE20_PARF_SYS_CTRL); + writel(0, pcie->parf + PCIE20_PARF_Q2A_FLUSH); + + writel(CMD_BME_VAL, pci->dbi_base + PCIE20_COMMAND_STATUS); + writel(DBI_RO_WR_EN, pci->dbi_base + PCIE20_MISC_CONTROL_1_REG); + writel(PCIE_CAP_LINK1_VAL, pci->dbi_base + PCIE20_CAP_LINK_1); + + val = readl(pci->dbi_base + PCIE20_CAP_LINK_CAPABILITIES); + val &= ~PCIE20_CAP_ACTIVE_STATE_LINK_PM_SUPPORT; + writel(val, pci->dbi_base + PCIE20_CAP_LINK_CAPABILITIES); + + writel(PCIE_CAP_CPL_TIMEOUT_DISABLE, pci->dbi_base + + PCIE20_DEVICE_CONTROL2_STATUS2); + + return 0; + +err_clk_aux: + clk_disable_unprepare(res->ahb_clk); +err_clk_ahb: + clk_disable_unprepare(res->axi_s_clk); +err_clk_axi_s: + clk_disable_unprepare(res->axi_m_clk); +err_clk_axi_m: + clk_disable_unprepare(res->iface); +err_clk_iface: + /* + * Not checking for failure, will anyway return + * the original failure in 'ret'. + */ + for (i = 0; i < ARRAY_SIZE(res->rst); i++) + reset_control_assert(res->rst[i]); + + return ret; +} + +static int qcom_pcie_link_up(struct dw_pcie *pci) +{ + u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA); + + return !!(val & PCI_EXP_LNKSTA_DLLLA); +} + +static int qcom_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct qcom_pcie *pcie = to_qcom_pcie(pci); + int ret; + + pm_runtime_get_sync(pci->dev); + qcom_ep_reset_assert(pcie); + + ret = pcie->ops->init(pcie); + if (ret) + return ret; + + ret = phy_power_on(pcie->phy); + if (ret) + goto err_deinit; + + if (pcie->ops->post_init) { + ret = pcie->ops->post_init(pcie); + if (ret) + goto err_disable_phy; + } + + dw_pcie_setup_rc(pp); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + + qcom_ep_reset_deassert(pcie); + + ret = qcom_pcie_establish_link(pcie); + if (ret) + goto err; + + return 0; +err: + qcom_ep_reset_assert(pcie); + if (pcie->ops->post_deinit) + pcie->ops->post_deinit(pcie); +err_disable_phy: + phy_power_off(pcie->phy); +err_deinit: + pcie->ops->deinit(pcie); + pm_runtime_put(pci->dev); + + return ret; +} + +static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + /* the device class is not reported correctly from the register */ + if (where == PCI_CLASS_REVISION && size == 4) { + *val = readl(pci->dbi_base + PCI_CLASS_REVISION); + *val &= 0xff; /* keep revision id */ + *val |= PCI_CLASS_BRIDGE_PCI << 16; + return PCIBIOS_SUCCESSFUL; + } + + return dw_pcie_read(pci->dbi_base + where, size, val); +} + +static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { + .host_init = qcom_pcie_host_init, + .rd_own_conf = qcom_pcie_rd_own_conf, +}; + +/* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ +static const struct qcom_pcie_ops ops_2_1_0 = { + .get_resources = qcom_pcie_get_resources_2_1_0, + .init = qcom_pcie_init_2_1_0, + .deinit = qcom_pcie_deinit_2_1_0, + .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, +}; + +/* Qcom IP rev.: 1.0.0 Synopsys IP rev.: 4.11a */ +static const struct qcom_pcie_ops ops_1_0_0 = { + .get_resources = qcom_pcie_get_resources_1_0_0, + .init = qcom_pcie_init_1_0_0, + .deinit = qcom_pcie_deinit_1_0_0, + .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, +}; + +/* Qcom IP rev.: 2.3.2 Synopsys IP rev.: 4.21a */ +static const struct qcom_pcie_ops ops_2_3_2 = { + .get_resources = qcom_pcie_get_resources_2_3_2, + .init = qcom_pcie_init_2_3_2, + .post_init = qcom_pcie_post_init_2_3_2, + .deinit = qcom_pcie_deinit_2_3_2, + .post_deinit = qcom_pcie_post_deinit_2_3_2, + .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, +}; + +/* Qcom IP rev.: 2.4.0 Synopsys IP rev.: 4.20a */ +static const struct qcom_pcie_ops ops_2_4_0 = { + .get_resources = qcom_pcie_get_resources_2_4_0, + .init = qcom_pcie_init_2_4_0, + .deinit = qcom_pcie_deinit_2_4_0, + .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, +}; + +/* Qcom IP rev.: 2.3.3 Synopsys IP rev.: 4.30a */ +static const struct qcom_pcie_ops ops_2_3_3 = { + .get_resources = qcom_pcie_get_resources_2_3_3, + .init = qcom_pcie_init_2_3_3, + .deinit = qcom_pcie_deinit_2_3_3, + .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, +}; + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = qcom_pcie_link_up, +}; + +static int qcom_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct pcie_port *pp; + struct dw_pcie *pci; + struct qcom_pcie *pcie; + int ret; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pm_runtime_enable(dev); + pci->dev = dev; + pci->ops = &dw_pcie_ops; + pp = &pci->pp; + + pcie->pci = pci; + + pcie->ops = of_device_get_match_data(dev); + + pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW); + if (IS_ERR(pcie->reset)) + return PTR_ERR(pcie->reset); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); + pcie->parf = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->parf)) + return PTR_ERR(pcie->parf); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); + pcie->elbi = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->elbi)) + return PTR_ERR(pcie->elbi); + + pcie->phy = devm_phy_optional_get(dev, "pciephy"); + if (IS_ERR(pcie->phy)) + return PTR_ERR(pcie->phy); + + ret = pcie->ops->get_resources(pcie); + if (ret) + return ret; + + pp->root_bus_nr = -1; + pp->ops = &qcom_pcie_dw_ops; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pp->msi_irq < 0) + return pp->msi_irq; + } + + ret = phy_init(pcie->phy); + if (ret) { + pm_runtime_disable(&pdev->dev); + return ret; + } + + platform_set_drvdata(pdev, pcie); + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "cannot initialize host\n"); + pm_runtime_disable(&pdev->dev); + return ret; + } + + return 0; +} + +static const struct of_device_id qcom_pcie_match[] = { + { .compatible = "qcom,pcie-apq8084", .data = &ops_1_0_0 }, + { .compatible = "qcom,pcie-ipq8064", .data = &ops_2_1_0 }, + { .compatible = "qcom,pcie-apq8064", .data = &ops_2_1_0 }, + { .compatible = "qcom,pcie-msm8996", .data = &ops_2_3_2 }, + { .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 }, + { .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 }, + { } +}; + +static struct platform_driver qcom_pcie_driver = { + .probe = qcom_pcie_probe, + .driver = { + .name = "qcom-pcie", + .suppress_bind_attrs = true, + .of_match_table = qcom_pcie_match, + }, +}; +builtin_platform_driver(qcom_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c new file mode 100644 index 000000000000..ecb58f7b7566 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-spear13xx.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for ST Microelectronics SPEAr13xx SoCs + * + * SPEAr13xx PCIe Glue Layer Source Code + * + * Copyright (C) 2010-2014 ST Microelectronics + * Pratyush Anand + * Mohit Kumar + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +struct spear13xx_pcie { + struct dw_pcie *pci; + void __iomem *app_base; + struct phy *phy; + struct clk *clk; + bool is_gen1; +}; + +struct pcie_app_reg { + u32 app_ctrl_0; /* cr0 */ + u32 app_ctrl_1; /* cr1 */ + u32 app_status_0; /* cr2 */ + u32 app_status_1; /* cr3 */ + u32 msg_status; /* cr4 */ + u32 msg_payload; /* cr5 */ + u32 int_sts; /* cr6 */ + u32 int_clr; /* cr7 */ + u32 int_mask; /* cr8 */ + u32 mst_bmisc; /* cr9 */ + u32 phy_ctrl; /* cr10 */ + u32 phy_status; /* cr11 */ + u32 cxpl_debug_info_0; /* cr12 */ + u32 cxpl_debug_info_1; /* cr13 */ + u32 ven_msg_ctrl_0; /* cr14 */ + u32 ven_msg_ctrl_1; /* cr15 */ + u32 ven_msg_data_0; /* cr16 */ + u32 ven_msg_data_1; /* cr17 */ + u32 ven_msi_0; /* cr18 */ + u32 ven_msi_1; /* cr19 */ + u32 mst_rmisc; /* cr20 */ +}; + +/* CR0 ID */ +#define APP_LTSSM_ENABLE_ID 3 +#define DEVICE_TYPE_RC (4 << 25) +#define MISCTRL_EN_ID 30 +#define REG_TRANSLATION_ENABLE 31 + +/* CR3 ID */ +#define XMLH_LINK_UP (1 << 6) + +/* CR6 */ +#define MSI_CTRL_INT (1 << 26) + +#define EXP_CAP_ID_OFFSET 0x70 + +#define to_spear13xx_pcie(x) dev_get_drvdata((x)->dev) + +static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie) +{ + struct dw_pcie *pci = spear13xx_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; + u32 val; + u32 exp_cap_off = EXP_CAP_ID_OFFSET; + + if (dw_pcie_link_up(pci)) { + dev_err(pci->dev, "link already up\n"); + return 0; + } + + dw_pcie_setup_rc(pp); + + /* + * this controller support only 128 bytes read size, however its + * default value in capability register is 512 bytes. So force + * it to 128 here. + */ + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val); + val &= ~PCI_EXP_DEVCTL_READRQ; + dw_pcie_write(pci->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val); + + dw_pcie_write(pci->dbi_base + PCI_VENDOR_ID, 2, 0x104A); + dw_pcie_write(pci->dbi_base + PCI_DEVICE_ID, 2, 0xCD80); + + /* + * if is_gen1 is set then handle it, so that some buggy card + * also works + */ + if (spear13xx_pcie->is_gen1) { + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCAP, + 4, &val); + if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { + val &= ~((u32)PCI_EXP_LNKCAP_SLS); + val |= PCI_EXP_LNKCAP_SLS_2_5GB; + dw_pcie_write(pci->dbi_base + exp_cap_off + + PCI_EXP_LNKCAP, 4, val); + } + + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2, + 2, &val); + if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { + val &= ~((u32)PCI_EXP_LNKCAP_SLS); + val |= PCI_EXP_LNKCAP_SLS_2_5GB; + dw_pcie_write(pci->dbi_base + exp_cap_off + + PCI_EXP_LNKCTL2, 2, val); + } + } + + /* enable ltssm */ + writel(DEVICE_TYPE_RC | (1 << MISCTRL_EN_ID) + | (1 << APP_LTSSM_ENABLE_ID) + | ((u32)1 << REG_TRANSLATION_ENABLE), + &app_reg->app_ctrl_0); + + return dw_pcie_wait_for_link(pci); +} + +static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg) +{ + struct spear13xx_pcie *spear13xx_pcie = arg; + struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; + struct dw_pcie *pci = spear13xx_pcie->pci; + struct pcie_port *pp = &pci->pp; + unsigned int status; + + status = readl(&app_reg->int_sts); + + if (status & MSI_CTRL_INT) { + BUG_ON(!IS_ENABLED(CONFIG_PCI_MSI)); + dw_handle_msi_irq(pp); + } + + writel(status, &app_reg->int_clr); + + return IRQ_HANDLED; +} + +static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pcie) +{ + struct dw_pcie *pci = spear13xx_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; + + /* Enable MSI interrupt */ + if (IS_ENABLED(CONFIG_PCI_MSI)) { + dw_pcie_msi_init(pp); + writel(readl(&app_reg->int_mask) | + MSI_CTRL_INT, &app_reg->int_mask); + } +} + +static int spear13xx_pcie_link_up(struct dw_pcie *pci) +{ + struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci); + struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; + + if (readl(&app_reg->app_status_1) & XMLH_LINK_UP) + return 1; + + return 0; +} + +static int spear13xx_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci); + + spear13xx_pcie_establish_link(spear13xx_pcie); + spear13xx_pcie_enable_interrupts(spear13xx_pcie); + + return 0; +} + +static const struct dw_pcie_host_ops spear13xx_pcie_host_ops = { + .host_init = spear13xx_pcie_host_init, +}; + +static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, + struct platform_device *pdev) +{ + struct dw_pcie *pci = spear13xx_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + pp->irq = platform_get_irq(pdev, 0); + if (pp->irq < 0) { + dev_err(dev, "failed to get irq\n"); + return pp->irq; + } + ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, + "spear1340-pcie", spear13xx_pcie); + if (ret) { + dev_err(dev, "failed to request irq %d\n", pp->irq); + return ret; + } + + pp->root_bus_nr = -1; + pp->ops = &spear13xx_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = spear13xx_pcie_link_up, +}; + +static int spear13xx_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct spear13xx_pcie *spear13xx_pcie; + struct device_node *np = dev->of_node; + struct resource *dbi_base; + int ret; + + spear13xx_pcie = devm_kzalloc(dev, sizeof(*spear13xx_pcie), GFP_KERNEL); + if (!spear13xx_pcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + spear13xx_pcie->pci = pci; + + spear13xx_pcie->phy = devm_phy_get(dev, "pcie-phy"); + if (IS_ERR(spear13xx_pcie->phy)) { + ret = PTR_ERR(spear13xx_pcie->phy); + if (ret == -EPROBE_DEFER) + dev_info(dev, "probe deferred\n"); + else + dev_err(dev, "couldn't get pcie-phy\n"); + return ret; + } + + phy_init(spear13xx_pcie->phy); + + spear13xx_pcie->clk = devm_clk_get(dev, NULL); + if (IS_ERR(spear13xx_pcie->clk)) { + dev_err(dev, "couldn't get clk for pcie\n"); + return PTR_ERR(spear13xx_pcie->clk); + } + ret = clk_prepare_enable(spear13xx_pcie->clk); + if (ret) { + dev_err(dev, "couldn't enable clk for pcie\n"); + return ret; + } + + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) { + dev_err(dev, "couldn't remap dbi base %p\n", dbi_base); + ret = PTR_ERR(pci->dbi_base); + goto fail_clk; + } + spear13xx_pcie->app_base = pci->dbi_base + 0x2000; + + if (of_property_read_bool(np, "st,pcie-is-gen1")) + spear13xx_pcie->is_gen1 = true; + + platform_set_drvdata(pdev, spear13xx_pcie); + + ret = spear13xx_add_pcie_port(spear13xx_pcie, pdev); + if (ret < 0) + goto fail_clk; + + return 0; + +fail_clk: + clk_disable_unprepare(spear13xx_pcie->clk); + + return ret; +} + +static const struct of_device_id spear13xx_pcie_of_match[] = { + { .compatible = "st,spear1340-pcie", }, + {}, +}; + +static struct platform_driver spear13xx_pcie_driver = { + .probe = spear13xx_pcie_probe, + .driver = { + .name = "spear-pcie", + .of_match_table = of_match_ptr(spear13xx_pcie_of_match), + .suppress_bind_attrs = true, + }, +}; + +builtin_platform_driver(spear13xx_pcie_driver); diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c new file mode 100644 index 000000000000..d3172d5d3d35 --- /dev/null +++ b/drivers/pci/controller/pci-aardvark.c @@ -0,0 +1,978 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Aardvark PCIe controller, used on Marvell Armada + * 3700. + * + * Copyright (C) 2016 Marvell + * + * Author: Hezi Shahmoon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* PCIe core registers */ +#define PCIE_CORE_CMD_STATUS_REG 0x4 +#define PCIE_CORE_CMD_IO_ACCESS_EN BIT(0) +#define PCIE_CORE_CMD_MEM_ACCESS_EN BIT(1) +#define PCIE_CORE_CMD_MEM_IO_REQ_EN BIT(2) +#define PCIE_CORE_DEV_CTRL_STATS_REG 0xc8 +#define PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE (0 << 4) +#define PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT 5 +#define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11) +#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT 12 +#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ 0x2 +#define PCIE_CORE_LINK_CTRL_STAT_REG 0xd0 +#define PCIE_CORE_LINK_L0S_ENTRY BIT(0) +#define PCIE_CORE_LINK_TRAINING BIT(5) +#define PCIE_CORE_LINK_WIDTH_SHIFT 20 +#define PCIE_CORE_ERR_CAPCTL_REG 0x118 +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8) + +/* PIO registers base address and register offsets */ +#define PIO_BASE_ADDR 0x4000 +#define PIO_CTRL (PIO_BASE_ADDR + 0x0) +#define PIO_CTRL_TYPE_MASK GENMASK(3, 0) +#define PIO_CTRL_ADDR_WIN_DISABLE BIT(24) +#define PIO_STAT (PIO_BASE_ADDR + 0x4) +#define PIO_COMPLETION_STATUS_SHIFT 7 +#define PIO_COMPLETION_STATUS_MASK GENMASK(9, 7) +#define PIO_COMPLETION_STATUS_OK 0 +#define PIO_COMPLETION_STATUS_UR 1 +#define PIO_COMPLETION_STATUS_CRS 2 +#define PIO_COMPLETION_STATUS_CA 4 +#define PIO_NON_POSTED_REQ BIT(0) +#define PIO_ADDR_LS (PIO_BASE_ADDR + 0x8) +#define PIO_ADDR_MS (PIO_BASE_ADDR + 0xc) +#define PIO_WR_DATA (PIO_BASE_ADDR + 0x10) +#define PIO_WR_DATA_STRB (PIO_BASE_ADDR + 0x14) +#define PIO_RD_DATA (PIO_BASE_ADDR + 0x18) +#define PIO_START (PIO_BASE_ADDR + 0x1c) +#define PIO_ISR (PIO_BASE_ADDR + 0x20) +#define PIO_ISRM (PIO_BASE_ADDR + 0x24) + +/* Aardvark Control registers */ +#define CONTROL_BASE_ADDR 0x4800 +#define PCIE_CORE_CTRL0_REG (CONTROL_BASE_ADDR + 0x0) +#define PCIE_GEN_SEL_MSK 0x3 +#define PCIE_GEN_SEL_SHIFT 0x0 +#define SPEED_GEN_1 0 +#define SPEED_GEN_2 1 +#define SPEED_GEN_3 2 +#define IS_RC_MSK 1 +#define IS_RC_SHIFT 2 +#define LANE_CNT_MSK 0x18 +#define LANE_CNT_SHIFT 0x3 +#define LANE_COUNT_1 (0 << LANE_CNT_SHIFT) +#define LANE_COUNT_2 (1 << LANE_CNT_SHIFT) +#define LANE_COUNT_4 (2 << LANE_CNT_SHIFT) +#define LANE_COUNT_8 (3 << LANE_CNT_SHIFT) +#define LINK_TRAINING_EN BIT(6) +#define LEGACY_INTA BIT(28) +#define LEGACY_INTB BIT(29) +#define LEGACY_INTC BIT(30) +#define LEGACY_INTD BIT(31) +#define PCIE_CORE_CTRL1_REG (CONTROL_BASE_ADDR + 0x4) +#define HOT_RESET_GEN BIT(0) +#define PCIE_CORE_CTRL2_REG (CONTROL_BASE_ADDR + 0x8) +#define PCIE_CORE_CTRL2_RESERVED 0x7 +#define PCIE_CORE_CTRL2_TD_ENABLE BIT(4) +#define PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE BIT(5) +#define PCIE_CORE_CTRL2_OB_WIN_ENABLE BIT(6) +#define PCIE_CORE_CTRL2_MSI_ENABLE BIT(10) +#define PCIE_ISR0_REG (CONTROL_BASE_ADDR + 0x40) +#define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44) +#define PCIE_ISR0_MSI_INT_PENDING BIT(24) +#define PCIE_ISR0_INTX_ASSERT(val) BIT(16 + (val)) +#define PCIE_ISR0_INTX_DEASSERT(val) BIT(20 + (val)) +#define PCIE_ISR0_ALL_MASK GENMASK(26, 0) +#define PCIE_ISR1_REG (CONTROL_BASE_ADDR + 0x48) +#define PCIE_ISR1_MASK_REG (CONTROL_BASE_ADDR + 0x4C) +#define PCIE_ISR1_POWER_STATE_CHANGE BIT(4) +#define PCIE_ISR1_FLUSH BIT(5) +#define PCIE_ISR1_INTX_ASSERT(val) BIT(8 + (val)) +#define PCIE_ISR1_ALL_MASK GENMASK(11, 4) +#define PCIE_MSI_ADDR_LOW_REG (CONTROL_BASE_ADDR + 0x50) +#define PCIE_MSI_ADDR_HIGH_REG (CONTROL_BASE_ADDR + 0x54) +#define PCIE_MSI_STATUS_REG (CONTROL_BASE_ADDR + 0x58) +#define PCIE_MSI_MASK_REG (CONTROL_BASE_ADDR + 0x5C) +#define PCIE_MSI_PAYLOAD_REG (CONTROL_BASE_ADDR + 0x9C) + +/* PCIe window configuration */ +#define OB_WIN_BASE_ADDR 0x4c00 +#define OB_WIN_BLOCK_SIZE 0x20 +#define OB_WIN_REG_ADDR(win, offset) (OB_WIN_BASE_ADDR + \ + OB_WIN_BLOCK_SIZE * (win) + \ + (offset)) +#define OB_WIN_MATCH_LS(win) OB_WIN_REG_ADDR(win, 0x00) +#define OB_WIN_MATCH_MS(win) OB_WIN_REG_ADDR(win, 0x04) +#define OB_WIN_REMAP_LS(win) OB_WIN_REG_ADDR(win, 0x08) +#define OB_WIN_REMAP_MS(win) OB_WIN_REG_ADDR(win, 0x0c) +#define OB_WIN_MASK_LS(win) OB_WIN_REG_ADDR(win, 0x10) +#define OB_WIN_MASK_MS(win) OB_WIN_REG_ADDR(win, 0x14) +#define OB_WIN_ACTIONS(win) OB_WIN_REG_ADDR(win, 0x18) + +/* PCIe window types */ +#define OB_PCIE_MEM 0x0 +#define OB_PCIE_IO 0x4 + +/* LMI registers base address and register offsets */ +#define LMI_BASE_ADDR 0x6000 +#define CFG_REG (LMI_BASE_ADDR + 0x0) +#define LTSSM_SHIFT 24 +#define LTSSM_MASK 0x3f +#define LTSSM_L0 0x10 +#define RC_BAR_CONFIG 0x300 + +/* PCIe core controller registers */ +#define CTRL_CORE_BASE_ADDR 0x18000 +#define CTRL_CONFIG_REG (CTRL_CORE_BASE_ADDR + 0x0) +#define CTRL_MODE_SHIFT 0x0 +#define CTRL_MODE_MASK 0x1 +#define PCIE_CORE_MODE_DIRECT 0x0 +#define PCIE_CORE_MODE_COMMAND 0x1 + +/* PCIe Central Interrupts Registers */ +#define CENTRAL_INT_BASE_ADDR 0x1b000 +#define HOST_CTRL_INT_STATUS_REG (CENTRAL_INT_BASE_ADDR + 0x0) +#define HOST_CTRL_INT_MASK_REG (CENTRAL_INT_BASE_ADDR + 0x4) +#define PCIE_IRQ_CMDQ_INT BIT(0) +#define PCIE_IRQ_MSI_STATUS_INT BIT(1) +#define PCIE_IRQ_CMD_SENT_DONE BIT(3) +#define PCIE_IRQ_DMA_INT BIT(4) +#define PCIE_IRQ_IB_DXFERDONE BIT(5) +#define PCIE_IRQ_OB_DXFERDONE BIT(6) +#define PCIE_IRQ_OB_RXFERDONE BIT(7) +#define PCIE_IRQ_COMPQ_INT BIT(12) +#define PCIE_IRQ_DIR_RD_DDR_DET BIT(13) +#define PCIE_IRQ_DIR_WR_DDR_DET BIT(14) +#define PCIE_IRQ_CORE_INT BIT(16) +#define PCIE_IRQ_CORE_INT_PIO BIT(17) +#define PCIE_IRQ_DPMU_INT BIT(18) +#define PCIE_IRQ_PCIE_MIS_INT BIT(19) +#define PCIE_IRQ_MSI_INT1_DET BIT(20) +#define PCIE_IRQ_MSI_INT2_DET BIT(21) +#define PCIE_IRQ_RC_DBELL_DET BIT(22) +#define PCIE_IRQ_EP_STATUS BIT(23) +#define PCIE_IRQ_ALL_MASK 0xfff0fb +#define PCIE_IRQ_ENABLE_INTS_MASK PCIE_IRQ_CORE_INT + +/* Transaction types */ +#define PCIE_CONFIG_RD_TYPE0 0x8 +#define PCIE_CONFIG_RD_TYPE1 0x9 +#define PCIE_CONFIG_WR_TYPE0 0xa +#define PCIE_CONFIG_WR_TYPE1 0xb + +#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20) +#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15) +#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12) +#define PCIE_CONF_REG(reg) ((reg) & 0xffc) +#define PCIE_CONF_ADDR(bus, devfn, where) \ + (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ + PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where)) + +#define PIO_TIMEOUT_MS 1 + +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP_MIN 90000 +#define LINK_WAIT_USLEEP_MAX 100000 + +#define MSI_IRQ_NUM 32 + +struct advk_pcie { + struct platform_device *pdev; + void __iomem *base; + struct list_head resources; + struct irq_domain *irq_domain; + struct irq_chip irq_chip; + struct irq_domain *msi_domain; + struct irq_domain *msi_inner_domain; + struct irq_chip msi_bottom_irq_chip; + struct irq_chip msi_irq_chip; + struct msi_domain_info msi_domain_info; + DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); + struct mutex msi_used_lock; + u16 msi_msg; + int root_bus_nr; +}; + +static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg) +{ + writel(val, pcie->base + reg); +} + +static inline u32 advk_readl(struct advk_pcie *pcie, u64 reg) +{ + return readl(pcie->base + reg); +} + +static int advk_pcie_link_up(struct advk_pcie *pcie) +{ + u32 val, ltssm_state; + + val = advk_readl(pcie, CFG_REG); + ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; + return ltssm_state >= LTSSM_L0; +} + +static int advk_pcie_wait_for_link(struct advk_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (advk_pcie_link_up(pcie)) { + dev_info(dev, "link up\n"); + return 0; + } + + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + dev_err(dev, "link never came up\n"); + return -ETIMEDOUT; +} + +/* + * Set PCIe address window register which could be used for memory + * mapping. + */ +static void advk_pcie_set_ob_win(struct advk_pcie *pcie, + u32 win_num, u32 match_ms, + u32 match_ls, u32 mask_ms, + u32 mask_ls, u32 remap_ms, + u32 remap_ls, u32 action) +{ + advk_writel(pcie, match_ls, OB_WIN_MATCH_LS(win_num)); + advk_writel(pcie, match_ms, OB_WIN_MATCH_MS(win_num)); + advk_writel(pcie, mask_ms, OB_WIN_MASK_MS(win_num)); + advk_writel(pcie, mask_ls, OB_WIN_MASK_LS(win_num)); + advk_writel(pcie, remap_ms, OB_WIN_REMAP_MS(win_num)); + advk_writel(pcie, remap_ls, OB_WIN_REMAP_LS(win_num)); + advk_writel(pcie, action, OB_WIN_ACTIONS(win_num)); + advk_writel(pcie, match_ls | BIT(0), OB_WIN_MATCH_LS(win_num)); +} + +static void advk_pcie_setup_hw(struct advk_pcie *pcie) +{ + u32 reg; + int i; + + /* Point PCIe unit MBUS decode windows to DRAM space */ + for (i = 0; i < 8; i++) + advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0); + + /* Set to Direct mode */ + reg = advk_readl(pcie, CTRL_CONFIG_REG); + reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT); + reg |= ((PCIE_CORE_MODE_DIRECT & CTRL_MODE_MASK) << CTRL_MODE_SHIFT); + advk_writel(pcie, reg, CTRL_CONFIG_REG); + + /* Set PCI global control register to RC mode */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg |= (IS_RC_MSK << IS_RC_SHIFT); + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Set Advanced Error Capabilities and Control PF0 register */ + reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX | + PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN | + PCIE_CORE_ERR_CAPCTL_ECRC_CHCK | + PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV; + advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG); + + /* Set PCIe Device Control and Status 1 PF0 register */ + reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE | + (7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) | + PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE | + (PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ << + PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT); + advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG); + + /* Program PCIe Control 2 to disable strict ordering */ + reg = PCIE_CORE_CTRL2_RESERVED | + PCIE_CORE_CTRL2_TD_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Set GEN2 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~PCIE_GEN_SEL_MSK; + reg |= SPEED_GEN_2; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Set lane X1 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~LANE_CNT_MSK; + reg |= LANE_COUNT_1; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Enable link training */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg |= LINK_TRAINING_EN; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Enable MSI */ + reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); + reg |= PCIE_CORE_CTRL2_MSI_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Clear all interrupts */ + advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG); + advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG); + advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG); + + /* Disable All ISR0/1 Sources */ + reg = PCIE_ISR0_ALL_MASK; + reg &= ~PCIE_ISR0_MSI_INT_PENDING; + advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); + + advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG); + + /* Unmask all MSI's */ + advk_writel(pcie, 0, PCIE_MSI_MASK_REG); + + /* Enable summary interrupt for GIC SPI source */ + reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK); + advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG); + + reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); + reg |= PCIE_CORE_CTRL2_OB_WIN_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Bypass the address window mapping for PIO */ + reg = advk_readl(pcie, PIO_CTRL); + reg |= PIO_CTRL_ADDR_WIN_DISABLE; + advk_writel(pcie, reg, PIO_CTRL); + + /* Start link training */ + reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); + reg |= PCIE_CORE_LINK_TRAINING; + advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); + + advk_pcie_wait_for_link(pcie); + + reg = PCIE_CORE_LINK_L0S_ENTRY | + (1 << PCIE_CORE_LINK_WIDTH_SHIFT); + advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); + + reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG); + reg |= PCIE_CORE_CMD_MEM_ACCESS_EN | + PCIE_CORE_CMD_IO_ACCESS_EN | + PCIE_CORE_CMD_MEM_IO_REQ_EN; + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); +} + +static void advk_pcie_check_pio_status(struct advk_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + u32 reg; + unsigned int status; + char *strcomp_status, *str_posted; + + reg = advk_readl(pcie, PIO_STAT); + status = (reg & PIO_COMPLETION_STATUS_MASK) >> + PIO_COMPLETION_STATUS_SHIFT; + + if (!status) + return; + + switch (status) { + case PIO_COMPLETION_STATUS_UR: + strcomp_status = "UR"; + break; + case PIO_COMPLETION_STATUS_CRS: + strcomp_status = "CRS"; + break; + case PIO_COMPLETION_STATUS_CA: + strcomp_status = "CA"; + break; + default: + strcomp_status = "Unknown"; + break; + } + + if (reg & PIO_NON_POSTED_REQ) + str_posted = "Non-posted"; + else + str_posted = "Posted"; + + dev_err(dev, "%s PIO Response Status: %s, %#x @ %#x\n", + str_posted, strcomp_status, reg, advk_readl(pcie, PIO_ADDR_LS)); +} + +static int advk_pcie_wait_pio(struct advk_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS); + + while (time_before(jiffies, timeout)) { + u32 start, isr; + + start = advk_readl(pcie, PIO_START); + isr = advk_readl(pcie, PIO_ISR); + if (!start && isr) + return 0; + } + + dev_err(dev, "config read/write timed out\n"); + return -ETIMEDOUT; +} + +static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 *val) +{ + struct advk_pcie *pcie = bus->sysdata; + u32 reg; + int ret; + + if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + /* Start PIO */ + advk_writel(pcie, 0, PIO_START); + advk_writel(pcie, 1, PIO_ISR); + + /* Program the control register */ + reg = advk_readl(pcie, PIO_CTRL); + reg &= ~PIO_CTRL_TYPE_MASK; + if (bus->number == pcie->root_bus_nr) + reg |= PCIE_CONFIG_RD_TYPE0; + else + reg |= PCIE_CONFIG_RD_TYPE1; + advk_writel(pcie, reg, PIO_CTRL); + + /* Program the address registers */ + reg = PCIE_CONF_ADDR(bus->number, devfn, where); + advk_writel(pcie, reg, PIO_ADDR_LS); + advk_writel(pcie, 0, PIO_ADDR_MS); + + /* Program the data strobe */ + advk_writel(pcie, 0xf, PIO_WR_DATA_STRB); + + /* Start the transfer */ + advk_writel(pcie, 1, PIO_START); + + ret = advk_pcie_wait_pio(pcie); + if (ret < 0) + return PCIBIOS_SET_FAILED; + + advk_pcie_check_pio_status(pcie); + + /* Get the read result */ + *val = advk_readl(pcie, PIO_RD_DATA); + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 3))) & 0xffff; + + return PCIBIOS_SUCCESSFUL; +} + +static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct advk_pcie *pcie = bus->sysdata; + u32 reg; + u32 data_strobe = 0x0; + int offset; + int ret; + + if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (where % size) + return PCIBIOS_SET_FAILED; + + /* Start PIO */ + advk_writel(pcie, 0, PIO_START); + advk_writel(pcie, 1, PIO_ISR); + + /* Program the control register */ + reg = advk_readl(pcie, PIO_CTRL); + reg &= ~PIO_CTRL_TYPE_MASK; + if (bus->number == pcie->root_bus_nr) + reg |= PCIE_CONFIG_WR_TYPE0; + else + reg |= PCIE_CONFIG_WR_TYPE1; + advk_writel(pcie, reg, PIO_CTRL); + + /* Program the address registers */ + reg = PCIE_CONF_ADDR(bus->number, devfn, where); + advk_writel(pcie, reg, PIO_ADDR_LS); + advk_writel(pcie, 0, PIO_ADDR_MS); + + /* Calculate the write strobe */ + offset = where & 0x3; + reg = val << (8 * offset); + data_strobe = GENMASK(size - 1, 0) << offset; + + /* Program the data register */ + advk_writel(pcie, reg, PIO_WR_DATA); + + /* Program the data strobe */ + advk_writel(pcie, data_strobe, PIO_WR_DATA_STRB); + + /* Start the transfer */ + advk_writel(pcie, 1, PIO_START); + + ret = advk_pcie_wait_pio(pcie); + if (ret < 0) + return PCIBIOS_SET_FAILED; + + advk_pcie_check_pio_status(pcie); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops advk_pcie_ops = { + .read = advk_pcie_rd_conf, + .write = advk_pcie_wr_conf, +}; + +static void advk_msi_irq_compose_msi_msg(struct irq_data *data, + struct msi_msg *msg) +{ + struct advk_pcie *pcie = irq_data_get_irq_chip_data(data); + phys_addr_t msi_msg = virt_to_phys(&pcie->msi_msg); + + msg->address_lo = lower_32_bits(msi_msg); + msg->address_hi = upper_32_bits(msi_msg); + msg->data = data->irq; +} + +static int advk_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static int advk_msi_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct advk_pcie *pcie = domain->host_data; + int hwirq, i; + + mutex_lock(&pcie->msi_used_lock); + hwirq = bitmap_find_next_zero_area(pcie->msi_used, MSI_IRQ_NUM, + 0, nr_irqs, 0); + if (hwirq >= MSI_IRQ_NUM) { + mutex_unlock(&pcie->msi_used_lock); + return -ENOSPC; + } + + bitmap_set(pcie->msi_used, hwirq, nr_irqs); + mutex_unlock(&pcie->msi_used_lock); + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, hwirq + i, + &pcie->msi_bottom_irq_chip, + domain->host_data, handle_simple_irq, + NULL, NULL); + + return hwirq; +} + +static void advk_msi_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct advk_pcie *pcie = domain->host_data; + + mutex_lock(&pcie->msi_used_lock); + bitmap_clear(pcie->msi_used, d->hwirq, nr_irqs); + mutex_unlock(&pcie->msi_used_lock); +} + +static const struct irq_domain_ops advk_msi_domain_ops = { + .alloc = advk_msi_irq_domain_alloc, + .free = advk_msi_irq_domain_free, +}; + +static void advk_pcie_irq_mask(struct irq_data *d) +{ + struct advk_pcie *pcie = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 mask; + + mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); + mask |= PCIE_ISR1_INTX_ASSERT(hwirq); + advk_writel(pcie, mask, PCIE_ISR1_MASK_REG); +} + +static void advk_pcie_irq_unmask(struct irq_data *d) +{ + struct advk_pcie *pcie = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 mask; + + mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); + mask &= ~PCIE_ISR1_INTX_ASSERT(hwirq); + advk_writel(pcie, mask, PCIE_ISR1_MASK_REG); +} + +static int advk_pcie_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) +{ + struct advk_pcie *pcie = h->host_data; + + advk_pcie_irq_mask(irq_get_irq_data(virq)); + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_and_handler(virq, &pcie->irq_chip, + handle_level_irq); + irq_set_chip_data(virq, pcie); + + return 0; +} + +static const struct irq_domain_ops advk_pcie_irq_domain_ops = { + .map = advk_pcie_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + struct irq_chip *bottom_ic, *msi_ic; + struct msi_domain_info *msi_di; + phys_addr_t msi_msg_phys; + + mutex_init(&pcie->msi_used_lock); + + bottom_ic = &pcie->msi_bottom_irq_chip; + + bottom_ic->name = "MSI"; + bottom_ic->irq_compose_msi_msg = advk_msi_irq_compose_msi_msg; + bottom_ic->irq_set_affinity = advk_msi_set_affinity; + + msi_ic = &pcie->msi_irq_chip; + msi_ic->name = "advk-MSI"; + + msi_di = &pcie->msi_domain_info; + msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_MULTI_PCI_MSI; + msi_di->chip = msi_ic; + + msi_msg_phys = virt_to_phys(&pcie->msi_msg); + + advk_writel(pcie, lower_32_bits(msi_msg_phys), + PCIE_MSI_ADDR_LOW_REG); + advk_writel(pcie, upper_32_bits(msi_msg_phys), + PCIE_MSI_ADDR_HIGH_REG); + + pcie->msi_inner_domain = + irq_domain_add_linear(NULL, MSI_IRQ_NUM, + &advk_msi_domain_ops, pcie); + if (!pcie->msi_inner_domain) + return -ENOMEM; + + pcie->msi_domain = + pci_msi_create_irq_domain(of_node_to_fwnode(node), + msi_di, pcie->msi_inner_domain); + if (!pcie->msi_domain) { + irq_domain_remove(pcie->msi_inner_domain); + return -ENOMEM; + } + + return 0; +} + +static void advk_pcie_remove_msi_irq_domain(struct advk_pcie *pcie) +{ + irq_domain_remove(pcie->msi_domain); + irq_domain_remove(pcie->msi_inner_domain); +} + +static int advk_pcie_init_irq_domain(struct advk_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; + struct irq_chip *irq_chip; + + pcie_intc_node = of_get_next_child(node, NULL); + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return -ENODEV; + } + + irq_chip = &pcie->irq_chip; + + irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", + dev_name(dev)); + if (!irq_chip->name) { + of_node_put(pcie_intc_node); + return -ENOMEM; + } + + irq_chip->irq_mask = advk_pcie_irq_mask; + irq_chip->irq_mask_ack = advk_pcie_irq_mask; + irq_chip->irq_unmask = advk_pcie_irq_unmask; + + pcie->irq_domain = + irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &advk_pcie_irq_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + of_node_put(pcie_intc_node); + return -ENOMEM; + } + + return 0; +} + +static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie) +{ + irq_domain_remove(pcie->irq_domain); +} + +static void advk_pcie_handle_msi(struct advk_pcie *pcie) +{ + u32 msi_val, msi_mask, msi_status, msi_idx; + u16 msi_data; + + msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG); + msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG); + msi_status = msi_val & ~msi_mask; + + for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) { + if (!(BIT(msi_idx) & msi_status)) + continue; + + advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG); + msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & 0xFF; + generic_handle_irq(msi_data); + } + + advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING, + PCIE_ISR0_REG); +} + +static void advk_pcie_handle_int(struct advk_pcie *pcie) +{ + u32 isr0_val, isr0_mask, isr0_status; + u32 isr1_val, isr1_mask, isr1_status; + int i, virq; + + isr0_val = advk_readl(pcie, PCIE_ISR0_REG); + isr0_mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); + isr0_status = isr0_val & ((~isr0_mask) & PCIE_ISR0_ALL_MASK); + + isr1_val = advk_readl(pcie, PCIE_ISR1_REG); + isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); + isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK); + + if (!isr0_status && !isr1_status) { + advk_writel(pcie, isr0_val, PCIE_ISR0_REG); + advk_writel(pcie, isr1_val, PCIE_ISR1_REG); + return; + } + + /* Process MSI interrupts */ + if (isr0_status & PCIE_ISR0_MSI_INT_PENDING) + advk_pcie_handle_msi(pcie); + + /* Process legacy interrupts */ + for (i = 0; i < PCI_NUM_INTX; i++) { + if (!(isr1_status & PCIE_ISR1_INTX_ASSERT(i))) + continue; + + advk_writel(pcie, PCIE_ISR1_INTX_ASSERT(i), + PCIE_ISR1_REG); + + virq = irq_find_mapping(pcie->irq_domain, i); + generic_handle_irq(virq); + } +} + +static irqreturn_t advk_pcie_irq_handler(int irq, void *arg) +{ + struct advk_pcie *pcie = arg; + u32 status; + + status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG); + if (!(status & PCIE_IRQ_CORE_INT)) + return IRQ_NONE; + + advk_pcie_handle_int(pcie); + + /* Clear interrupt */ + advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG); + + return IRQ_HANDLED; +} + +static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie) +{ + int err, res_valid = 0; + struct device *dev = &pcie->pdev->dev; + struct resource_entry *win, *tmp; + resource_size_t iobase; + + INIT_LIST_HEAD(&pcie->resources); + + err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, + &pcie->resources, &iobase); + if (err) + return err; + + err = devm_request_pci_bus_resources(dev, &pcie->resources); + if (err) + goto out_release_res; + + resource_list_for_each_entry_safe(win, tmp, &pcie->resources) { + struct resource *res = win->res; + + switch (resource_type(res)) { + case IORESOURCE_IO: + advk_pcie_set_ob_win(pcie, 1, + upper_32_bits(res->start), + lower_32_bits(res->start), + 0, 0xF8000000, 0, + lower_32_bits(res->start), + OB_PCIE_IO); + err = pci_remap_iospace(res, iobase); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, res); + resource_list_destroy_entry(win); + } + break; + case IORESOURCE_MEM: + advk_pcie_set_ob_win(pcie, 0, + upper_32_bits(res->start), + lower_32_bits(res->start), + 0x0, 0xF8000000, 0, + lower_32_bits(res->start), + (2 << 20) | OB_PCIE_MEM); + res_valid |= !(res->flags & IORESOURCE_PREFETCH); + break; + case IORESOURCE_BUS: + pcie->root_bus_nr = res->start; + break; + } + } + + if (!res_valid) { + dev_err(dev, "non-prefetchable memory resource required\n"); + err = -EINVAL; + goto out_release_res; + } + + return 0; + +out_release_res: + pci_free_resource_list(&pcie->resources); + return err; +} + +static int advk_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct advk_pcie *pcie; + struct resource *res; + struct pci_bus *bus, *child; + struct pci_host_bridge *bridge; + int ret, irq; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie)); + if (!bridge) + return -ENOMEM; + + pcie = pci_host_bridge_priv(bridge); + pcie->pdev = pdev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcie->base = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->base)) + return PTR_ERR(pcie->base); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, irq, advk_pcie_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie", + pcie); + if (ret) { + dev_err(dev, "Failed to register interrupt\n"); + return ret; + } + + ret = advk_pcie_parse_request_of_pci_ranges(pcie); + if (ret) { + dev_err(dev, "Failed to parse resources\n"); + return ret; + } + + advk_pcie_setup_hw(pcie); + + ret = advk_pcie_init_irq_domain(pcie); + if (ret) { + dev_err(dev, "Failed to initialize irq\n"); + return ret; + } + + ret = advk_pcie_init_msi_irq_domain(pcie); + if (ret) { + dev_err(dev, "Failed to initialize irq\n"); + advk_pcie_remove_irq_domain(pcie); + return ret; + } + + list_splice_init(&pcie->resources, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = pcie; + bridge->busnr = 0; + bridge->ops = &advk_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret < 0) { + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); + return ret; + } + + bus = bridge->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 const struct of_device_id advk_pcie_of_match_table[] = { + { .compatible = "marvell,armada-3700-pcie", }, + {}, +}; + +static struct platform_driver advk_pcie_driver = { + .driver = { + .name = "advk-pcie", + .of_match_table = advk_pcie_of_match_table, + /* Driver unloading/unbinding currently not supported */ + .suppress_bind_attrs = true, + }, + .probe = advk_pcie_probe, +}; +builtin_platform_driver(advk_pcie_driver); diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c new file mode 100644 index 000000000000..a1ebe9ed441f --- /dev/null +++ b/drivers/pci/controller/pci-ftpci100.c @@ -0,0 +1,619 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for Faraday Technology FTPC100 PCI Controller + * + * Copyright (C) 2017 Linus Walleij + * + * Based on the out-of-tree OpenWRT patch for Cortina Gemini: + * Copyright (C) 2009 Janos Laube + * Copyright (C) 2009 Paulius Zaleckas + * Based on SL2312 PCI controller code + * Storlink (C) 2003 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* + * Special configuration registers directly in the first few words + * in I/O space. + */ +#define PCI_IOSIZE 0x00 +#define PCI_PROT 0x04 /* AHB protection */ +#define PCI_CTRL 0x08 /* PCI control signal */ +#define PCI_SOFTRST 0x10 /* Soft reset counter and response error enable */ +#define PCI_CONFIG 0x28 /* PCI configuration command register */ +#define PCI_DATA 0x2C + +#define FARADAY_PCI_STATUS_CMD 0x04 /* Status and command */ +#define FARADAY_PCI_PMC 0x40 /* Power management control */ +#define FARADAY_PCI_PMCSR 0x44 /* Power management status */ +#define FARADAY_PCI_CTRL1 0x48 /* Control register 1 */ +#define FARADAY_PCI_CTRL2 0x4C /* Control register 2 */ +#define FARADAY_PCI_MEM1_BASE_SIZE 0x50 /* Memory base and size #1 */ +#define FARADAY_PCI_MEM2_BASE_SIZE 0x54 /* Memory base and size #2 */ +#define FARADAY_PCI_MEM3_BASE_SIZE 0x58 /* Memory base and size #3 */ + +#define PCI_STATUS_66MHZ_CAPABLE BIT(21) + +/* Bits 31..28 gives INTD..INTA status */ +#define PCI_CTRL2_INTSTS_SHIFT 28 +#define PCI_CTRL2_INTMASK_CMDERR BIT(27) +#define PCI_CTRL2_INTMASK_PARERR BIT(26) +/* Bits 25..22 masks INTD..INTA */ +#define PCI_CTRL2_INTMASK_SHIFT 22 +#define PCI_CTRL2_INTMASK_MABRT_RX BIT(21) +#define PCI_CTRL2_INTMASK_TABRT_RX BIT(20) +#define PCI_CTRL2_INTMASK_TABRT_TX BIT(19) +#define PCI_CTRL2_INTMASK_RETRY4 BIT(18) +#define PCI_CTRL2_INTMASK_SERR_RX BIT(17) +#define PCI_CTRL2_INTMASK_PERR_RX BIT(16) +/* Bit 15 reserved */ +#define PCI_CTRL2_MSTPRI_REQ6 BIT(14) +#define PCI_CTRL2_MSTPRI_REQ5 BIT(13) +#define PCI_CTRL2_MSTPRI_REQ4 BIT(12) +#define PCI_CTRL2_MSTPRI_REQ3 BIT(11) +#define PCI_CTRL2_MSTPRI_REQ2 BIT(10) +#define PCI_CTRL2_MSTPRI_REQ1 BIT(9) +#define PCI_CTRL2_MSTPRI_REQ0 BIT(8) +/* Bits 7..4 reserved */ +/* Bits 3..0 TRDYW */ + +/* + * Memory configs: + * Bit 31..20 defines the PCI side memory base + * Bit 19..16 (4 bits) defines the size per below + */ +#define FARADAY_PCI_MEMBASE_MASK 0xfff00000 +#define FARADAY_PCI_MEMSIZE_1MB 0x0 +#define FARADAY_PCI_MEMSIZE_2MB 0x1 +#define FARADAY_PCI_MEMSIZE_4MB 0x2 +#define FARADAY_PCI_MEMSIZE_8MB 0x3 +#define FARADAY_PCI_MEMSIZE_16MB 0x4 +#define FARADAY_PCI_MEMSIZE_32MB 0x5 +#define FARADAY_PCI_MEMSIZE_64MB 0x6 +#define FARADAY_PCI_MEMSIZE_128MB 0x7 +#define FARADAY_PCI_MEMSIZE_256MB 0x8 +#define FARADAY_PCI_MEMSIZE_512MB 0x9 +#define FARADAY_PCI_MEMSIZE_1GB 0xa +#define FARADAY_PCI_MEMSIZE_2GB 0xb +#define FARADAY_PCI_MEMSIZE_SHIFT 16 + +/* + * The DMA base is set to 0x0 for all memory segments, it reflects the + * fact that the memory of the host system starts at 0x0. + */ +#define FARADAY_PCI_DMA_MEM1_BASE 0x00000000 +#define FARADAY_PCI_DMA_MEM2_BASE 0x00000000 +#define FARADAY_PCI_DMA_MEM3_BASE 0x00000000 + +/* Defines for PCI configuration command register */ +#define PCI_CONF_ENABLE BIT(31) +#define PCI_CONF_WHERE(r) ((r) & 0xFC) +#define PCI_CONF_BUS(b) (((b) & 0xFF) << 16) +#define PCI_CONF_DEVICE(d) (((d) & 0x1F) << 11) +#define PCI_CONF_FUNCTION(f) (((f) & 0x07) << 8) + +/** + * struct faraday_pci_variant - encodes IP block differences + * @cascaded_irq: this host has cascaded IRQs from an interrupt controller + * embedded in the host bridge. + */ +struct faraday_pci_variant { + bool cascaded_irq; +}; + +struct faraday_pci { + struct device *dev; + void __iomem *base; + struct irq_domain *irqdomain; + struct pci_bus *bus; + struct clk *bus_clk; +}; + +static int faraday_res_to_memcfg(resource_size_t mem_base, + resource_size_t mem_size, u32 *val) +{ + u32 outval; + + switch (mem_size) { + case SZ_1M: + outval = FARADAY_PCI_MEMSIZE_1MB; + break; + case SZ_2M: + outval = FARADAY_PCI_MEMSIZE_2MB; + break; + case SZ_4M: + outval = FARADAY_PCI_MEMSIZE_4MB; + break; + case SZ_8M: + outval = FARADAY_PCI_MEMSIZE_8MB; + break; + case SZ_16M: + outval = FARADAY_PCI_MEMSIZE_16MB; + break; + case SZ_32M: + outval = FARADAY_PCI_MEMSIZE_32MB; + break; + case SZ_64M: + outval = FARADAY_PCI_MEMSIZE_64MB; + break; + case SZ_128M: + outval = FARADAY_PCI_MEMSIZE_128MB; + break; + case SZ_256M: + outval = FARADAY_PCI_MEMSIZE_256MB; + break; + case SZ_512M: + outval = FARADAY_PCI_MEMSIZE_512MB; + break; + case SZ_1G: + outval = FARADAY_PCI_MEMSIZE_1GB; + break; + case SZ_2G: + outval = FARADAY_PCI_MEMSIZE_2GB; + break; + default: + return -EINVAL; + } + outval <<= FARADAY_PCI_MEMSIZE_SHIFT; + + /* This is probably not good */ + if (mem_base & ~(FARADAY_PCI_MEMBASE_MASK)) + pr_warn("truncated PCI memory base\n"); + /* Translate to bridge side address space */ + outval |= (mem_base & FARADAY_PCI_MEMBASE_MASK); + pr_debug("Translated pci base @%pap, size %pap to config %08x\n", + &mem_base, &mem_size, outval); + + *val = outval; + return 0; +} + +static int faraday_raw_pci_read_config(struct faraday_pci *p, int bus_number, + unsigned int fn, int config, int size, + u32 *value) +{ + writel(PCI_CONF_BUS(bus_number) | + PCI_CONF_DEVICE(PCI_SLOT(fn)) | + PCI_CONF_FUNCTION(PCI_FUNC(fn)) | + PCI_CONF_WHERE(config) | + PCI_CONF_ENABLE, + p->base + PCI_CONFIG); + + *value = readl(p->base + PCI_DATA); + + if (size == 1) + *value = (*value >> (8 * (config & 3))) & 0xFF; + else if (size == 2) + *value = (*value >> (8 * (config & 3))) & 0xFFFF; + + return PCIBIOS_SUCCESSFUL; +} + +static int faraday_pci_read_config(struct pci_bus *bus, unsigned int fn, + int config, int size, u32 *value) +{ + struct faraday_pci *p = bus->sysdata; + + dev_dbg(&bus->dev, + "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); + + return faraday_raw_pci_read_config(p, bus->number, fn, config, size, value); +} + +static int faraday_raw_pci_write_config(struct faraday_pci *p, int bus_number, + unsigned int fn, int config, int size, + u32 value) +{ + int ret = PCIBIOS_SUCCESSFUL; + + writel(PCI_CONF_BUS(bus_number) | + PCI_CONF_DEVICE(PCI_SLOT(fn)) | + PCI_CONF_FUNCTION(PCI_FUNC(fn)) | + PCI_CONF_WHERE(config) | + PCI_CONF_ENABLE, + p->base + PCI_CONFIG); + + switch (size) { + case 4: + writel(value, p->base + PCI_DATA); + break; + case 2: + writew(value, p->base + PCI_DATA + (config & 3)); + break; + case 1: + writeb(value, p->base + PCI_DATA + (config & 3)); + break; + default: + ret = PCIBIOS_BAD_REGISTER_NUMBER; + } + + return ret; +} + +static int faraday_pci_write_config(struct pci_bus *bus, unsigned int fn, + int config, int size, u32 value) +{ + struct faraday_pci *p = bus->sysdata; + + dev_dbg(&bus->dev, + "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); + + return faraday_raw_pci_write_config(p, bus->number, fn, config, size, + value); +} + +static struct pci_ops faraday_pci_ops = { + .read = faraday_pci_read_config, + .write = faraday_pci_write_config, +}; + +static void faraday_pci_ack_irq(struct irq_data *d) +{ + struct faraday_pci *p = irq_data_get_irq_chip_data(d); + unsigned int reg; + + faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); + reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); + reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTSTS_SHIFT); + faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); +} + +static void faraday_pci_mask_irq(struct irq_data *d) +{ + struct faraday_pci *p = irq_data_get_irq_chip_data(d); + unsigned int reg; + + faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); + reg &= ~((0xF << PCI_CTRL2_INTSTS_SHIFT) + | BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT)); + faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); +} + +static void faraday_pci_unmask_irq(struct irq_data *d) +{ + struct faraday_pci *p = irq_data_get_irq_chip_data(d); + unsigned int reg; + + faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); + reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); + reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT); + faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); +} + +static void faraday_pci_irq_handler(struct irq_desc *desc) +{ + struct faraday_pci *p = irq_desc_get_handler_data(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + unsigned int irq_stat, reg, i; + + faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); + irq_stat = reg >> PCI_CTRL2_INTSTS_SHIFT; + + chained_irq_enter(irqchip, desc); + + for (i = 0; i < 4; i++) { + if ((irq_stat & BIT(i)) == 0) + continue; + generic_handle_irq(irq_find_mapping(p->irqdomain, i)); + } + + chained_irq_exit(irqchip, desc); +} + +static struct irq_chip faraday_pci_irq_chip = { + .name = "PCI", + .irq_ack = faraday_pci_ack_irq, + .irq_mask = faraday_pci_mask_irq, + .irq_unmask = faraday_pci_unmask_irq, +}; + +static int faraday_pci_irq_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &faraday_pci_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops faraday_pci_irqdomain_ops = { + .map = faraday_pci_irq_map, +}; + +static int faraday_pci_setup_cascaded_irq(struct faraday_pci *p) +{ + struct device_node *intc = of_get_next_child(p->dev->of_node, NULL); + int irq; + int i; + + if (!intc) { + dev_err(p->dev, "missing child interrupt-controller node\n"); + return -EINVAL; + } + + /* All PCI IRQs cascade off this one */ + irq = of_irq_get(intc, 0); + if (irq <= 0) { + dev_err(p->dev, "failed to get parent IRQ\n"); + return irq ?: -EINVAL; + } + + p->irqdomain = irq_domain_add_linear(intc, PCI_NUM_INTX, + &faraday_pci_irqdomain_ops, p); + if (!p->irqdomain) { + dev_err(p->dev, "failed to create Gemini PCI IRQ domain\n"); + return -EINVAL; + } + + irq_set_chained_handler_and_data(irq, faraday_pci_irq_handler, p); + + for (i = 0; i < 4; i++) + irq_create_mapping(p->irqdomain, i); + + return 0; +} + +static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, + struct device_node *np) +{ + struct of_pci_range range; + struct of_pci_range_parser parser; + struct device *dev = p->dev; + u32 confreg[3] = { + FARADAY_PCI_MEM1_BASE_SIZE, + FARADAY_PCI_MEM2_BASE_SIZE, + FARADAY_PCI_MEM3_BASE_SIZE, + }; + int i = 0; + u32 val; + + if (of_pci_dma_range_parser_init(&parser, np)) { + dev_err(dev, "missing dma-ranges property\n"); + return -EINVAL; + } + + /* + * Get the dma-ranges from the device tree + */ + for_each_of_pci_range(&parser, &range) { + u64 end = range.pci_addr + range.size - 1; + int ret; + + ret = faraday_res_to_memcfg(range.pci_addr, range.size, &val); + if (ret) { + dev_err(dev, + "DMA range %d: illegal MEM resource size\n", i); + return -EINVAL; + } + + dev_info(dev, "DMA MEM%d BASE: 0x%016llx -> 0x%016llx config %08x\n", + i + 1, range.pci_addr, end, val); + if (i <= 2) { + faraday_raw_pci_write_config(p, 0, 0, confreg[i], + 4, val); + } else { + dev_err(dev, "ignore extraneous dma-range %d\n", i); + break; + } + + i++; + } + + return 0; +} + +static int faraday_pci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct faraday_pci_variant *variant = + of_device_get_match_data(dev); + struct resource *regs; + resource_size_t io_base; + struct resource_entry *win; + struct faraday_pci *p; + struct resource *mem; + struct resource *io; + struct pci_host_bridge *host; + struct clk *clk; + unsigned char max_bus_speed = PCI_SPEED_33MHz; + unsigned char cur_bus_speed = PCI_SPEED_33MHz; + int ret; + u32 val; + LIST_HEAD(res); + + host = devm_pci_alloc_host_bridge(dev, sizeof(*p)); + if (!host) + return -ENOMEM; + + host->dev.parent = dev; + host->ops = &faraday_pci_ops; + host->busnr = 0; + host->msi = NULL; + host->map_irq = of_irq_parse_and_map_pci; + host->swizzle_irq = pci_common_swizzle; + p = pci_host_bridge_priv(host); + host->sysdata = p; + p->dev = dev; + + /* Retrieve and enable optional clocks */ + clk = devm_clk_get(dev, "PCLK"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "could not prepare PCLK\n"); + return ret; + } + p->bus_clk = devm_clk_get(dev, "PCICLK"); + if (IS_ERR(p->bus_clk)) + return PTR_ERR(p->bus_clk); + ret = clk_prepare_enable(p->bus_clk); + if (ret) { + dev_err(dev, "could not prepare PCICLK\n"); + return ret; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + p->base = devm_ioremap_resource(dev, regs); + if (IS_ERR(p->base)) + return PTR_ERR(p->base); + + ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, + &res, &io_base); + if (ret) + return ret; + + ret = devm_request_pci_bus_resources(dev, &res); + if (ret) + return ret; + + /* Get the I/O and memory ranges from DT */ + resource_list_for_each_entry(win, &res) { + switch (resource_type(win->res)) { + case IORESOURCE_IO: + io = win->res; + io->name = "Gemini PCI I/O"; + if (!faraday_res_to_memcfg(io->start - win->offset, + resource_size(io), &val)) { + /* setup I/O space size */ + writel(val, p->base + PCI_IOSIZE); + } else { + dev_err(dev, "illegal IO mem size\n"); + return -EINVAL; + } + ret = pci_remap_iospace(io, io_base); + if (ret) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + ret, io); + continue; + } + break; + case IORESOURCE_MEM: + mem = win->res; + mem->name = "Gemini PCI MEM"; + break; + case IORESOURCE_BUS: + break; + default: + break; + } + } + + /* Setup hostbridge */ + val = readl(p->base + PCI_CTRL); + val |= PCI_COMMAND_IO; + val |= PCI_COMMAND_MEMORY; + val |= PCI_COMMAND_MASTER; + writel(val, p->base + PCI_CTRL); + /* Mask and clear all interrupts */ + faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2 + 2, 2, 0xF000); + if (variant->cascaded_irq) { + ret = faraday_pci_setup_cascaded_irq(p); + if (ret) { + dev_err(dev, "failed to setup cascaded IRQ\n"); + return ret; + } + } + + /* Check bus clock if we can gear up to 66 MHz */ + if (!IS_ERR(p->bus_clk)) { + unsigned long rate; + u32 val; + + faraday_raw_pci_read_config(p, 0, 0, + FARADAY_PCI_STATUS_CMD, 4, &val); + rate = clk_get_rate(p->bus_clk); + + if ((rate == 33000000) && (val & PCI_STATUS_66MHZ_CAPABLE)) { + dev_info(dev, "33MHz bus is 66MHz capable\n"); + max_bus_speed = PCI_SPEED_66MHz; + ret = clk_set_rate(p->bus_clk, 66000000); + if (ret) + dev_err(dev, "failed to set bus clock\n"); + } else { + dev_info(dev, "33MHz only bus\n"); + max_bus_speed = PCI_SPEED_33MHz; + } + + /* Bumping the clock may fail so read back the rate */ + rate = clk_get_rate(p->bus_clk); + if (rate == 33000000) + cur_bus_speed = PCI_SPEED_33MHz; + if (rate == 66000000) + cur_bus_speed = PCI_SPEED_66MHz; + } + + ret = faraday_pci_parse_map_dma_ranges(p, dev->of_node); + if (ret) + return ret; + + list_splice_init(&res, &host->windows); + ret = pci_scan_root_bus_bridge(host); + if (ret) { + dev_err(dev, "failed to scan host: %d\n", ret); + return ret; + } + p->bus = host->bus; + p->bus->max_bus_speed = max_bus_speed; + p->bus->cur_bus_speed = cur_bus_speed; + + pci_bus_assign_resources(p->bus); + pci_bus_add_devices(p->bus); + pci_free_resource_list(&res); + + return 0; +} + +/* + * We encode bridge variants here, we have at least two so it doesn't + * hurt to have infrastructure to encompass future variants as well. + */ +static const struct faraday_pci_variant faraday_regular = { + .cascaded_irq = true, +}; + +static const struct faraday_pci_variant faraday_dual = { + .cascaded_irq = false, +}; + +static const struct of_device_id faraday_pci_of_match[] = { + { + .compatible = "faraday,ftpci100", + .data = &faraday_regular, + }, + { + .compatible = "faraday,ftpci100-dual", + .data = &faraday_dual, + }, + {}, +}; + +static struct platform_driver faraday_pci_driver = { + .driver = { + .name = "ftpci100", + .of_match_table = of_match_ptr(faraday_pci_of_match), + .suppress_bind_attrs = true, + }, + .probe = faraday_pci_probe, +}; +builtin_platform_driver(faraday_pci_driver); diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c new file mode 100644 index 000000000000..d8f10451f273 --- /dev/null +++ b/drivers/pci/controller/pci-host-common.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic PCI host driver common code + * + * Copyright (C) 2014 ARM Limited + * + * Author: Will Deacon + */ + +#include +#include +#include +#include +#include + +static void gen_pci_unmap_cfg(void *ptr) +{ + pci_ecam_free((struct pci_config_window *)ptr); +} + +static struct pci_config_window *gen_pci_init(struct device *dev, + struct list_head *resources, struct pci_ecam_ops *ops) +{ + int err; + struct resource cfgres; + struct resource *bus_range = NULL; + struct pci_config_window *cfg; + + /* Parse our PCI ranges and request their resources */ + err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); + if (err) + return ERR_PTR(err); + + err = of_address_to_resource(dev->of_node, 0, &cfgres); + if (err) { + dev_err(dev, "missing \"reg\" property\n"); + goto err_out; + } + + cfg = pci_ecam_create(dev, &cfgres, bus_range, ops); + if (IS_ERR(cfg)) { + err = PTR_ERR(cfg); + goto err_out; + } + + err = devm_add_action(dev, gen_pci_unmap_cfg, cfg); + if (err) { + gen_pci_unmap_cfg(cfg); + goto err_out; + } + return cfg; + +err_out: + pci_free_resource_list(resources); + return ERR_PTR(err); +} + +int pci_host_common_probe(struct platform_device *pdev, + struct pci_ecam_ops *ops) +{ + const char *type; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct pci_host_bridge *bridge; + struct pci_config_window *cfg; + struct list_head resources; + int ret; + + bridge = devm_pci_alloc_host_bridge(dev, 0); + if (!bridge) + return -ENOMEM; + + type = of_get_property(np, "device_type", NULL); + if (!type || strcmp(type, "pci")) { + dev_err(dev, "invalid \"device_type\" %s\n", type); + return -EINVAL; + } + + of_pci_check_probe_only(); + + /* Parse and map our Configuration Space windows */ + cfg = gen_pci_init(dev, &resources, ops); + if (IS_ERR(cfg)) + return PTR_ERR(cfg); + + /* Do not reassign resources if probe only */ + if (!pci_has_flag(PCI_PROBE_ONLY)) + pci_add_flags(PCI_REASSIGN_ALL_BUS); + + list_splice_init(&resources, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = cfg; + bridge->busnr = cfg->busr.start; + bridge->ops = &ops->pci_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_host_probe(bridge); + if (ret < 0) { + pci_free_resource_list(&resources); + return ret; + } + + platform_set_drvdata(pdev, bridge->bus); + return 0; +} + +int pci_host_common_remove(struct platform_device *pdev) +{ + struct pci_bus *bus = platform_get_drvdata(pdev); + + pci_lock_rescan_remove(); + pci_stop_root_bus(bus); + pci_remove_root_bus(bus); + pci_unlock_rescan_remove(); + + return 0; +} diff --git a/drivers/pci/controller/pci-host-generic.c b/drivers/pci/controller/pci-host-generic.c new file mode 100644 index 000000000000..dea3ec7592a2 --- /dev/null +++ b/drivers/pci/controller/pci-host-generic.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Simple, generic PCI host controller driver targetting firmware-initialised + * systems and virtual machines (e.g. the PCI emulation provided by kvmtool). + * + * Copyright (C) 2014 ARM Limited + * + * Author: Will Deacon + */ + +#include +#include +#include +#include +#include +#include + +static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { + .bus_shift = 16, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, + } +}; + +static bool pci_dw_valid_device(struct pci_bus *bus, unsigned int devfn) +{ + struct pci_config_window *cfg = bus->sysdata; + + /* + * The Synopsys DesignWare PCIe controller in ECAM mode will not filter + * type 0 config TLPs sent to devices 1 and up on its downstream port, + * resulting in devices appearing multiple times on bus 0 unless we + * filter out those accesses here. + */ + if (bus->number == cfg->busr.start && PCI_SLOT(devfn) > 0) + return false; + + return true; +} + +static void __iomem *pci_dw_ecam_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + if (!pci_dw_valid_device(bus, devfn)) + return NULL; + + return pci_ecam_map_bus(bus, devfn, where); +} + +static struct pci_ecam_ops pci_dw_ecam_bus_ops = { + .bus_shift = 20, + .pci_ops = { + .map_bus = pci_dw_ecam_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, + } +}; + +static const struct of_device_id gen_pci_of_match[] = { + { .compatible = "pci-host-cam-generic", + .data = &gen_pci_cfg_cam_bus_ops }, + + { .compatible = "pci-host-ecam-generic", + .data = &pci_generic_ecam_ops }, + + { .compatible = "marvell,armada8k-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + + { .compatible = "socionext,synquacer-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + + { .compatible = "snps,dw-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + + { }, +}; + +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, + .suppress_bind_attrs = true, + }, + .probe = gen_pci_probe, + .remove = pci_host_common_remove, +}; +builtin_platform_driver(gen_pci_driver); diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c new file mode 100644 index 000000000000..6cc5036ac83c --- /dev/null +++ b/drivers/pci/controller/pci-hyperv.c @@ -0,0 +1,2694 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Microsoft Corporation. + * + * Author: + * Jake Oshins + * + * This driver acts as a paravirtual front-end for PCI Express root buses. + * When a PCI Express function (either an entire device or an SR-IOV + * Virtual Function) is being passed through to the VM, this driver exposes + * a new bus to the guest VM. This is modeled as a root PCI bus because + * no bridges are being exposed to the VM. In fact, with a "Generation 2" + * VM within Hyper-V, there may seem to be no PCI bus at all in the VM + * until a device as been exposed using this driver. + * + * Each root PCI bus has its own PCI domain, which is called "Segment" in + * the PCI Firmware Specifications. Thus while each device passed through + * to the VM using this front-end will appear at "device 0", the domain will + * be unique. Typically, each bus will have one PCI function on it, though + * this driver does support more than one. + * + * In order to map the interrupts from the device through to the guest VM, + * this driver also implements an IRQ Domain, which handles interrupts (either + * MSI or MSI-X) associated with the functions on the bus. As interrupts are + * set up, torn down, or reaffined, this driver communicates with the + * underlying hypervisor to adjust the mappings in the I/O MMU so that each + * interrupt will be delivered to the correct virtual processor at the right + * vector. This driver does not support level-triggered (line-based) + * interrupts, and will report that the Interrupt Line register in the + * function's configuration space is zero. + * + * The rest of this driver mostly maps PCI concepts onto underlying Hyper-V + * facilities. For instance, the configuration space of a function exposed + * by Hyper-V is mapped into a single page of memory space, and the + * read and write handlers for config space must be aware of this mechanism. + * Similarly, device setup and teardown involves messages sent to and from + * the PCI back-end driver in Hyper-V. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Protocol versions. The low word is the minor version, the high word the + * major version. + */ + +#define PCI_MAKE_VERSION(major, minor) ((u32)(((major) << 16) | (minor))) +#define PCI_MAJOR_VERSION(version) ((u32)(version) >> 16) +#define PCI_MINOR_VERSION(version) ((u32)(version) & 0xff) + +enum pci_protocol_version_t { + PCI_PROTOCOL_VERSION_1_1 = PCI_MAKE_VERSION(1, 1), /* Win10 */ + PCI_PROTOCOL_VERSION_1_2 = PCI_MAKE_VERSION(1, 2), /* RS1 */ +}; + +#define CPU_AFFINITY_ALL -1ULL + +/* + * Supported protocol versions in the order of probing - highest go + * first. + */ +static enum pci_protocol_version_t pci_protocol_versions[] = { + PCI_PROTOCOL_VERSION_1_2, + PCI_PROTOCOL_VERSION_1_1, +}; + +/* + * Protocol version negotiated by hv_pci_protocol_negotiation(). + */ +static enum pci_protocol_version_t pci_protocol_version; + +#define PCI_CONFIG_MMIO_LENGTH 0x2000 +#define CFG_PAGE_OFFSET 0x1000 +#define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET) + +#define MAX_SUPPORTED_MSI_MESSAGES 0x400 + +#define STATUS_REVISION_MISMATCH 0xC0000059 + +/* + * Message Types + */ + +enum pci_message_type { + /* + * Version 1.1 + */ + PCI_MESSAGE_BASE = 0x42490000, + PCI_BUS_RELATIONS = PCI_MESSAGE_BASE + 0, + PCI_QUERY_BUS_RELATIONS = PCI_MESSAGE_BASE + 1, + PCI_POWER_STATE_CHANGE = PCI_MESSAGE_BASE + 4, + PCI_QUERY_RESOURCE_REQUIREMENTS = PCI_MESSAGE_BASE + 5, + PCI_QUERY_RESOURCE_RESOURCES = PCI_MESSAGE_BASE + 6, + PCI_BUS_D0ENTRY = PCI_MESSAGE_BASE + 7, + PCI_BUS_D0EXIT = PCI_MESSAGE_BASE + 8, + PCI_READ_BLOCK = PCI_MESSAGE_BASE + 9, + PCI_WRITE_BLOCK = PCI_MESSAGE_BASE + 0xA, + PCI_EJECT = PCI_MESSAGE_BASE + 0xB, + PCI_QUERY_STOP = PCI_MESSAGE_BASE + 0xC, + PCI_REENABLE = PCI_MESSAGE_BASE + 0xD, + PCI_QUERY_STOP_FAILED = PCI_MESSAGE_BASE + 0xE, + PCI_EJECTION_COMPLETE = PCI_MESSAGE_BASE + 0xF, + PCI_RESOURCES_ASSIGNED = PCI_MESSAGE_BASE + 0x10, + PCI_RESOURCES_RELEASED = PCI_MESSAGE_BASE + 0x11, + PCI_INVALIDATE_BLOCK = PCI_MESSAGE_BASE + 0x12, + PCI_QUERY_PROTOCOL_VERSION = PCI_MESSAGE_BASE + 0x13, + PCI_CREATE_INTERRUPT_MESSAGE = PCI_MESSAGE_BASE + 0x14, + PCI_DELETE_INTERRUPT_MESSAGE = PCI_MESSAGE_BASE + 0x15, + PCI_RESOURCES_ASSIGNED2 = PCI_MESSAGE_BASE + 0x16, + PCI_CREATE_INTERRUPT_MESSAGE2 = PCI_MESSAGE_BASE + 0x17, + PCI_DELETE_INTERRUPT_MESSAGE2 = PCI_MESSAGE_BASE + 0x18, /* unused */ + PCI_MESSAGE_MAXIMUM +}; + +/* + * Structures defining the virtual PCI Express protocol. + */ + +union pci_version { + struct { + u16 minor_version; + u16 major_version; + } parts; + u32 version; +} __packed; + +/* + * Function numbers are 8-bits wide on Express, as interpreted through ARI, + * which is all this driver does. This representation is the one used in + * Windows, which is what is expected when sending this back and forth with + * the Hyper-V parent partition. + */ +union win_slot_encoding { + struct { + u32 dev:5; + u32 func:3; + u32 reserved:24; + } bits; + u32 slot; +} __packed; + +/* + * Pretty much as defined in the PCI Specifications. + */ +struct pci_function_description { + u16 v_id; /* vendor ID */ + u16 d_id; /* device ID */ + u8 rev; + u8 prog_intf; + u8 subclass; + u8 base_class; + u32 subsystem_id; + union win_slot_encoding win_slot; + u32 ser; /* serial number */ +} __packed; + +/** + * struct hv_msi_desc + * @vector: IDT entry + * @delivery_mode: As defined in Intel's Programmer's + * Reference Manual, Volume 3, Chapter 8. + * @vector_count: Number of contiguous entries in the + * Interrupt Descriptor Table that are + * occupied by this Message-Signaled + * Interrupt. For "MSI", as first defined + * in PCI 2.2, this can be between 1 and + * 32. For "MSI-X," as first defined in PCI + * 3.0, this must be 1, as each MSI-X table + * entry would have its own descriptor. + * @reserved: Empty space + * @cpu_mask: All the target virtual processors. + */ +struct hv_msi_desc { + u8 vector; + u8 delivery_mode; + u16 vector_count; + u32 reserved; + u64 cpu_mask; +} __packed; + +/** + * struct hv_msi_desc2 - 1.2 version of hv_msi_desc + * @vector: IDT entry + * @delivery_mode: As defined in Intel's Programmer's + * Reference Manual, Volume 3, Chapter 8. + * @vector_count: Number of contiguous entries in the + * Interrupt Descriptor Table that are + * occupied by this Message-Signaled + * Interrupt. For "MSI", as first defined + * in PCI 2.2, this can be between 1 and + * 32. For "MSI-X," as first defined in PCI + * 3.0, this must be 1, as each MSI-X table + * entry would have its own descriptor. + * @processor_count: number of bits enabled in array. + * @processor_array: All the target virtual processors. + */ +struct hv_msi_desc2 { + u8 vector; + u8 delivery_mode; + u16 vector_count; + u16 processor_count; + u16 processor_array[32]; +} __packed; + +/** + * struct tran_int_desc + * @reserved: unused, padding + * @vector_count: same as in hv_msi_desc + * @data: This is the "data payload" value that is + * written by the device when it generates + * a message-signaled interrupt, either MSI + * or MSI-X. + * @address: This is the address to which the data + * payload is written on interrupt + * generation. + */ +struct tran_int_desc { + u16 reserved; + u16 vector_count; + u32 data; + u64 address; +} __packed; + +/* + * A generic message format for virtual PCI. + * Specific message formats are defined later in the file. + */ + +struct pci_message { + u32 type; +} __packed; + +struct pci_child_message { + struct pci_message message_type; + union win_slot_encoding wslot; +} __packed; + +struct pci_incoming_message { + struct vmpacket_descriptor hdr; + struct pci_message message_type; +} __packed; + +struct pci_response { + struct vmpacket_descriptor hdr; + s32 status; /* negative values are failures */ +} __packed; + +struct pci_packet { + void (*completion_func)(void *context, struct pci_response *resp, + int resp_packet_size); + void *compl_ctxt; + + struct pci_message message[0]; +}; + +/* + * Specific message types supporting the PCI protocol. + */ + +/* + * Version negotiation message. Sent from the guest to the host. + * The guest is free to try different versions until the host + * accepts the version. + * + * pci_version: The protocol version requested. + * is_last_attempt: If TRUE, this is the last version guest will request. + * reservedz: Reserved field, set to zero. + */ + +struct pci_version_request { + struct pci_message message_type; + u32 protocol_version; +} __packed; + +/* + * Bus D0 Entry. This is sent from the guest to the host when the virtual + * bus (PCI Express port) is ready for action. + */ + +struct pci_bus_d0_entry { + struct pci_message message_type; + u32 reserved; + u64 mmio_base; +} __packed; + +struct pci_bus_relations { + struct pci_incoming_message incoming; + u32 device_count; + struct pci_function_description func[0]; +} __packed; + +struct pci_q_res_req_response { + struct vmpacket_descriptor hdr; + s32 status; /* negative values are failures */ + u32 probed_bar[6]; +} __packed; + +struct pci_set_power { + struct pci_message message_type; + union win_slot_encoding wslot; + u32 power_state; /* In Windows terms */ + u32 reserved; +} __packed; + +struct pci_set_power_response { + struct vmpacket_descriptor hdr; + s32 status; /* negative values are failures */ + union win_slot_encoding wslot; + u32 resultant_state; /* In Windows terms */ + u32 reserved; +} __packed; + +struct pci_resources_assigned { + struct pci_message message_type; + union win_slot_encoding wslot; + u8 memory_range[0x14][6]; /* not used here */ + u32 msi_descriptors; + u32 reserved[4]; +} __packed; + +struct pci_resources_assigned2 { + struct pci_message message_type; + union win_slot_encoding wslot; + u8 memory_range[0x14][6]; /* not used here */ + u32 msi_descriptor_count; + u8 reserved[70]; +} __packed; + +struct pci_create_interrupt { + struct pci_message message_type; + union win_slot_encoding wslot; + struct hv_msi_desc int_desc; +} __packed; + +struct pci_create_int_response { + struct pci_response response; + u32 reserved; + struct tran_int_desc int_desc; +} __packed; + +struct pci_create_interrupt2 { + struct pci_message message_type; + union win_slot_encoding wslot; + struct hv_msi_desc2 int_desc; +} __packed; + +struct pci_delete_interrupt { + struct pci_message message_type; + union win_slot_encoding wslot; + struct tran_int_desc int_desc; +} __packed; + +struct pci_dev_incoming { + struct pci_incoming_message incoming; + union win_slot_encoding wslot; +} __packed; + +struct pci_eject_response { + struct pci_message message_type; + union win_slot_encoding wslot; + u32 status; +} __packed; + +static int pci_ring_size = (4 * PAGE_SIZE); + +/* + * Definitions or interrupt steering hypercall. + */ +#define HV_PARTITION_ID_SELF ((u64)-1) +#define HVCALL_RETARGET_INTERRUPT 0x7e + +struct hv_interrupt_entry { + u32 source; /* 1 for MSI(-X) */ + u32 reserved1; + u32 address; + u32 data; +}; + +#define HV_VP_SET_BANK_COUNT_MAX 5 /* current implementation limit */ + +struct hv_vp_set { + u64 format; /* 0 (HvGenericSetSparse4k) */ + u64 valid_banks; + u64 masks[HV_VP_SET_BANK_COUNT_MAX]; +}; + +/* + * flags for hv_device_interrupt_target.flags + */ +#define HV_DEVICE_INTERRUPT_TARGET_MULTICAST 1 +#define HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET 2 + +struct hv_device_interrupt_target { + u32 vector; + u32 flags; + union { + u64 vp_mask; + struct hv_vp_set vp_set; + }; +}; + +struct retarget_msi_interrupt { + u64 partition_id; /* use "self" */ + u64 device_id; + struct hv_interrupt_entry int_entry; + u64 reserved2; + struct hv_device_interrupt_target int_target; +} __packed; + +/* + * Driver specific state. + */ + +enum hv_pcibus_state { + hv_pcibus_init = 0, + hv_pcibus_probed, + hv_pcibus_installed, + hv_pcibus_removed, + hv_pcibus_maximum +}; + +struct hv_pcibus_device { + struct pci_sysdata sysdata; + enum hv_pcibus_state state; + refcount_t remove_lock; + struct hv_device *hdev; + resource_size_t low_mmio_space; + resource_size_t high_mmio_space; + struct resource *mem_config; + struct resource *low_mmio_res; + struct resource *high_mmio_res; + struct completion *survey_event; + struct completion remove_event; + struct pci_bus *pci_bus; + spinlock_t config_lock; /* Avoid two threads writing index page */ + spinlock_t device_list_lock; /* Protect lists below */ + void __iomem *cfg_addr; + + struct list_head resources_for_children; + + struct list_head children; + struct list_head dr_list; + + struct msi_domain_info msi_info; + struct msi_controller msi_chip; + struct irq_domain *irq_domain; + + /* hypercall arg, must not cross page boundary */ + struct retarget_msi_interrupt retarget_msi_interrupt_params; + + spinlock_t retarget_msi_interrupt_lock; + + struct workqueue_struct *wq; +}; + +/* + * Tracks "Device Relations" messages from the host, which must be both + * processed in order and deferred so that they don't run in the context + * of the incoming packet callback. + */ +struct hv_dr_work { + struct work_struct wrk; + struct hv_pcibus_device *bus; +}; + +struct hv_dr_state { + struct list_head list_entry; + u32 device_count; + struct pci_function_description func[0]; +}; + +enum hv_pcichild_state { + hv_pcichild_init = 0, + hv_pcichild_requirements, + hv_pcichild_resourced, + hv_pcichild_ejecting, + hv_pcichild_maximum +}; + +struct hv_pci_dev { + /* List protected by pci_rescan_remove_lock */ + struct list_head list_entry; + refcount_t refs; + enum hv_pcichild_state state; + struct pci_function_description desc; + bool reported_missing; + struct hv_pcibus_device *hbus; + struct work_struct wrk; + + /* + * What would be observed if one wrote 0xFFFFFFFF to a BAR and then + * read it back, for each of the BAR offsets within config space. + */ + u32 probed_bar[6]; +}; + +struct hv_pci_compl { + struct completion host_event; + s32 completion_status; +}; + +static void hv_pci_onchannelcallback(void *context); + +/** + * hv_pci_generic_compl() - Invoked for a completion packet + * @context: Set up by the sender of the packet. + * @resp: The response packet + * @resp_packet_size: Size in bytes of the packet + * + * This function is used to trigger an event and report status + * for any message for which the completion packet contains a + * status and nothing else. + */ +static void hv_pci_generic_compl(void *context, struct pci_response *resp, + int resp_packet_size) +{ + struct hv_pci_compl *comp_pkt = context; + + if (resp_packet_size >= offsetofend(struct pci_response, status)) + comp_pkt->completion_status = resp->status; + else + comp_pkt->completion_status = -1; + + complete(&comp_pkt->host_event); +} + +static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus, + u32 wslot); + +static void get_pcichild(struct hv_pci_dev *hpdev) +{ + refcount_inc(&hpdev->refs); +} + +static void put_pcichild(struct hv_pci_dev *hpdev) +{ + if (refcount_dec_and_test(&hpdev->refs)) + kfree(hpdev); +} + +static void get_hvpcibus(struct hv_pcibus_device *hv_pcibus); +static void put_hvpcibus(struct hv_pcibus_device *hv_pcibus); + +/* + * There is no good way to get notified from vmbus_onoffer_rescind(), + * so let's use polling here, since this is not a hot path. + */ +static int wait_for_response(struct hv_device *hdev, + struct completion *comp) +{ + while (true) { + if (hdev->channel->rescind) { + dev_warn_once(&hdev->device, "The device is gone.\n"); + return -ENODEV; + } + + if (wait_for_completion_timeout(comp, HZ / 10)) + break; + } + + return 0; +} + +/** + * devfn_to_wslot() - Convert from Linux PCI slot to Windows + * @devfn: The Linux representation of PCI slot + * + * Windows uses a slightly different representation of PCI slot. + * + * Return: The Windows representation + */ +static u32 devfn_to_wslot(int devfn) +{ + union win_slot_encoding wslot; + + wslot.slot = 0; + wslot.bits.dev = PCI_SLOT(devfn); + wslot.bits.func = PCI_FUNC(devfn); + + return wslot.slot; +} + +/** + * wslot_to_devfn() - Convert from Windows PCI slot to Linux + * @wslot: The Windows representation of PCI slot + * + * Windows uses a slightly different representation of PCI slot. + * + * Return: The Linux representation + */ +static int wslot_to_devfn(u32 wslot) +{ + union win_slot_encoding slot_no; + + slot_no.slot = wslot; + return PCI_DEVFN(slot_no.bits.dev, slot_no.bits.func); +} + +/* + * PCI Configuration Space for these root PCI buses is implemented as a pair + * of pages in memory-mapped I/O space. Writing to the first page chooses + * the PCI function being written or read. Once the first page has been + * written to, the following page maps in the entire configuration space of + * the function. + */ + +/** + * _hv_pcifront_read_config() - Internal PCI config read + * @hpdev: The PCI driver's representation of the device + * @where: Offset within config space + * @size: Size of the transfer + * @val: Pointer to the buffer receiving the data + */ +static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where, + int size, u32 *val) +{ + unsigned long flags; + void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where; + + /* + * If the attempt is to read the IDs or the ROM BAR, simulate that. + */ + if (where + size <= PCI_COMMAND) { + memcpy(val, ((u8 *)&hpdev->desc.v_id) + where, size); + } else if (where >= PCI_CLASS_REVISION && where + size <= + PCI_CACHE_LINE_SIZE) { + memcpy(val, ((u8 *)&hpdev->desc.rev) + where - + PCI_CLASS_REVISION, size); + } else if (where >= PCI_SUBSYSTEM_VENDOR_ID && where + size <= + PCI_ROM_ADDRESS) { + memcpy(val, (u8 *)&hpdev->desc.subsystem_id + where - + PCI_SUBSYSTEM_VENDOR_ID, size); + } else if (where >= PCI_ROM_ADDRESS && where + size <= + PCI_CAPABILITY_LIST) { + /* ROM BARs are unimplemented */ + *val = 0; + } else if (where >= PCI_INTERRUPT_LINE && where + size <= + PCI_INTERRUPT_PIN) { + /* + * Interrupt Line and Interrupt PIN are hard-wired to zero + * because this front-end only supports message-signaled + * interrupts. + */ + *val = 0; + } else if (where + size <= CFG_PAGE_SIZE) { + spin_lock_irqsave(&hpdev->hbus->config_lock, flags); + /* Choose the function to be read. (See comment above) */ + writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr); + /* Make sure the function was chosen before we start reading. */ + mb(); + /* Read from that function's config space. */ + switch (size) { + case 1: + *val = readb(addr); + break; + case 2: + *val = readw(addr); + break; + default: + *val = readl(addr); + break; + } + /* + * Make sure the read was done before we release the spinlock + * allowing consecutive reads/writes. + */ + mb(); + spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags); + } else { + dev_err(&hpdev->hbus->hdev->device, + "Attempt to read beyond a function's config space.\n"); + } +} + +static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev) +{ + u16 ret; + unsigned long flags; + void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + + PCI_VENDOR_ID; + + spin_lock_irqsave(&hpdev->hbus->config_lock, flags); + + /* Choose the function to be read. (See comment above) */ + writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr); + /* Make sure the function was chosen before we start reading. */ + mb(); + /* Read from that function's config space. */ + ret = readw(addr); + /* + * mb() is not required here, because the spin_unlock_irqrestore() + * is a barrier. + */ + + spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags); + + return ret; +} + +/** + * _hv_pcifront_write_config() - Internal PCI config write + * @hpdev: The PCI driver's representation of the device + * @where: Offset within config space + * @size: Size of the transfer + * @val: The data being transferred + */ +static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where, + int size, u32 val) +{ + unsigned long flags; + void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where; + + if (where >= PCI_SUBSYSTEM_VENDOR_ID && + where + size <= PCI_CAPABILITY_LIST) { + /* SSIDs and ROM BARs are read-only */ + } else if (where >= PCI_COMMAND && where + size <= CFG_PAGE_SIZE) { + spin_lock_irqsave(&hpdev->hbus->config_lock, flags); + /* Choose the function to be written. (See comment above) */ + writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr); + /* Make sure the function was chosen before we start writing. */ + wmb(); + /* Write to that function's config space. */ + switch (size) { + case 1: + writeb(val, addr); + break; + case 2: + writew(val, addr); + break; + default: + writel(val, addr); + break; + } + /* + * Make sure the write was done before we release the spinlock + * allowing consecutive reads/writes. + */ + mb(); + spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags); + } else { + dev_err(&hpdev->hbus->hdev->device, + "Attempt to write beyond a function's config space.\n"); + } +} + +/** + * hv_pcifront_read_config() - Read configuration space + * @bus: PCI Bus structure + * @devfn: Device/function + * @where: Offset from base + * @size: Byte/word/dword + * @val: Value to be read + * + * Return: PCIBIOS_SUCCESSFUL on success + * PCIBIOS_DEVICE_NOT_FOUND on failure + */ +static int hv_pcifront_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct hv_pcibus_device *hbus = + container_of(bus->sysdata, struct hv_pcibus_device, sysdata); + struct hv_pci_dev *hpdev; + + hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(devfn)); + if (!hpdev) + return PCIBIOS_DEVICE_NOT_FOUND; + + _hv_pcifront_read_config(hpdev, where, size, val); + + put_pcichild(hpdev); + return PCIBIOS_SUCCESSFUL; +} + +/** + * hv_pcifront_write_config() - Write configuration space + * @bus: PCI Bus structure + * @devfn: Device/function + * @where: Offset from base + * @size: Byte/word/dword + * @val: Value to be written to device + * + * Return: PCIBIOS_SUCCESSFUL on success + * PCIBIOS_DEVICE_NOT_FOUND on failure + */ +static int hv_pcifront_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct hv_pcibus_device *hbus = + container_of(bus->sysdata, struct hv_pcibus_device, sysdata); + struct hv_pci_dev *hpdev; + + hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(devfn)); + if (!hpdev) + return PCIBIOS_DEVICE_NOT_FOUND; + + _hv_pcifront_write_config(hpdev, where, size, val); + + put_pcichild(hpdev); + return PCIBIOS_SUCCESSFUL; +} + +/* PCIe operations */ +static struct pci_ops hv_pcifront_ops = { + .read = hv_pcifront_read_config, + .write = hv_pcifront_write_config, +}; + +/* Interrupt management hooks */ +static void hv_int_desc_free(struct hv_pci_dev *hpdev, + struct tran_int_desc *int_desc) +{ + struct pci_delete_interrupt *int_pkt; + struct { + struct pci_packet pkt; + u8 buffer[sizeof(struct pci_delete_interrupt)]; + } ctxt; + + memset(&ctxt, 0, sizeof(ctxt)); + int_pkt = (struct pci_delete_interrupt *)&ctxt.pkt.message; + int_pkt->message_type.type = + PCI_DELETE_INTERRUPT_MESSAGE; + int_pkt->wslot.slot = hpdev->desc.win_slot.slot; + int_pkt->int_desc = *int_desc; + vmbus_sendpacket(hpdev->hbus->hdev->channel, int_pkt, sizeof(*int_pkt), + (unsigned long)&ctxt.pkt, VM_PKT_DATA_INBAND, 0); + kfree(int_desc); +} + +/** + * hv_msi_free() - Free the MSI. + * @domain: The interrupt domain pointer + * @info: Extra MSI-related context + * @irq: Identifies the IRQ. + * + * The Hyper-V parent partition and hypervisor are tracking the + * messages that are in use, keeping the interrupt redirection + * table up to date. This callback sends a message that frees + * the IRT entry and related tracking nonsense. + */ +static void hv_msi_free(struct irq_domain *domain, struct msi_domain_info *info, + unsigned int irq) +{ + struct hv_pcibus_device *hbus; + struct hv_pci_dev *hpdev; + struct pci_dev *pdev; + struct tran_int_desc *int_desc; + struct irq_data *irq_data = irq_domain_get_irq_data(domain, irq); + struct msi_desc *msi = irq_data_get_msi_desc(irq_data); + + pdev = msi_desc_to_pci_dev(msi); + hbus = info->data; + int_desc = irq_data_get_irq_chip_data(irq_data); + if (!int_desc) + return; + + irq_data->chip_data = NULL; + hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn)); + if (!hpdev) { + kfree(int_desc); + return; + } + + hv_int_desc_free(hpdev, int_desc); + put_pcichild(hpdev); +} + +static int hv_set_affinity(struct irq_data *data, const struct cpumask *dest, + bool force) +{ + struct irq_data *parent = data->parent_data; + + return parent->chip->irq_set_affinity(parent, dest, force); +} + +static void hv_irq_mask(struct irq_data *data) +{ + pci_msi_mask_irq(data); +} + +/** + * hv_irq_unmask() - "Unmask" the IRQ by setting its current + * affinity. + * @data: Describes the IRQ + * + * Build new a destination for the MSI and make a hypercall to + * update the Interrupt Redirection Table. "Device Logical ID" + * is built out of this PCI bus's instance GUID and the function + * number of the device. + */ +static void hv_irq_unmask(struct irq_data *data) +{ + struct msi_desc *msi_desc = irq_data_get_msi_desc(data); + struct irq_cfg *cfg = irqd_cfg(data); + struct retarget_msi_interrupt *params; + struct hv_pcibus_device *hbus; + struct cpumask *dest; + struct pci_bus *pbus; + struct pci_dev *pdev; + unsigned long flags; + u32 var_size = 0; + int cpu_vmbus; + int cpu; + u64 res; + + dest = irq_data_get_effective_affinity_mask(data); + pdev = msi_desc_to_pci_dev(msi_desc); + pbus = pdev->bus; + hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); + + spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags); + + params = &hbus->retarget_msi_interrupt_params; + memset(params, 0, sizeof(*params)); + params->partition_id = HV_PARTITION_ID_SELF; + params->int_entry.source = 1; /* MSI(-X) */ + params->int_entry.address = msi_desc->msg.address_lo; + params->int_entry.data = msi_desc->msg.data; + params->device_id = (hbus->hdev->dev_instance.b[5] << 24) | + (hbus->hdev->dev_instance.b[4] << 16) | + (hbus->hdev->dev_instance.b[7] << 8) | + (hbus->hdev->dev_instance.b[6] & 0xf8) | + PCI_FUNC(pdev->devfn); + params->int_target.vector = cfg->vector; + + /* + * Honoring apic->irq_delivery_mode set to dest_Fixed by + * setting the HV_DEVICE_INTERRUPT_TARGET_MULTICAST flag results in a + * spurious interrupt storm. Not doing so does not seem to have a + * negative effect (yet?). + */ + + if (pci_protocol_version >= PCI_PROTOCOL_VERSION_1_2) { + /* + * PCI_PROTOCOL_VERSION_1_2 supports the VP_SET version of the + * HVCALL_RETARGET_INTERRUPT hypercall, which also coincides + * with >64 VP support. + * ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED + * is not sufficient for this hypercall. + */ + params->int_target.flags |= + HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET; + params->int_target.vp_set.valid_banks = + (1ull << HV_VP_SET_BANK_COUNT_MAX) - 1; + + /* + * var-sized hypercall, var-size starts after vp_mask (thus + * vp_set.format does not count, but vp_set.valid_banks does). + */ + var_size = 1 + HV_VP_SET_BANK_COUNT_MAX; + + for_each_cpu_and(cpu, dest, cpu_online_mask) { + cpu_vmbus = hv_cpu_number_to_vp_number(cpu); + + if (cpu_vmbus >= HV_VP_SET_BANK_COUNT_MAX * 64) { + dev_err(&hbus->hdev->device, + "too high CPU %d", cpu_vmbus); + res = 1; + goto exit_unlock; + } + + params->int_target.vp_set.masks[cpu_vmbus / 64] |= + (1ULL << (cpu_vmbus & 63)); + } + } else { + for_each_cpu_and(cpu, dest, cpu_online_mask) { + params->int_target.vp_mask |= + (1ULL << hv_cpu_number_to_vp_number(cpu)); + } + } + + res = hv_do_hypercall(HVCALL_RETARGET_INTERRUPT | (var_size << 17), + params, NULL); + +exit_unlock: + spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags); + + if (res) { + dev_err(&hbus->hdev->device, + "%s() failed: %#llx", __func__, res); + return; + } + + pci_msi_unmask_irq(data); +} + +struct compose_comp_ctxt { + struct hv_pci_compl comp_pkt; + struct tran_int_desc int_desc; +}; + +static void hv_pci_compose_compl(void *context, struct pci_response *resp, + int resp_packet_size) +{ + struct compose_comp_ctxt *comp_pkt = context; + struct pci_create_int_response *int_resp = + (struct pci_create_int_response *)resp; + + comp_pkt->comp_pkt.completion_status = resp->status; + comp_pkt->int_desc = int_resp->int_desc; + complete(&comp_pkt->comp_pkt.host_event); +} + +static u32 hv_compose_msi_req_v1( + struct pci_create_interrupt *int_pkt, struct cpumask *affinity, + u32 slot, u8 vector) +{ + int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE; + int_pkt->wslot.slot = slot; + int_pkt->int_desc.vector = vector; + int_pkt->int_desc.vector_count = 1; + int_pkt->int_desc.delivery_mode = dest_Fixed; + + /* + * Create MSI w/ dummy vCPU set, overwritten by subsequent retarget in + * hv_irq_unmask(). + */ + int_pkt->int_desc.cpu_mask = CPU_AFFINITY_ALL; + + return sizeof(*int_pkt); +} + +static u32 hv_compose_msi_req_v2( + struct pci_create_interrupt2 *int_pkt, struct cpumask *affinity, + u32 slot, u8 vector) +{ + int cpu; + + int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE2; + int_pkt->wslot.slot = slot; + int_pkt->int_desc.vector = vector; + int_pkt->int_desc.vector_count = 1; + int_pkt->int_desc.delivery_mode = dest_Fixed; + + /* + * Create MSI w/ dummy vCPU set targeting just one vCPU, overwritten + * by subsequent retarget in hv_irq_unmask(). + */ + cpu = cpumask_first_and(affinity, cpu_online_mask); + int_pkt->int_desc.processor_array[0] = + hv_cpu_number_to_vp_number(cpu); + int_pkt->int_desc.processor_count = 1; + + return sizeof(*int_pkt); +} + +/** + * hv_compose_msi_msg() - Supplies a valid MSI address/data + * @data: Everything about this MSI + * @msg: Buffer that is filled in by this function + * + * This function unpacks the IRQ looking for target CPU set, IDT + * vector and mode and sends a message to the parent partition + * asking for a mapping for that tuple in this partition. The + * response supplies a data value and address to which that data + * should be written to trigger that interrupt. + */ +static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct irq_cfg *cfg = irqd_cfg(data); + struct hv_pcibus_device *hbus; + struct hv_pci_dev *hpdev; + struct pci_bus *pbus; + struct pci_dev *pdev; + struct cpumask *dest; + struct compose_comp_ctxt comp; + struct tran_int_desc *int_desc; + struct { + struct pci_packet pci_pkt; + union { + struct pci_create_interrupt v1; + struct pci_create_interrupt2 v2; + } int_pkts; + } __packed ctxt; + + u32 size; + int ret; + + pdev = msi_desc_to_pci_dev(irq_data_get_msi_desc(data)); + dest = irq_data_get_effective_affinity_mask(data); + pbus = pdev->bus; + hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); + hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn)); + if (!hpdev) + goto return_null_message; + + /* Free any previous message that might have already been composed. */ + if (data->chip_data) { + int_desc = data->chip_data; + data->chip_data = NULL; + hv_int_desc_free(hpdev, int_desc); + } + + int_desc = kzalloc(sizeof(*int_desc), GFP_ATOMIC); + if (!int_desc) + goto drop_reference; + + memset(&ctxt, 0, sizeof(ctxt)); + init_completion(&comp.comp_pkt.host_event); + ctxt.pci_pkt.completion_func = hv_pci_compose_compl; + ctxt.pci_pkt.compl_ctxt = ∁ + + switch (pci_protocol_version) { + case PCI_PROTOCOL_VERSION_1_1: + size = hv_compose_msi_req_v1(&ctxt.int_pkts.v1, + dest, + hpdev->desc.win_slot.slot, + cfg->vector); + break; + + case PCI_PROTOCOL_VERSION_1_2: + size = hv_compose_msi_req_v2(&ctxt.int_pkts.v2, + dest, + hpdev->desc.win_slot.slot, + cfg->vector); + break; + + default: + /* As we only negotiate protocol versions known to this driver, + * this path should never hit. However, this is it not a hot + * path so we print a message to aid future updates. + */ + dev_err(&hbus->hdev->device, + "Unexpected vPCI protocol, update driver."); + goto free_int_desc; + } + + ret = vmbus_sendpacket(hpdev->hbus->hdev->channel, &ctxt.int_pkts, + size, (unsigned long)&ctxt.pci_pkt, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret) { + dev_err(&hbus->hdev->device, + "Sending request for interrupt failed: 0x%x", + comp.comp_pkt.completion_status); + goto free_int_desc; + } + + /* + * Since this function is called with IRQ locks held, can't + * do normal wait for completion; instead poll. + */ + while (!try_wait_for_completion(&comp.comp_pkt.host_event)) { + /* 0xFFFF means an invalid PCI VENDOR ID. */ + if (hv_pcifront_get_vendor_id(hpdev) == 0xFFFF) { + dev_err_once(&hbus->hdev->device, + "the device has gone\n"); + goto free_int_desc; + } + + /* + * When the higher level interrupt code calls us with + * interrupt disabled, we must poll the channel by calling + * the channel callback directly when channel->target_cpu is + * the current CPU. When the higher level interrupt code + * calls us with interrupt enabled, let's add the + * local_bh_disable()/enable() to avoid race. + */ + local_bh_disable(); + + if (hbus->hdev->channel->target_cpu == smp_processor_id()) + hv_pci_onchannelcallback(hbus); + + local_bh_enable(); + + if (hpdev->state == hv_pcichild_ejecting) { + dev_err_once(&hbus->hdev->device, + "the device is being ejected\n"); + goto free_int_desc; + } + + udelay(100); + } + + if (comp.comp_pkt.completion_status < 0) { + dev_err(&hbus->hdev->device, + "Request for interrupt failed: 0x%x", + comp.comp_pkt.completion_status); + goto free_int_desc; + } + + /* + * Record the assignment so that this can be unwound later. Using + * irq_set_chip_data() here would be appropriate, but the lock it takes + * is already held. + */ + *int_desc = comp.int_desc; + data->chip_data = int_desc; + + /* Pass up the result. */ + msg->address_hi = comp.int_desc.address >> 32; + msg->address_lo = comp.int_desc.address & 0xffffffff; + msg->data = comp.int_desc.data; + + put_pcichild(hpdev); + return; + +free_int_desc: + kfree(int_desc); +drop_reference: + put_pcichild(hpdev); +return_null_message: + msg->address_hi = 0; + msg->address_lo = 0; + msg->data = 0; +} + +/* HW Interrupt Chip Descriptor */ +static struct irq_chip hv_msi_irq_chip = { + .name = "Hyper-V PCIe MSI", + .irq_compose_msi_msg = hv_compose_msi_msg, + .irq_set_affinity = hv_set_affinity, + .irq_ack = irq_chip_ack_parent, + .irq_mask = hv_irq_mask, + .irq_unmask = hv_irq_unmask, +}; + +static irq_hw_number_t hv_msi_domain_ops_get_hwirq(struct msi_domain_info *info, + msi_alloc_info_t *arg) +{ + return arg->msi_hwirq; +} + +static struct msi_domain_ops hv_msi_ops = { + .get_hwirq = hv_msi_domain_ops_get_hwirq, + .msi_prepare = pci_msi_prepare, + .set_desc = pci_msi_set_desc, + .msi_free = hv_msi_free, +}; + +/** + * hv_pcie_init_irq_domain() - Initialize IRQ domain + * @hbus: The root PCI bus + * + * This function creates an IRQ domain which will be used for + * interrupts from devices that have been passed through. These + * devices only support MSI and MSI-X, not line-based interrupts + * or simulations of line-based interrupts through PCIe's + * fabric-layer messages. Because interrupts are remapped, we + * can support multi-message MSI here. + * + * Return: '0' on success and error value on failure + */ +static int hv_pcie_init_irq_domain(struct hv_pcibus_device *hbus) +{ + hbus->msi_info.chip = &hv_msi_irq_chip; + hbus->msi_info.ops = &hv_msi_ops; + hbus->msi_info.flags = (MSI_FLAG_USE_DEF_DOM_OPS | + MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_MULTI_PCI_MSI | + MSI_FLAG_PCI_MSIX); + hbus->msi_info.handler = handle_edge_irq; + hbus->msi_info.handler_name = "edge"; + hbus->msi_info.data = hbus; + hbus->irq_domain = pci_msi_create_irq_domain(hbus->sysdata.fwnode, + &hbus->msi_info, + x86_vector_domain); + if (!hbus->irq_domain) { + dev_err(&hbus->hdev->device, + "Failed to build an MSI IRQ domain\n"); + return -ENODEV; + } + + return 0; +} + +/** + * get_bar_size() - Get the address space consumed by a BAR + * @bar_val: Value that a BAR returned after -1 was written + * to it. + * + * This function returns the size of the BAR, rounded up to 1 + * page. It has to be rounded up because the hypervisor's page + * table entry that maps the BAR into the VM can't specify an + * offset within a page. The invariant is that the hypervisor + * must place any BARs of smaller than page length at the + * beginning of a page. + * + * Return: Size in bytes of the consumed MMIO space. + */ +static u64 get_bar_size(u64 bar_val) +{ + return round_up((1 + ~(bar_val & PCI_BASE_ADDRESS_MEM_MASK)), + PAGE_SIZE); +} + +/** + * survey_child_resources() - Total all MMIO requirements + * @hbus: Root PCI bus, as understood by this driver + */ +static void survey_child_resources(struct hv_pcibus_device *hbus) +{ + struct hv_pci_dev *hpdev; + resource_size_t bar_size = 0; + unsigned long flags; + struct completion *event; + u64 bar_val; + int i; + + /* If nobody is waiting on the answer, don't compute it. */ + event = xchg(&hbus->survey_event, NULL); + if (!event) + return; + + /* If the answer has already been computed, go with it. */ + if (hbus->low_mmio_space || hbus->high_mmio_space) { + complete(event); + return; + } + + spin_lock_irqsave(&hbus->device_list_lock, flags); + + /* + * Due to an interesting quirk of the PCI spec, all memory regions + * for a child device are a power of 2 in size and aligned in memory, + * so it's sufficient to just add them up without tracking alignment. + */ + list_for_each_entry(hpdev, &hbus->children, list_entry) { + for (i = 0; i < 6; i++) { + if (hpdev->probed_bar[i] & PCI_BASE_ADDRESS_SPACE_IO) + dev_err(&hbus->hdev->device, + "There's an I/O BAR in this list!\n"); + + if (hpdev->probed_bar[i] != 0) { + /* + * A probed BAR has all the upper bits set that + * can be changed. + */ + + bar_val = hpdev->probed_bar[i]; + if (bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64) + bar_val |= + ((u64)hpdev->probed_bar[++i] << 32); + else + bar_val |= 0xffffffff00000000ULL; + + bar_size = get_bar_size(bar_val); + + if (bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64) + hbus->high_mmio_space += bar_size; + else + hbus->low_mmio_space += bar_size; + } + } + } + + spin_unlock_irqrestore(&hbus->device_list_lock, flags); + complete(event); +} + +/** + * prepopulate_bars() - Fill in BARs with defaults + * @hbus: Root PCI bus, as understood by this driver + * + * The core PCI driver code seems much, much happier if the BARs + * for a device have values upon first scan. So fill them in. + * The algorithm below works down from large sizes to small, + * attempting to pack the assignments optimally. The assumption, + * enforced in other parts of the code, is that the beginning of + * the memory-mapped I/O space will be aligned on the largest + * BAR size. + */ +static void prepopulate_bars(struct hv_pcibus_device *hbus) +{ + resource_size_t high_size = 0; + resource_size_t low_size = 0; + resource_size_t high_base = 0; + resource_size_t low_base = 0; + resource_size_t bar_size; + struct hv_pci_dev *hpdev; + unsigned long flags; + u64 bar_val; + u32 command; + bool high; + int i; + + if (hbus->low_mmio_space) { + low_size = 1ULL << (63 - __builtin_clzll(hbus->low_mmio_space)); + low_base = hbus->low_mmio_res->start; + } + + if (hbus->high_mmio_space) { + high_size = 1ULL << + (63 - __builtin_clzll(hbus->high_mmio_space)); + high_base = hbus->high_mmio_res->start; + } + + spin_lock_irqsave(&hbus->device_list_lock, flags); + + /* Pick addresses for the BARs. */ + do { + list_for_each_entry(hpdev, &hbus->children, list_entry) { + for (i = 0; i < 6; i++) { + bar_val = hpdev->probed_bar[i]; + if (bar_val == 0) + continue; + high = bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64; + if (high) { + bar_val |= + ((u64)hpdev->probed_bar[i + 1] + << 32); + } else { + bar_val |= 0xffffffffULL << 32; + } + bar_size = get_bar_size(bar_val); + if (high) { + if (high_size != bar_size) { + i++; + continue; + } + _hv_pcifront_write_config(hpdev, + PCI_BASE_ADDRESS_0 + (4 * i), + 4, + (u32)(high_base & 0xffffff00)); + i++; + _hv_pcifront_write_config(hpdev, + PCI_BASE_ADDRESS_0 + (4 * i), + 4, (u32)(high_base >> 32)); + high_base += bar_size; + } else { + if (low_size != bar_size) + continue; + _hv_pcifront_write_config(hpdev, + PCI_BASE_ADDRESS_0 + (4 * i), + 4, + (u32)(low_base & 0xffffff00)); + low_base += bar_size; + } + } + if (high_size <= 1 && low_size <= 1) { + /* Set the memory enable bit. */ + _hv_pcifront_read_config(hpdev, PCI_COMMAND, 2, + &command); + command |= PCI_COMMAND_MEMORY; + _hv_pcifront_write_config(hpdev, PCI_COMMAND, 2, + command); + break; + } + } + + high_size >>= 1; + low_size >>= 1; + } while (high_size || low_size); + + spin_unlock_irqrestore(&hbus->device_list_lock, flags); +} + +/** + * create_root_hv_pci_bus() - Expose a new root PCI bus + * @hbus: Root PCI bus, as understood by this driver + * + * Return: 0 on success, -errno on failure + */ +static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus) +{ + /* Register the device */ + hbus->pci_bus = pci_create_root_bus(&hbus->hdev->device, + 0, /* bus number is always zero */ + &hv_pcifront_ops, + &hbus->sysdata, + &hbus->resources_for_children); + if (!hbus->pci_bus) + return -ENODEV; + + hbus->pci_bus->msi = &hbus->msi_chip; + hbus->pci_bus->msi->dev = &hbus->hdev->device; + + pci_lock_rescan_remove(); + pci_scan_child_bus(hbus->pci_bus); + pci_bus_assign_resources(hbus->pci_bus); + pci_bus_add_devices(hbus->pci_bus); + pci_unlock_rescan_remove(); + hbus->state = hv_pcibus_installed; + return 0; +} + +struct q_res_req_compl { + struct completion host_event; + struct hv_pci_dev *hpdev; +}; + +/** + * q_resource_requirements() - Query Resource Requirements + * @context: The completion context. + * @resp: The response that came from the host. + * @resp_packet_size: The size in bytes of resp. + * + * This function is invoked on completion of a Query Resource + * Requirements packet. + */ +static void q_resource_requirements(void *context, struct pci_response *resp, + int resp_packet_size) +{ + struct q_res_req_compl *completion = context; + struct pci_q_res_req_response *q_res_req = + (struct pci_q_res_req_response *)resp; + int i; + + if (resp->status < 0) { + dev_err(&completion->hpdev->hbus->hdev->device, + "query resource requirements failed: %x\n", + resp->status); + } else { + for (i = 0; i < 6; i++) { + completion->hpdev->probed_bar[i] = + q_res_req->probed_bar[i]; + } + } + + complete(&completion->host_event); +} + +/** + * new_pcichild_device() - Create a new child device + * @hbus: The internal struct tracking this root PCI bus. + * @desc: The information supplied so far from the host + * about the device. + * + * This function creates the tracking structure for a new child + * device and kicks off the process of figuring out what it is. + * + * Return: Pointer to the new tracking struct + */ +static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus, + struct pci_function_description *desc) +{ + struct hv_pci_dev *hpdev; + struct pci_child_message *res_req; + struct q_res_req_compl comp_pkt; + struct { + struct pci_packet init_packet; + u8 buffer[sizeof(struct pci_child_message)]; + } pkt; + unsigned long flags; + int ret; + + hpdev = kzalloc(sizeof(*hpdev), GFP_ATOMIC); + if (!hpdev) + return NULL; + + hpdev->hbus = hbus; + + memset(&pkt, 0, sizeof(pkt)); + init_completion(&comp_pkt.host_event); + comp_pkt.hpdev = hpdev; + pkt.init_packet.compl_ctxt = &comp_pkt; + pkt.init_packet.completion_func = q_resource_requirements; + res_req = (struct pci_child_message *)&pkt.init_packet.message; + res_req->message_type.type = PCI_QUERY_RESOURCE_REQUIREMENTS; + res_req->wslot.slot = desc->win_slot.slot; + + ret = vmbus_sendpacket(hbus->hdev->channel, res_req, + sizeof(struct pci_child_message), + (unsigned long)&pkt.init_packet, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret) + goto error; + + if (wait_for_response(hbus->hdev, &comp_pkt.host_event)) + goto error; + + hpdev->desc = *desc; + refcount_set(&hpdev->refs, 1); + get_pcichild(hpdev); + spin_lock_irqsave(&hbus->device_list_lock, flags); + + list_add_tail(&hpdev->list_entry, &hbus->children); + spin_unlock_irqrestore(&hbus->device_list_lock, flags); + return hpdev; + +error: + kfree(hpdev); + return NULL; +} + +/** + * get_pcichild_wslot() - Find device from slot + * @hbus: Root PCI bus, as understood by this driver + * @wslot: Location on the bus + * + * This function looks up a PCI device and returns the internal + * representation of it. It acquires a reference on it, so that + * the device won't be deleted while somebody is using it. The + * caller is responsible for calling put_pcichild() to release + * this reference. + * + * Return: Internal representation of a PCI device + */ +static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus, + u32 wslot) +{ + unsigned long flags; + struct hv_pci_dev *iter, *hpdev = NULL; + + spin_lock_irqsave(&hbus->device_list_lock, flags); + list_for_each_entry(iter, &hbus->children, list_entry) { + if (iter->desc.win_slot.slot == wslot) { + hpdev = iter; + get_pcichild(hpdev); + break; + } + } + spin_unlock_irqrestore(&hbus->device_list_lock, flags); + + return hpdev; +} + +/** + * pci_devices_present_work() - Handle new list of child devices + * @work: Work struct embedded in struct hv_dr_work + * + * "Bus Relations" is the Windows term for "children of this + * bus." The terminology is preserved here for people trying to + * debug the interaction between Hyper-V and Linux. This + * function is called when the parent partition reports a list + * of functions that should be observed under this PCI Express + * port (bus). + * + * This function updates the list, and must tolerate being + * called multiple times with the same information. The typical + * number of child devices is one, with very atypical cases + * involving three or four, so the algorithms used here can be + * simple and inefficient. + * + * It must also treat the omission of a previously observed device as + * notification that the device no longer exists. + * + * Note that this function is serialized with hv_eject_device_work(), + * because both are pushed to the ordered workqueue hbus->wq. + */ +static void pci_devices_present_work(struct work_struct *work) +{ + u32 child_no; + bool found; + struct pci_function_description *new_desc; + struct hv_pci_dev *hpdev; + struct hv_pcibus_device *hbus; + struct list_head removed; + struct hv_dr_work *dr_wrk; + struct hv_dr_state *dr = NULL; + unsigned long flags; + + dr_wrk = container_of(work, struct hv_dr_work, wrk); + hbus = dr_wrk->bus; + kfree(dr_wrk); + + INIT_LIST_HEAD(&removed); + + /* Pull this off the queue and process it if it was the last one. */ + spin_lock_irqsave(&hbus->device_list_lock, flags); + while (!list_empty(&hbus->dr_list)) { + dr = list_first_entry(&hbus->dr_list, struct hv_dr_state, + list_entry); + list_del(&dr->list_entry); + + /* Throw this away if the list still has stuff in it. */ + if (!list_empty(&hbus->dr_list)) { + kfree(dr); + continue; + } + } + spin_unlock_irqrestore(&hbus->device_list_lock, flags); + + if (!dr) { + put_hvpcibus(hbus); + return; + } + + /* First, mark all existing children as reported missing. */ + spin_lock_irqsave(&hbus->device_list_lock, flags); + list_for_each_entry(hpdev, &hbus->children, list_entry) { + hpdev->reported_missing = true; + } + spin_unlock_irqrestore(&hbus->device_list_lock, flags); + + /* Next, add back any reported devices. */ + for (child_no = 0; child_no < dr->device_count; child_no++) { + found = false; + new_desc = &dr->func[child_no]; + + spin_lock_irqsave(&hbus->device_list_lock, flags); + list_for_each_entry(hpdev, &hbus->children, list_entry) { + if ((hpdev->desc.win_slot.slot == new_desc->win_slot.slot) && + (hpdev->desc.v_id == new_desc->v_id) && + (hpdev->desc.d_id == new_desc->d_id) && + (hpdev->desc.ser == new_desc->ser)) { + hpdev->reported_missing = false; + found = true; + } + } + spin_unlock_irqrestore(&hbus->device_list_lock, flags); + + if (!found) { + hpdev = new_pcichild_device(hbus, new_desc); + if (!hpdev) + dev_err(&hbus->hdev->device, + "couldn't record a child device.\n"); + } + } + + /* Move missing children to a list on the stack. */ + spin_lock_irqsave(&hbus->device_list_lock, flags); + do { + found = false; + list_for_each_entry(hpdev, &hbus->children, list_entry) { + if (hpdev->reported_missing) { + found = true; + put_pcichild(hpdev); + list_move_tail(&hpdev->list_entry, &removed); + break; + } + } + } while (found); + spin_unlock_irqrestore(&hbus->device_list_lock, flags); + + /* Delete everything that should no longer exist. */ + while (!list_empty(&removed)) { + hpdev = list_first_entry(&removed, struct hv_pci_dev, + list_entry); + list_del(&hpdev->list_entry); + put_pcichild(hpdev); + } + + switch (hbus->state) { + case hv_pcibus_installed: + /* + * Tell the core to rescan bus + * because there may have been changes. + */ + pci_lock_rescan_remove(); + pci_scan_child_bus(hbus->pci_bus); + pci_unlock_rescan_remove(); + break; + + case hv_pcibus_init: + case hv_pcibus_probed: + survey_child_resources(hbus); + break; + + default: + break; + } + + put_hvpcibus(hbus); + kfree(dr); +} + +/** + * hv_pci_devices_present() - Handles list of new children + * @hbus: Root PCI bus, as understood by this driver + * @relations: Packet from host listing children + * + * This function is invoked whenever a new list of devices for + * this bus appears. + */ +static void hv_pci_devices_present(struct hv_pcibus_device *hbus, + struct pci_bus_relations *relations) +{ + struct hv_dr_state *dr; + struct hv_dr_work *dr_wrk; + unsigned long flags; + bool pending_dr; + + dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT); + if (!dr_wrk) + return; + + dr = kzalloc(offsetof(struct hv_dr_state, func) + + (sizeof(struct pci_function_description) * + (relations->device_count)), GFP_NOWAIT); + if (!dr) { + kfree(dr_wrk); + return; + } + + INIT_WORK(&dr_wrk->wrk, pci_devices_present_work); + dr_wrk->bus = hbus; + dr->device_count = relations->device_count; + if (dr->device_count != 0) { + memcpy(dr->func, relations->func, + sizeof(struct pci_function_description) * + dr->device_count); + } + + spin_lock_irqsave(&hbus->device_list_lock, flags); + /* + * If pending_dr is true, we have already queued a work, + * which will see the new dr. Otherwise, we need to + * queue a new work. + */ + pending_dr = !list_empty(&hbus->dr_list); + list_add_tail(&dr->list_entry, &hbus->dr_list); + spin_unlock_irqrestore(&hbus->device_list_lock, flags); + + if (pending_dr) { + kfree(dr_wrk); + } else { + get_hvpcibus(hbus); + queue_work(hbus->wq, &dr_wrk->wrk); + } +} + +/** + * hv_eject_device_work() - Asynchronously handles ejection + * @work: Work struct embedded in internal device struct + * + * This function handles ejecting a device. Windows will + * attempt to gracefully eject a device, waiting 60 seconds to + * hear back from the guest OS that this completed successfully. + * If this timer expires, the device will be forcibly removed. + */ +static void hv_eject_device_work(struct work_struct *work) +{ + struct pci_eject_response *ejct_pkt; + struct hv_pci_dev *hpdev; + struct pci_dev *pdev; + unsigned long flags; + int wslot; + struct { + struct pci_packet pkt; + u8 buffer[sizeof(struct pci_eject_response)]; + } ctxt; + + hpdev = container_of(work, struct hv_pci_dev, wrk); + + WARN_ON(hpdev->state != hv_pcichild_ejecting); + + /* + * Ejection can come before or after the PCI bus has been set up, so + * attempt to find it and tear down the bus state, if it exists. This + * must be done without constructs like pci_domain_nr(hbus->pci_bus) + * because hbus->pci_bus may not exist yet. + */ + wslot = wslot_to_devfn(hpdev->desc.win_slot.slot); + pdev = pci_get_domain_bus_and_slot(hpdev->hbus->sysdata.domain, 0, + wslot); + if (pdev) { + pci_lock_rescan_remove(); + pci_stop_and_remove_bus_device(pdev); + pci_dev_put(pdev); + pci_unlock_rescan_remove(); + } + + spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags); + list_del(&hpdev->list_entry); + spin_unlock_irqrestore(&hpdev->hbus->device_list_lock, flags); + + memset(&ctxt, 0, sizeof(ctxt)); + ejct_pkt = (struct pci_eject_response *)&ctxt.pkt.message; + ejct_pkt->message_type.type = PCI_EJECTION_COMPLETE; + ejct_pkt->wslot.slot = hpdev->desc.win_slot.slot; + vmbus_sendpacket(hpdev->hbus->hdev->channel, ejct_pkt, + sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt, + VM_PKT_DATA_INBAND, 0); + + put_pcichild(hpdev); + put_pcichild(hpdev); + put_hvpcibus(hpdev->hbus); +} + +/** + * hv_pci_eject_device() - Handles device ejection + * @hpdev: Internal device tracking struct + * + * This function is invoked when an ejection packet arrives. It + * just schedules work so that we don't re-enter the packet + * delivery code handling the ejection. + */ +static void hv_pci_eject_device(struct hv_pci_dev *hpdev) +{ + hpdev->state = hv_pcichild_ejecting; + get_pcichild(hpdev); + INIT_WORK(&hpdev->wrk, hv_eject_device_work); + get_hvpcibus(hpdev->hbus); + queue_work(hpdev->hbus->wq, &hpdev->wrk); +} + +/** + * hv_pci_onchannelcallback() - Handles incoming packets + * @context: Internal bus tracking struct + * + * This function is invoked whenever the host sends a packet to + * this channel (which is private to this root PCI bus). + */ +static void hv_pci_onchannelcallback(void *context) +{ + const int packet_size = 0x100; + int ret; + struct hv_pcibus_device *hbus = context; + u32 bytes_recvd; + u64 req_id; + struct vmpacket_descriptor *desc; + unsigned char *buffer; + int bufferlen = packet_size; + struct pci_packet *comp_packet; + struct pci_response *response; + struct pci_incoming_message *new_message; + struct pci_bus_relations *bus_rel; + struct pci_dev_incoming *dev_message; + struct hv_pci_dev *hpdev; + + buffer = kmalloc(bufferlen, GFP_ATOMIC); + if (!buffer) + return; + + while (1) { + ret = vmbus_recvpacket_raw(hbus->hdev->channel, buffer, + bufferlen, &bytes_recvd, &req_id); + + if (ret == -ENOBUFS) { + kfree(buffer); + /* Handle large packet */ + bufferlen = bytes_recvd; + buffer = kmalloc(bytes_recvd, GFP_ATOMIC); + if (!buffer) + return; + continue; + } + + /* Zero length indicates there are no more packets. */ + if (ret || !bytes_recvd) + break; + + /* + * All incoming packets must be at least as large as a + * response. + */ + if (bytes_recvd <= sizeof(struct pci_response)) + continue; + desc = (struct vmpacket_descriptor *)buffer; + + switch (desc->type) { + case VM_PKT_COMP: + + /* + * The host is trusted, and thus it's safe to interpret + * this transaction ID as a pointer. + */ + comp_packet = (struct pci_packet *)req_id; + response = (struct pci_response *)buffer; + comp_packet->completion_func(comp_packet->compl_ctxt, + response, + bytes_recvd); + break; + + case VM_PKT_DATA_INBAND: + + new_message = (struct pci_incoming_message *)buffer; + switch (new_message->message_type.type) { + case PCI_BUS_RELATIONS: + + bus_rel = (struct pci_bus_relations *)buffer; + if (bytes_recvd < + offsetof(struct pci_bus_relations, func) + + (sizeof(struct pci_function_description) * + (bus_rel->device_count))) { + dev_err(&hbus->hdev->device, + "bus relations too small\n"); + break; + } + + hv_pci_devices_present(hbus, bus_rel); + break; + + case PCI_EJECT: + + dev_message = (struct pci_dev_incoming *)buffer; + hpdev = get_pcichild_wslot(hbus, + dev_message->wslot.slot); + if (hpdev) { + hv_pci_eject_device(hpdev); + put_pcichild(hpdev); + } + break; + + default: + dev_warn(&hbus->hdev->device, + "Unimplemented protocol message %x\n", + new_message->message_type.type); + break; + } + break; + + default: + dev_err(&hbus->hdev->device, + "unhandled packet type %d, tid %llx len %d\n", + desc->type, req_id, bytes_recvd); + break; + } + } + + kfree(buffer); +} + +/** + * hv_pci_protocol_negotiation() - Set up protocol + * @hdev: VMBus's tracking struct for this root PCI bus + * + * This driver is intended to support running on Windows 10 + * (server) and later versions. It will not run on earlier + * versions, as they assume that many of the operations which + * Linux needs accomplished with a spinlock held were done via + * asynchronous messaging via VMBus. Windows 10 increases the + * surface area of PCI emulation so that these actions can take + * place by suspending a virtual processor for their duration. + * + * This function negotiates the channel protocol version, + * failing if the host doesn't support the necessary protocol + * level. + */ +static int hv_pci_protocol_negotiation(struct hv_device *hdev) +{ + struct pci_version_request *version_req; + struct hv_pci_compl comp_pkt; + struct pci_packet *pkt; + int ret; + int i; + + /* + * Initiate the handshake with the host and negotiate + * a version that the host can support. We start with the + * highest version number and go down if the host cannot + * support it. + */ + pkt = kzalloc(sizeof(*pkt) + sizeof(*version_req), GFP_KERNEL); + if (!pkt) + return -ENOMEM; + + init_completion(&comp_pkt.host_event); + pkt->completion_func = hv_pci_generic_compl; + pkt->compl_ctxt = &comp_pkt; + version_req = (struct pci_version_request *)&pkt->message; + version_req->message_type.type = PCI_QUERY_PROTOCOL_VERSION; + + for (i = 0; i < ARRAY_SIZE(pci_protocol_versions); i++) { + version_req->protocol_version = pci_protocol_versions[i]; + ret = vmbus_sendpacket(hdev->channel, version_req, + sizeof(struct pci_version_request), + (unsigned long)pkt, VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (!ret) + ret = wait_for_response(hdev, &comp_pkt.host_event); + + if (ret) { + dev_err(&hdev->device, + "PCI Pass-through VSP failed to request version: %d", + ret); + goto exit; + } + + if (comp_pkt.completion_status >= 0) { + pci_protocol_version = pci_protocol_versions[i]; + dev_info(&hdev->device, + "PCI VMBus probing: Using version %#x\n", + pci_protocol_version); + goto exit; + } + + if (comp_pkt.completion_status != STATUS_REVISION_MISMATCH) { + dev_err(&hdev->device, + "PCI Pass-through VSP failed version request: %#x", + comp_pkt.completion_status); + ret = -EPROTO; + goto exit; + } + + reinit_completion(&comp_pkt.host_event); + } + + dev_err(&hdev->device, + "PCI pass-through VSP failed to find supported version"); + ret = -EPROTO; + +exit: + kfree(pkt); + return ret; +} + +/** + * hv_pci_free_bridge_windows() - Release memory regions for the + * bus + * @hbus: Root PCI bus, as understood by this driver + */ +static void hv_pci_free_bridge_windows(struct hv_pcibus_device *hbus) +{ + /* + * Set the resources back to the way they looked when they + * were allocated by setting IORESOURCE_BUSY again. + */ + + if (hbus->low_mmio_space && hbus->low_mmio_res) { + hbus->low_mmio_res->flags |= IORESOURCE_BUSY; + vmbus_free_mmio(hbus->low_mmio_res->start, + resource_size(hbus->low_mmio_res)); + } + + if (hbus->high_mmio_space && hbus->high_mmio_res) { + hbus->high_mmio_res->flags |= IORESOURCE_BUSY; + vmbus_free_mmio(hbus->high_mmio_res->start, + resource_size(hbus->high_mmio_res)); + } +} + +/** + * hv_pci_allocate_bridge_windows() - Allocate memory regions + * for the bus + * @hbus: Root PCI bus, as understood by this driver + * + * This function calls vmbus_allocate_mmio(), which is itself a + * bit of a compromise. Ideally, we might change the pnp layer + * in the kernel such that it comprehends either PCI devices + * which are "grandchildren of ACPI," with some intermediate bus + * node (in this case, VMBus) or change it such that it + * understands VMBus. The pnp layer, however, has been declared + * deprecated, and not subject to change. + * + * The workaround, implemented here, is to ask VMBus to allocate + * MMIO space for this bus. VMBus itself knows which ranges are + * appropriate by looking at its own ACPI objects. Then, after + * these ranges are claimed, they're modified to look like they + * would have looked if the ACPI and pnp code had allocated + * bridge windows. These descriptors have to exist in this form + * in order to satisfy the code which will get invoked when the + * endpoint PCI function driver calls request_mem_region() or + * request_mem_region_exclusive(). + * + * Return: 0 on success, -errno on failure + */ +static int hv_pci_allocate_bridge_windows(struct hv_pcibus_device *hbus) +{ + resource_size_t align; + int ret; + + if (hbus->low_mmio_space) { + align = 1ULL << (63 - __builtin_clzll(hbus->low_mmio_space)); + ret = vmbus_allocate_mmio(&hbus->low_mmio_res, hbus->hdev, 0, + (u64)(u32)0xffffffff, + hbus->low_mmio_space, + align, false); + if (ret) { + dev_err(&hbus->hdev->device, + "Need %#llx of low MMIO space. Consider reconfiguring the VM.\n", + hbus->low_mmio_space); + return ret; + } + + /* Modify this resource to become a bridge window. */ + hbus->low_mmio_res->flags |= IORESOURCE_WINDOW; + hbus->low_mmio_res->flags &= ~IORESOURCE_BUSY; + pci_add_resource(&hbus->resources_for_children, + hbus->low_mmio_res); + } + + if (hbus->high_mmio_space) { + align = 1ULL << (63 - __builtin_clzll(hbus->high_mmio_space)); + ret = vmbus_allocate_mmio(&hbus->high_mmio_res, hbus->hdev, + 0x100000000, -1, + hbus->high_mmio_space, align, + false); + if (ret) { + dev_err(&hbus->hdev->device, + "Need %#llx of high MMIO space. Consider reconfiguring the VM.\n", + hbus->high_mmio_space); + goto release_low_mmio; + } + + /* Modify this resource to become a bridge window. */ + hbus->high_mmio_res->flags |= IORESOURCE_WINDOW; + hbus->high_mmio_res->flags &= ~IORESOURCE_BUSY; + pci_add_resource(&hbus->resources_for_children, + hbus->high_mmio_res); + } + + return 0; + +release_low_mmio: + if (hbus->low_mmio_res) { + vmbus_free_mmio(hbus->low_mmio_res->start, + resource_size(hbus->low_mmio_res)); + } + + return ret; +} + +/** + * hv_allocate_config_window() - Find MMIO space for PCI Config + * @hbus: Root PCI bus, as understood by this driver + * + * This function claims memory-mapped I/O space for accessing + * configuration space for the functions on this bus. + * + * Return: 0 on success, -errno on failure + */ +static int hv_allocate_config_window(struct hv_pcibus_device *hbus) +{ + int ret; + + /* + * Set up a region of MMIO space to use for accessing configuration + * space. + */ + ret = vmbus_allocate_mmio(&hbus->mem_config, hbus->hdev, 0, -1, + PCI_CONFIG_MMIO_LENGTH, 0x1000, false); + if (ret) + return ret; + + /* + * vmbus_allocate_mmio() gets used for allocating both device endpoint + * resource claims (those which cannot be overlapped) and the ranges + * which are valid for the children of this bus, which are intended + * to be overlapped by those children. Set the flag on this claim + * meaning that this region can't be overlapped. + */ + + hbus->mem_config->flags |= IORESOURCE_BUSY; + + return 0; +} + +static void hv_free_config_window(struct hv_pcibus_device *hbus) +{ + vmbus_free_mmio(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH); +} + +/** + * hv_pci_enter_d0() - Bring the "bus" into the D0 power state + * @hdev: VMBus's tracking struct for this root PCI bus + * + * Return: 0 on success, -errno on failure + */ +static int hv_pci_enter_d0(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); + struct pci_bus_d0_entry *d0_entry; + struct hv_pci_compl comp_pkt; + struct pci_packet *pkt; + int ret; + + /* + * Tell the host that the bus is ready to use, and moved into the + * powered-on state. This includes telling the host which region + * of memory-mapped I/O space has been chosen for configuration space + * access. + */ + pkt = kzalloc(sizeof(*pkt) + sizeof(*d0_entry), GFP_KERNEL); + if (!pkt) + return -ENOMEM; + + init_completion(&comp_pkt.host_event); + pkt->completion_func = hv_pci_generic_compl; + pkt->compl_ctxt = &comp_pkt; + d0_entry = (struct pci_bus_d0_entry *)&pkt->message; + d0_entry->message_type.type = PCI_BUS_D0ENTRY; + d0_entry->mmio_base = hbus->mem_config->start; + + ret = vmbus_sendpacket(hdev->channel, d0_entry, sizeof(*d0_entry), + (unsigned long)pkt, VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (!ret) + ret = wait_for_response(hdev, &comp_pkt.host_event); + + if (ret) + goto exit; + + if (comp_pkt.completion_status < 0) { + dev_err(&hdev->device, + "PCI Pass-through VSP failed D0 Entry with status %x\n", + comp_pkt.completion_status); + ret = -EPROTO; + goto exit; + } + + ret = 0; + +exit: + kfree(pkt); + return ret; +} + +/** + * hv_pci_query_relations() - Ask host to send list of child + * devices + * @hdev: VMBus's tracking struct for this root PCI bus + * + * Return: 0 on success, -errno on failure + */ +static int hv_pci_query_relations(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); + struct pci_message message; + struct completion comp; + int ret; + + /* Ask the host to send along the list of child devices */ + init_completion(&comp); + if (cmpxchg(&hbus->survey_event, NULL, &comp)) + return -ENOTEMPTY; + + memset(&message, 0, sizeof(message)); + message.type = PCI_QUERY_BUS_RELATIONS; + + ret = vmbus_sendpacket(hdev->channel, &message, sizeof(message), + 0, VM_PKT_DATA_INBAND, 0); + if (!ret) + ret = wait_for_response(hdev, &comp); + + return ret; +} + +/** + * hv_send_resources_allocated() - Report local resource choices + * @hdev: VMBus's tracking struct for this root PCI bus + * + * The host OS is expecting to be sent a request as a message + * which contains all the resources that the device will use. + * The response contains those same resources, "translated" + * which is to say, the values which should be used by the + * hardware, when it delivers an interrupt. (MMIO resources are + * used in local terms.) This is nice for Windows, and lines up + * with the FDO/PDO split, which doesn't exist in Linux. Linux + * is deeply expecting to scan an emulated PCI configuration + * space. So this message is sent here only to drive the state + * machine on the host forward. + * + * Return: 0 on success, -errno on failure + */ +static int hv_send_resources_allocated(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); + struct pci_resources_assigned *res_assigned; + struct pci_resources_assigned2 *res_assigned2; + struct hv_pci_compl comp_pkt; + struct hv_pci_dev *hpdev; + struct pci_packet *pkt; + size_t size_res; + u32 wslot; + int ret; + + size_res = (pci_protocol_version < PCI_PROTOCOL_VERSION_1_2) + ? sizeof(*res_assigned) : sizeof(*res_assigned2); + + pkt = kmalloc(sizeof(*pkt) + size_res, GFP_KERNEL); + if (!pkt) + return -ENOMEM; + + ret = 0; + + for (wslot = 0; wslot < 256; wslot++) { + hpdev = get_pcichild_wslot(hbus, wslot); + if (!hpdev) + continue; + + memset(pkt, 0, sizeof(*pkt) + size_res); + init_completion(&comp_pkt.host_event); + pkt->completion_func = hv_pci_generic_compl; + pkt->compl_ctxt = &comp_pkt; + + if (pci_protocol_version < PCI_PROTOCOL_VERSION_1_2) { + res_assigned = + (struct pci_resources_assigned *)&pkt->message; + res_assigned->message_type.type = + PCI_RESOURCES_ASSIGNED; + res_assigned->wslot.slot = hpdev->desc.win_slot.slot; + } else { + res_assigned2 = + (struct pci_resources_assigned2 *)&pkt->message; + res_assigned2->message_type.type = + PCI_RESOURCES_ASSIGNED2; + res_assigned2->wslot.slot = hpdev->desc.win_slot.slot; + } + put_pcichild(hpdev); + + ret = vmbus_sendpacket(hdev->channel, &pkt->message, + size_res, (unsigned long)pkt, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (!ret) + ret = wait_for_response(hdev, &comp_pkt.host_event); + if (ret) + break; + + if (comp_pkt.completion_status < 0) { + ret = -EPROTO; + dev_err(&hdev->device, + "resource allocated returned 0x%x", + comp_pkt.completion_status); + break; + } + } + + kfree(pkt); + return ret; +} + +/** + * hv_send_resources_released() - Report local resources + * released + * @hdev: VMBus's tracking struct for this root PCI bus + * + * Return: 0 on success, -errno on failure + */ +static int hv_send_resources_released(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); + struct pci_child_message pkt; + struct hv_pci_dev *hpdev; + u32 wslot; + int ret; + + for (wslot = 0; wslot < 256; wslot++) { + hpdev = get_pcichild_wslot(hbus, wslot); + if (!hpdev) + continue; + + memset(&pkt, 0, sizeof(pkt)); + pkt.message_type.type = PCI_RESOURCES_RELEASED; + pkt.wslot.slot = hpdev->desc.win_slot.slot; + + put_pcichild(hpdev); + + ret = vmbus_sendpacket(hdev->channel, &pkt, sizeof(pkt), 0, + VM_PKT_DATA_INBAND, 0); + if (ret) + return ret; + } + + return 0; +} + +static void get_hvpcibus(struct hv_pcibus_device *hbus) +{ + refcount_inc(&hbus->remove_lock); +} + +static void put_hvpcibus(struct hv_pcibus_device *hbus) +{ + if (refcount_dec_and_test(&hbus->remove_lock)) + complete(&hbus->remove_event); +} + +/** + * hv_pci_probe() - New VMBus channel probe, for a root PCI bus + * @hdev: VMBus's tracking struct for this root PCI bus + * @dev_id: Identifies the device itself + * + * Return: 0 on success, -errno on failure + */ +static int hv_pci_probe(struct hv_device *hdev, + const struct hv_vmbus_device_id *dev_id) +{ + struct hv_pcibus_device *hbus; + int ret; + + /* + * hv_pcibus_device contains the hypercall arguments for retargeting in + * hv_irq_unmask(). Those must not cross a page boundary. + */ + BUILD_BUG_ON(sizeof(*hbus) > PAGE_SIZE); + + hbus = (struct hv_pcibus_device *)get_zeroed_page(GFP_KERNEL); + if (!hbus) + return -ENOMEM; + hbus->state = hv_pcibus_init; + + /* + * The PCI bus "domain" is what is called "segment" in ACPI and + * other specs. Pull it from the instance ID, to get something + * unique. Bytes 8 and 9 are what is used in Windows guests, so + * do the same thing for consistency. Note that, since this code + * only runs in a Hyper-V VM, Hyper-V can (and does) guarantee + * that (1) the only domain in use for something that looks like + * a physical PCI bus (which is actually emulated by the + * hypervisor) is domain 0 and (2) there will be no overlap + * between domains derived from these instance IDs in the same + * VM. + */ + hbus->sysdata.domain = hdev->dev_instance.b[9] | + hdev->dev_instance.b[8] << 8; + + hbus->hdev = hdev; + refcount_set(&hbus->remove_lock, 1); + INIT_LIST_HEAD(&hbus->children); + INIT_LIST_HEAD(&hbus->dr_list); + INIT_LIST_HEAD(&hbus->resources_for_children); + spin_lock_init(&hbus->config_lock); + spin_lock_init(&hbus->device_list_lock); + spin_lock_init(&hbus->retarget_msi_interrupt_lock); + init_completion(&hbus->remove_event); + hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0, + hbus->sysdata.domain); + if (!hbus->wq) { + ret = -ENOMEM; + goto free_bus; + } + + ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0, + hv_pci_onchannelcallback, hbus); + if (ret) + goto destroy_wq; + + hv_set_drvdata(hdev, hbus); + + ret = hv_pci_protocol_negotiation(hdev); + if (ret) + goto close; + + ret = hv_allocate_config_window(hbus); + if (ret) + goto close; + + hbus->cfg_addr = ioremap(hbus->mem_config->start, + PCI_CONFIG_MMIO_LENGTH); + if (!hbus->cfg_addr) { + dev_err(&hdev->device, + "Unable to map a virtual address for config space\n"); + ret = -ENOMEM; + goto free_config; + } + + hbus->sysdata.fwnode = irq_domain_alloc_fwnode(hbus); + if (!hbus->sysdata.fwnode) { + ret = -ENOMEM; + goto unmap; + } + + ret = hv_pcie_init_irq_domain(hbus); + if (ret) + goto free_fwnode; + + ret = hv_pci_query_relations(hdev); + if (ret) + goto free_irq_domain; + + ret = hv_pci_enter_d0(hdev); + if (ret) + goto free_irq_domain; + + ret = hv_pci_allocate_bridge_windows(hbus); + if (ret) + goto free_irq_domain; + + ret = hv_send_resources_allocated(hdev); + if (ret) + goto free_windows; + + prepopulate_bars(hbus); + + hbus->state = hv_pcibus_probed; + + ret = create_root_hv_pci_bus(hbus); + if (ret) + goto free_windows; + + return 0; + +free_windows: + hv_pci_free_bridge_windows(hbus); +free_irq_domain: + irq_domain_remove(hbus->irq_domain); +free_fwnode: + irq_domain_free_fwnode(hbus->sysdata.fwnode); +unmap: + iounmap(hbus->cfg_addr); +free_config: + hv_free_config_window(hbus); +close: + vmbus_close(hdev->channel); +destroy_wq: + destroy_workqueue(hbus->wq); +free_bus: + free_page((unsigned long)hbus); + return ret; +} + +static void hv_pci_bus_exit(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); + struct { + struct pci_packet teardown_packet; + u8 buffer[sizeof(struct pci_message)]; + } pkt; + struct pci_bus_relations relations; + struct hv_pci_compl comp_pkt; + int ret; + + /* + * After the host sends the RESCIND_CHANNEL message, it doesn't + * access the per-channel ringbuffer any longer. + */ + if (hdev->channel->rescind) + return; + + /* Delete any children which might still exist. */ + memset(&relations, 0, sizeof(relations)); + hv_pci_devices_present(hbus, &relations); + + ret = hv_send_resources_released(hdev); + if (ret) + dev_err(&hdev->device, + "Couldn't send resources released packet(s)\n"); + + memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet)); + init_completion(&comp_pkt.host_event); + pkt.teardown_packet.completion_func = hv_pci_generic_compl; + pkt.teardown_packet.compl_ctxt = &comp_pkt; + pkt.teardown_packet.message[0].type = PCI_BUS_D0EXIT; + + ret = vmbus_sendpacket(hdev->channel, &pkt.teardown_packet.message, + sizeof(struct pci_message), + (unsigned long)&pkt.teardown_packet, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (!ret) + wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ); +} + +/** + * hv_pci_remove() - Remove routine for this VMBus channel + * @hdev: VMBus's tracking struct for this root PCI bus + * + * Return: 0 on success, -errno on failure + */ +static int hv_pci_remove(struct hv_device *hdev) +{ + struct hv_pcibus_device *hbus; + + hbus = hv_get_drvdata(hdev); + if (hbus->state == hv_pcibus_installed) { + /* Remove the bus from PCI's point of view. */ + pci_lock_rescan_remove(); + pci_stop_root_bus(hbus->pci_bus); + pci_remove_root_bus(hbus->pci_bus); + pci_unlock_rescan_remove(); + hbus->state = hv_pcibus_removed; + } + + hv_pci_bus_exit(hdev); + + vmbus_close(hdev->channel); + + iounmap(hbus->cfg_addr); + hv_free_config_window(hbus); + pci_free_resource_list(&hbus->resources_for_children); + hv_pci_free_bridge_windows(hbus); + irq_domain_remove(hbus->irq_domain); + irq_domain_free_fwnode(hbus->sysdata.fwnode); + put_hvpcibus(hbus); + wait_for_completion(&hbus->remove_event); + destroy_workqueue(hbus->wq); + free_page((unsigned long)hbus); + return 0; +} + +static const struct hv_vmbus_device_id hv_pci_id_table[] = { + /* PCI Pass-through Class ID */ + /* 44C4F61D-4444-4400-9D52-802E27EDE19F */ + { HV_PCIE_GUID, }, + { }, +}; + +MODULE_DEVICE_TABLE(vmbus, hv_pci_id_table); + +static struct hv_driver hv_pci_drv = { + .name = "hv_pci", + .id_table = hv_pci_id_table, + .probe = hv_pci_probe, + .remove = hv_pci_remove, +}; + +static void __exit exit_hv_pci_drv(void) +{ + vmbus_driver_unregister(&hv_pci_drv); +} + +static int __init init_hv_pci_drv(void) +{ + return vmbus_driver_register(&hv_pci_drv); +} + +module_init(init_hv_pci_drv); +module_exit(exit_hv_pci_drv); + +MODULE_DESCRIPTION("Hyper-V PCI"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c new file mode 100644 index 000000000000..23e270839e6a --- /dev/null +++ b/drivers/pci/controller/pci-mvebu.c @@ -0,0 +1,1313 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe driver for Marvell Armada 370 and Armada XP SoCs + * + * Author: Thomas Petazzoni + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* + * PCIe unit register offsets. + */ +#define PCIE_DEV_ID_OFF 0x0000 +#define PCIE_CMD_OFF 0x0004 +#define PCIE_DEV_REV_OFF 0x0008 +#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) +#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) +#define PCIE_CAP_PCIEXP 0x0060 +#define PCIE_HEADER_LOG_4_OFF 0x0128 +#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) +#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) +#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) +#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4)) +#define PCIE_WIN5_CTRL_OFF 0x1880 +#define PCIE_WIN5_BASE_OFF 0x1884 +#define PCIE_WIN5_REMAP_OFF 0x188c +#define PCIE_CONF_ADDR_OFF 0x18f8 +#define PCIE_CONF_ADDR_EN 0x80000000 +#define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc)) +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 16) +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11) +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 8) +#define PCIE_CONF_ADDR(bus, devfn, where) \ + (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ + PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where) | \ + PCIE_CONF_ADDR_EN) +#define PCIE_CONF_DATA_OFF 0x18fc +#define PCIE_MASK_OFF 0x1910 +#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_CTRL_OFF 0x1a00 +#define PCIE_CTRL_X1_MODE 0x0001 +#define PCIE_STAT_OFF 0x1a04 +#define PCIE_STAT_BUS 0xff00 +#define PCIE_STAT_DEV 0x1f0000 +#define PCIE_STAT_LINK_DOWN BIT(0) +#define PCIE_RC_RTSTA 0x1a14 +#define PCIE_DEBUG_CTRL 0x1a60 +#define PCIE_DEBUG_SOFT_RESET BIT(20) + +enum { + PCISWCAP = PCI_BRIDGE_CONTROL + 2, + PCISWCAP_EXP_LIST_ID = PCISWCAP + PCI_CAP_LIST_ID, + PCISWCAP_EXP_DEVCAP = PCISWCAP + PCI_EXP_DEVCAP, + PCISWCAP_EXP_DEVCTL = PCISWCAP + PCI_EXP_DEVCTL, + PCISWCAP_EXP_LNKCAP = PCISWCAP + PCI_EXP_LNKCAP, + PCISWCAP_EXP_LNKCTL = PCISWCAP + PCI_EXP_LNKCTL, + PCISWCAP_EXP_SLTCAP = PCISWCAP + PCI_EXP_SLTCAP, + PCISWCAP_EXP_SLTCTL = PCISWCAP + PCI_EXP_SLTCTL, + PCISWCAP_EXP_RTCTL = PCISWCAP + PCI_EXP_RTCTL, + PCISWCAP_EXP_RTSTA = PCISWCAP + PCI_EXP_RTSTA, + PCISWCAP_EXP_DEVCAP2 = PCISWCAP + PCI_EXP_DEVCAP2, + PCISWCAP_EXP_DEVCTL2 = PCISWCAP + PCI_EXP_DEVCTL2, + PCISWCAP_EXP_LNKCAP2 = PCISWCAP + PCI_EXP_LNKCAP2, + PCISWCAP_EXP_LNKCTL2 = PCISWCAP + PCI_EXP_LNKCTL2, + PCISWCAP_EXP_SLTCAP2 = PCISWCAP + PCI_EXP_SLTCAP2, + PCISWCAP_EXP_SLTCTL2 = PCISWCAP + PCI_EXP_SLTCTL2, +}; + +/* PCI configuration space of a PCI-to-PCI bridge */ +struct mvebu_sw_pci_bridge { + u16 vendor; + u16 device; + u16 command; + u16 status; + u16 class; + u8 interface; + u8 revision; + u8 bist; + u8 header_type; + u8 latency_timer; + u8 cache_line_size; + u32 bar[2]; + u8 primary_bus; + u8 secondary_bus; + u8 subordinate_bus; + u8 secondary_latency_timer; + u8 iobase; + u8 iolimit; + u16 secondary_status; + u16 membase; + u16 memlimit; + u16 iobaseupper; + u16 iolimitupper; + u32 romaddr; + u8 intline; + u8 intpin; + u16 bridgectrl; + + /* PCI express capability */ + u32 pcie_sltcap; + u16 pcie_devctl; + u16 pcie_rtctl; +}; + +struct mvebu_pcie_port; + +/* Structure representing all PCIe interfaces */ +struct mvebu_pcie { + struct platform_device *pdev; + struct mvebu_pcie_port *ports; + struct msi_controller *msi; + struct resource io; + struct resource realio; + struct resource mem; + struct resource busn; + int nports; +}; + +struct mvebu_pcie_window { + phys_addr_t base; + phys_addr_t remap; + size_t size; +}; + +/* Structure representing one PCIe interface */ +struct mvebu_pcie_port { + char *name; + void __iomem *base; + u32 port; + u32 lane; + int devfn; + unsigned int mem_target; + unsigned int mem_attr; + unsigned int io_target; + unsigned int io_attr; + struct clk *clk; + struct gpio_desc *reset_gpio; + char *reset_name; + struct mvebu_sw_pci_bridge bridge; + struct device_node *dn; + struct mvebu_pcie *pcie; + struct mvebu_pcie_window memwin; + struct mvebu_pcie_window iowin; + u32 saved_pcie_stat; +}; + +static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) +{ + writel(val, port->base + reg); +} + +static inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg) +{ + return readl(port->base + reg); +} + +static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) +{ + return port->io_target != -1 && port->io_attr != -1; +} + +static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) +{ + return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); +} + +static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr) +{ + u32 stat; + + stat = mvebu_readl(port, PCIE_STAT_OFF); + stat &= ~PCIE_STAT_BUS; + stat |= nr << 8; + mvebu_writel(port, stat, PCIE_STAT_OFF); +} + +static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr) +{ + u32 stat; + + stat = mvebu_readl(port, PCIE_STAT_OFF); + stat &= ~PCIE_STAT_DEV; + stat |= nr << 16; + mvebu_writel(port, stat, PCIE_STAT_OFF); +} + +/* + * Setup PCIE BARs and Address Decode Wins: + * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks + * WIN[0-3] -> DRAM bank[0-3] + */ +static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) +{ + const struct mbus_dram_target_info *dram; + u32 size; + int i; + + dram = mv_mbus_dram_info(); + + /* First, disable and clear BARs and windows. */ + for (i = 1; i < 3; i++) { + mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i)); + mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i)); + mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i)); + } + + for (i = 0; i < 5; i++) { + mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i)); + mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i)); + mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i)); + } + + mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF); + mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF); + mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF); + + /* Setup windows for DDR banks. Count total DDR size on the fly. */ + size = 0; + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + + mvebu_writel(port, cs->base & 0xffff0000, + PCIE_WIN04_BASE_OFF(i)); + mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i)); + mvebu_writel(port, + ((cs->size - 1) & 0xffff0000) | + (cs->mbus_attr << 8) | + (dram->mbus_dram_target_id << 4) | 1, + PCIE_WIN04_CTRL_OFF(i)); + + size += cs->size; + } + + /* Round up 'size' to the nearest power of two. */ + if ((size & (size - 1)) != 0) + size = 1 << fls(size); + + /* Setup BAR[1] to all DRAM banks. */ + mvebu_writel(port, dram->cs[0].base, PCIE_BAR_LO_OFF(1)); + mvebu_writel(port, 0, PCIE_BAR_HI_OFF(1)); + mvebu_writel(port, ((size - 1) & 0xffff0000) | 1, + PCIE_BAR_CTRL_OFF(1)); +} + +static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) +{ + u32 cmd, mask; + + /* Point PCIe unit MBUS decode windows to DRAM space. */ + mvebu_pcie_setup_wins(port); + + /* Master + slave enable. */ + cmd = mvebu_readl(port, PCIE_CMD_OFF); + cmd |= PCI_COMMAND_IO; + cmd |= PCI_COMMAND_MEMORY; + cmd |= PCI_COMMAND_MASTER; + mvebu_writel(port, cmd, PCIE_CMD_OFF); + + /* Enable interrupt lines A-D. */ + mask = mvebu_readl(port, PCIE_MASK_OFF); + mask |= PCIE_MASK_ENABLE_INTS; + mvebu_writel(port, mask, PCIE_MASK_OFF); +} + +static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, + struct pci_bus *bus, + u32 devfn, int where, int size, u32 *val) +{ + void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; + + mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), + PCIE_CONF_ADDR_OFF); + + switch (size) { + case 1: + *val = readb_relaxed(conf_data + (where & 3)); + break; + case 2: + *val = readw_relaxed(conf_data + (where & 2)); + break; + case 4: + *val = readl_relaxed(conf_data); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, + struct pci_bus *bus, + u32 devfn, int where, int size, u32 val) +{ + void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; + + mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), + PCIE_CONF_ADDR_OFF); + + switch (size) { + case 1: + writeb(val, conf_data + (where & 3)); + break; + case 2: + writew(val, conf_data + (where & 2)); + break; + case 4: + writel(val, conf_data); + break; + default: + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + return PCIBIOS_SUCCESSFUL; +} + +/* + * Remove windows, starting from the largest ones to the smallest + * ones. + */ +static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port, + phys_addr_t base, size_t size) +{ + while (size) { + size_t sz = 1 << (fls(size) - 1); + + mvebu_mbus_del_window(base, sz); + base += sz; + size -= sz; + } +} + +/* + * MBus windows can only have a power of two size, but PCI BARs do not + * have this constraint. Therefore, we have to split the PCI BAR into + * areas each having a power of two size. We start from the largest + * one (i.e highest order bit set in the size). + */ +static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, + unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap) +{ + size_t size_mapped = 0; + + while (size) { + size_t sz = 1 << (fls(size) - 1); + int ret; + + ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base, + sz, remap); + if (ret) { + phys_addr_t end = base + sz - 1; + + dev_err(&port->pcie->pdev->dev, + "Could not create MBus window at [mem %pa-%pa]: %d\n", + &base, &end, ret); + mvebu_pcie_del_windows(port, base - size_mapped, + size_mapped); + return; + } + + size -= sz; + size_mapped += sz; + base += sz; + if (remap != MVEBU_MBUS_NO_REMAP) + remap += sz; + } +} + +static void mvebu_pcie_set_window(struct mvebu_pcie_port *port, + unsigned int target, unsigned int attribute, + const struct mvebu_pcie_window *desired, + struct mvebu_pcie_window *cur) +{ + if (desired->base == cur->base && desired->remap == cur->remap && + desired->size == cur->size) + return; + + if (cur->size != 0) { + mvebu_pcie_del_windows(port, cur->base, cur->size); + cur->size = 0; + cur->base = 0; + + /* + * If something tries to change the window while it is enabled + * the change will not be done atomically. That would be + * difficult to do in the general case. + */ + } + + if (desired->size == 0) + return; + + mvebu_pcie_add_windows(port, target, attribute, desired->base, + desired->size, desired->remap); + *cur = *desired; +} + +static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) +{ + struct mvebu_pcie_window desired = {}; + + /* Are the new iobase/iolimit values invalid? */ + if (port->bridge.iolimit < port->bridge.iobase || + port->bridge.iolimitupper < port->bridge.iobaseupper || + !(port->bridge.command & PCI_COMMAND_IO)) { + mvebu_pcie_set_window(port, port->io_target, port->io_attr, + &desired, &port->iowin); + return; + } + + if (!mvebu_has_ioport(port)) { + dev_WARN(&port->pcie->pdev->dev, + "Attempt to set IO when IO is disabled\n"); + return; + } + + /* + * We read the PCI-to-PCI bridge emulated registers, and + * calculate the base address and size of the address decoding + * window to setup, according to the PCI-to-PCI bridge + * specifications. iobase is the bus address, port->iowin_base + * is the CPU address. + */ + desired.remap = ((port->bridge.iobase & 0xF0) << 8) | + (port->bridge.iobaseupper << 16); + desired.base = port->pcie->io.start + desired.remap; + desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | + (port->bridge.iolimitupper << 16)) - + desired.remap) + + 1; + + mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, + &port->iowin); +} + +static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) +{ + struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP}; + + /* Are the new membase/memlimit values invalid? */ + if (port->bridge.memlimit < port->bridge.membase || + !(port->bridge.command & PCI_COMMAND_MEMORY)) { + mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, + &desired, &port->memwin); + return; + } + + /* + * We read the PCI-to-PCI bridge emulated registers, and + * calculate the base address and size of the address decoding + * window to setup, according to the PCI-to-PCI bridge + * specifications. + */ + desired.base = ((port->bridge.membase & 0xFFF0) << 16); + desired.size = (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - + desired.base + 1; + + mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, + &port->memwin); +} + +/* + * Initialize the configuration space of the PCI-to-PCI bridge + * associated with the given PCIe interface. + */ +static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port) +{ + struct mvebu_sw_pci_bridge *bridge = &port->bridge; + + memset(bridge, 0, sizeof(struct mvebu_sw_pci_bridge)); + + bridge->class = PCI_CLASS_BRIDGE_PCI; + bridge->vendor = PCI_VENDOR_ID_MARVELL; + bridge->device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16; + bridge->revision = mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff; + bridge->header_type = PCI_HEADER_TYPE_BRIDGE; + bridge->cache_line_size = 0x10; + + /* We support 32 bits I/O addressing */ + bridge->iobase = PCI_IO_RANGE_TYPE_32; + bridge->iolimit = PCI_IO_RANGE_TYPE_32; + + /* Add capabilities */ + bridge->status = PCI_STATUS_CAP_LIST; +} + +/* + * Read the configuration space of the PCI-to-PCI bridge associated to + * the given PCIe interface. + */ +static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port, + unsigned int where, int size, u32 *value) +{ + struct mvebu_sw_pci_bridge *bridge = &port->bridge; + + switch (where & ~3) { + case PCI_VENDOR_ID: + *value = bridge->device << 16 | bridge->vendor; + break; + + case PCI_COMMAND: + *value = bridge->command | bridge->status << 16; + break; + + case PCI_CLASS_REVISION: + *value = bridge->class << 16 | bridge->interface << 8 | + bridge->revision; + break; + + case PCI_CACHE_LINE_SIZE: + *value = bridge->bist << 24 | bridge->header_type << 16 | + bridge->latency_timer << 8 | bridge->cache_line_size; + break; + + case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1: + *value = bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4]; + break; + + case PCI_PRIMARY_BUS: + *value = (bridge->secondary_latency_timer << 24 | + bridge->subordinate_bus << 16 | + bridge->secondary_bus << 8 | + bridge->primary_bus); + break; + + case PCI_IO_BASE: + if (!mvebu_has_ioport(port)) + *value = bridge->secondary_status << 16; + else + *value = (bridge->secondary_status << 16 | + bridge->iolimit << 8 | + bridge->iobase); + break; + + case PCI_MEMORY_BASE: + *value = (bridge->memlimit << 16 | bridge->membase); + break; + + case PCI_PREF_MEMORY_BASE: + *value = 0; + break; + + case PCI_IO_BASE_UPPER16: + *value = (bridge->iolimitupper << 16 | bridge->iobaseupper); + break; + + case PCI_CAPABILITY_LIST: + *value = PCISWCAP; + break; + + case PCI_ROM_ADDRESS1: + *value = 0; + break; + + case PCI_INTERRUPT_LINE: + /* LINE PIN MIN_GNT MAX_LAT */ + *value = 0; + break; + + case PCISWCAP_EXP_LIST_ID: + /* Set PCIe v2, root port, slot support */ + *value = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2 | + PCI_EXP_FLAGS_SLOT) << 16 | PCI_CAP_ID_EXP; + break; + + case PCISWCAP_EXP_DEVCAP: + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP); + break; + + case PCISWCAP_EXP_DEVCTL: + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) & + ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | + PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); + *value |= bridge->pcie_devctl; + break; + + case PCISWCAP_EXP_LNKCAP: + /* + * PCIe requires the clock power management capability to be + * hard-wired to zero for downstream ports + */ + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & + ~PCI_EXP_LNKCAP_CLKPM; + break; + + case PCISWCAP_EXP_LNKCTL: + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); + break; + + case PCISWCAP_EXP_SLTCAP: + *value = bridge->pcie_sltcap; + break; + + case PCISWCAP_EXP_SLTCTL: + *value = PCI_EXP_SLTSTA_PDS << 16; + break; + + case PCISWCAP_EXP_RTCTL: + *value = bridge->pcie_rtctl; + break; + + case PCISWCAP_EXP_RTSTA: + *value = mvebu_readl(port, PCIE_RC_RTSTA); + break; + + /* PCIe requires the v2 fields to be hard-wired to zero */ + case PCISWCAP_EXP_DEVCAP2: + case PCISWCAP_EXP_DEVCTL2: + case PCISWCAP_EXP_LNKCAP2: + case PCISWCAP_EXP_LNKCTL2: + case PCISWCAP_EXP_SLTCAP2: + case PCISWCAP_EXP_SLTCTL2: + default: + /* + * PCI defines configuration read accesses to reserved or + * unimplemented registers to read as zero and complete + * normally. + */ + *value = 0; + return PCIBIOS_SUCCESSFUL; + } + + if (size == 2) + *value = (*value >> (8 * (where & 3))) & 0xffff; + else if (size == 1) + *value = (*value >> (8 * (where & 3))) & 0xff; + + return PCIBIOS_SUCCESSFUL; +} + +/* Write to the PCI-to-PCI bridge configuration space */ +static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port, + unsigned int where, int size, u32 value) +{ + struct mvebu_sw_pci_bridge *bridge = &port->bridge; + u32 mask, reg; + int err; + + if (size == 4) + mask = 0x0; + else if (size == 2) + mask = ~(0xffff << ((where & 3) * 8)); + else if (size == 1) + mask = ~(0xff << ((where & 3) * 8)); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, ®); + if (err) + return err; + + value = (reg & mask) | value << ((where & 3) * 8); + + switch (where & ~3) { + case PCI_COMMAND: + { + u32 old = bridge->command; + + if (!mvebu_has_ioport(port)) + value &= ~PCI_COMMAND_IO; + + bridge->command = value & 0xffff; + if ((old ^ bridge->command) & PCI_COMMAND_IO) + mvebu_pcie_handle_iobase_change(port); + if ((old ^ bridge->command) & PCI_COMMAND_MEMORY) + mvebu_pcie_handle_membase_change(port); + break; + } + + case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1: + bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4] = value; + break; + + case PCI_IO_BASE: + /* + * We also keep bit 1 set, it is a read-only bit that + * indicates we support 32 bits addressing for the + * I/O + */ + bridge->iobase = (value & 0xff) | PCI_IO_RANGE_TYPE_32; + bridge->iolimit = ((value >> 8) & 0xff) | PCI_IO_RANGE_TYPE_32; + mvebu_pcie_handle_iobase_change(port); + break; + + case PCI_MEMORY_BASE: + bridge->membase = value & 0xffff; + bridge->memlimit = value >> 16; + mvebu_pcie_handle_membase_change(port); + break; + + case PCI_IO_BASE_UPPER16: + bridge->iobaseupper = value & 0xffff; + bridge->iolimitupper = value >> 16; + mvebu_pcie_handle_iobase_change(port); + break; + + case PCI_PRIMARY_BUS: + bridge->primary_bus = value & 0xff; + bridge->secondary_bus = (value >> 8) & 0xff; + bridge->subordinate_bus = (value >> 16) & 0xff; + bridge->secondary_latency_timer = (value >> 24) & 0xff; + mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus); + break; + + case PCISWCAP_EXP_DEVCTL: + /* + * Armada370 data says these bits must always + * be zero when in root complex mode. + */ + value &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | + PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); + + /* + * If the mask is 0xffff0000, then we only want to write + * the device control register, rather than clearing the + * RW1C bits in the device status register. Mask out the + * status register bits. + */ + if (mask == 0xffff0000) + value &= 0xffff; + + mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); + break; + + case PCISWCAP_EXP_LNKCTL: + /* + * If we don't support CLKREQ, we must ensure that the + * CLKREQ enable bit always reads zero. Since we haven't + * had this capability, and it's dependent on board wiring, + * disable it for the time being. + */ + value &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + + /* + * If the mask is 0xffff0000, then we only want to write + * the link control register, rather than clearing the + * RW1C bits in the link status register. Mask out the + * RW1C status register bits. + */ + if (mask == 0xffff0000) + value &= ~((PCI_EXP_LNKSTA_LABS | + PCI_EXP_LNKSTA_LBMS) << 16); + + mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); + break; + + case PCISWCAP_EXP_RTSTA: + mvebu_writel(port, value, PCIE_RC_RTSTA); + break; + + default: + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) +{ + return sys->private_data; +} + +static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, + struct pci_bus *bus, + int devfn) +{ + int i; + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; + + if (bus->number == 0 && port->devfn == devfn) + return port; + if (bus->number != 0 && + bus->number >= port->bridge.secondary_bus && + bus->number <= port->bridge.subordinate_bus) + return port; + } + + return NULL; +} + +/* PCI configuration space write function */ +static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata); + struct mvebu_pcie_port *port; + int ret; + + port = mvebu_pcie_find_port(pcie, bus, devfn); + if (!port) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Access the emulated PCI-to-PCI bridge */ + if (bus->number == 0) + return mvebu_sw_pci_bridge_write(port, where, size, val); + + if (!mvebu_pcie_link_up(port)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Access the real PCIe interface */ + ret = mvebu_pcie_hw_wr_conf(port, bus, devfn, + where, size, val); + + return ret; +} + +/* PCI configuration space read function */ +static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata); + struct mvebu_pcie_port *port; + int ret; + + port = mvebu_pcie_find_port(pcie, bus, devfn); + if (!port) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + /* Access the emulated PCI-to-PCI bridge */ + if (bus->number == 0) + return mvebu_sw_pci_bridge_read(port, where, size, val); + + if (!mvebu_pcie_link_up(port)) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + /* Access the real PCIe interface */ + ret = mvebu_pcie_hw_rd_conf(port, bus, devfn, + where, size, val); + + return ret; +} + +static struct pci_ops mvebu_pcie_ops = { + .read = mvebu_pcie_rd_conf, + .write = mvebu_pcie_wr_conf, +}; + +static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) +{ + struct mvebu_pcie *pcie = sys_to_pcie(sys); + int err, i; + + pcie->mem.name = "PCI MEM"; + pcie->realio.name = "PCI I/O"; + + if (resource_size(&pcie->realio) != 0) + pci_add_resource_offset(&sys->resources, &pcie->realio, + sys->io_offset); + + pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); + pci_add_resource(&sys->resources, &pcie->busn); + + err = devm_request_pci_bus_resources(&pcie->pdev->dev, &sys->resources); + if (err) + return 0; + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; + + if (!port->base) + continue; + mvebu_pcie_setup_hw(port); + } + + return 1; +} + +static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, + const struct resource *res, + resource_size_t start, + resource_size_t size, + resource_size_t align) +{ + if (dev->bus->number != 0) + return start; + + /* + * On the PCI-to-PCI bridge side, the I/O windows must have at + * least a 64 KB size and the memory windows must have at + * least a 1 MB size. Moreover, MBus windows need to have a + * base address aligned on their size, and their size must be + * a power of two. This means that if the BAR doesn't have a + * power of two size, several MBus windows will actually be + * created. We need to ensure that the biggest MBus window + * (which will be the first one) is aligned on its size, which + * explains the rounddown_pow_of_two() being done here. + */ + if (res->flags & IORESOURCE_IO) + return round_up(start, max_t(resource_size_t, SZ_64K, + rounddown_pow_of_two(size))); + else if (res->flags & IORESOURCE_MEM) + return round_up(start, max_t(resource_size_t, SZ_1M, + rounddown_pow_of_two(size))); + else + return start; +} + +static void mvebu_pcie_enable(struct mvebu_pcie *pcie) +{ + struct hw_pci hw; + + memset(&hw, 0, sizeof(hw)); + +#ifdef CONFIG_PCI_MSI + hw.msi_ctrl = pcie->msi; +#endif + + hw.nr_controllers = 1; + hw.private_data = (void **)&pcie; + hw.setup = mvebu_pcie_setup; + hw.map_irq = of_irq_parse_and_map_pci; + hw.ops = &mvebu_pcie_ops; + hw.align_resource = mvebu_pcie_align_resource; + + pci_common_init_dev(&pcie->pdev->dev, &hw); +} + +/* + * Looks up the list of register addresses encoded into the reg = + * <...> property for one that matches the given port/lane. Once + * found, maps it. + */ +static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev, + struct device_node *np, + struct mvebu_pcie_port *port) +{ + struct resource regs; + int ret = 0; + + ret = of_address_to_resource(np, 0, ®s); + if (ret) + return ERR_PTR(ret); + + return devm_ioremap_resource(&pdev->dev, ®s); +} + +#define DT_FLAGS_TO_TYPE(flags) (((flags) >> 24) & 0x03) +#define DT_TYPE_IO 0x1 +#define DT_TYPE_MEM32 0x2 +#define DT_CPUADDR_TO_TARGET(cpuaddr) (((cpuaddr) >> 56) & 0xFF) +#define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF) + +static int mvebu_get_tgt_attr(struct device_node *np, int devfn, + unsigned long type, + unsigned int *tgt, + unsigned int *attr) +{ + const int na = 3, ns = 2; + const __be32 *range; + int rlen, nranges, rangesz, pna, i; + + *tgt = -1; + *attr = -1; + + range = of_get_property(np, "ranges", &rlen); + if (!range) + return -EINVAL; + + pna = of_n_addr_cells(np); + rangesz = pna + na + ns; + nranges = rlen / sizeof(__be32) / rangesz; + + for (i = 0; i < nranges; i++, range += rangesz) { + u32 flags = of_read_number(range, 1); + u32 slot = of_read_number(range + 1, 1); + u64 cpuaddr = of_read_number(range + na, pna); + unsigned long rtype; + + if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_IO) + rtype = IORESOURCE_IO; + else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32) + rtype = IORESOURCE_MEM; + else + continue; + + if (slot == PCI_SLOT(devfn) && type == rtype) { + *tgt = DT_CPUADDR_TO_TARGET(cpuaddr); + *attr = DT_CPUADDR_TO_ATTR(cpuaddr); + return 0; + } + } + + return -ENOENT; +} + +#ifdef CONFIG_PM_SLEEP +static int mvebu_pcie_suspend(struct device *dev) +{ + struct mvebu_pcie *pcie; + int i; + + pcie = dev_get_drvdata(dev); + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = pcie->ports + i; + port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF); + } + + return 0; +} + +static int mvebu_pcie_resume(struct device *dev) +{ + struct mvebu_pcie *pcie; + int i; + + pcie = dev_get_drvdata(dev); + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = pcie->ports + i; + mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF); + mvebu_pcie_setup_hw(port); + } + + return 0; +} +#endif + +static void mvebu_pcie_port_clk_put(void *data) +{ + struct mvebu_pcie_port *port = data; + + clk_put(port->clk); +} + +static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + struct mvebu_pcie_port *port, struct device_node *child) +{ + struct device *dev = &pcie->pdev->dev; + enum of_gpio_flags flags; + int reset_gpio, ret; + + port->pcie = pcie; + + if (of_property_read_u32(child, "marvell,pcie-port", &port->port)) { + dev_warn(dev, "ignoring %pOF, missing pcie-port property\n", + child); + goto skip; + } + + if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane)) + port->lane = 0; + + port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port, + port->lane); + if (!port->name) { + ret = -ENOMEM; + goto err; + } + + port->devfn = of_pci_get_devfn(child); + if (port->devfn < 0) + goto skip; + + ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM, + &port->mem_target, &port->mem_attr); + if (ret < 0) { + dev_err(dev, "%s: cannot get tgt/attr for mem window\n", + port->name); + goto skip; + } + + if (resource_size(&pcie->io) != 0) { + mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_IO, + &port->io_target, &port->io_attr); + } else { + port->io_target = -1; + port->io_attr = -1; + } + + reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags); + if (reset_gpio == -EPROBE_DEFER) { + ret = reset_gpio; + goto err; + } + + if (gpio_is_valid(reset_gpio)) { + unsigned long gpio_flags; + + port->reset_name = devm_kasprintf(dev, GFP_KERNEL, "%s-reset", + port->name); + if (!port->reset_name) { + ret = -ENOMEM; + goto err; + } + + if (flags & OF_GPIO_ACTIVE_LOW) { + dev_info(dev, "%pOF: reset gpio is active low\n", + child); + gpio_flags = GPIOF_ACTIVE_LOW | + GPIOF_OUT_INIT_LOW; + } else { + gpio_flags = GPIOF_OUT_INIT_HIGH; + } + + ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags, + port->reset_name); + if (ret) { + if (ret == -EPROBE_DEFER) + goto err; + goto skip; + } + + port->reset_gpio = gpio_to_desc(reset_gpio); + } + + port->clk = of_clk_get_by_name(child, NULL); + if (IS_ERR(port->clk)) { + dev_err(dev, "%s: cannot get clock\n", port->name); + goto skip; + } + + ret = devm_add_action(dev, mvebu_pcie_port_clk_put, port); + if (ret < 0) { + clk_put(port->clk); + goto err; + } + + return 1; + +skip: + ret = 0; + + /* In the case of skipping, we need to free these */ + devm_kfree(dev, port->reset_name); + port->reset_name = NULL; + devm_kfree(dev, port->name); + port->name = NULL; + +err: + return ret; +} + +/* + * Power up a PCIe port. 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. + */ +static int mvebu_pcie_powerup(struct mvebu_pcie_port *port) +{ + int ret; + + ret = clk_prepare_enable(port->clk); + if (ret < 0) + return ret; + + if (port->reset_gpio) { + u32 reset_udelay = PCI_PM_D3COLD_WAIT * 1000; + + of_property_read_u32(port->dn, "reset-delay-us", + &reset_udelay); + + udelay(100); + + gpiod_set_value_cansleep(port->reset_gpio, 0); + msleep(reset_udelay / 1000); + } + + return 0; +} + +/* + * Power down a PCIe port. Strictly, PCIe requires us to place the card + * in D3hot state before asserting PERST#. + */ +static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port) +{ + gpiod_set_value_cansleep(port->reset_gpio, 1); + + clk_disable_unprepare(port->clk); +} + +static int mvebu_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mvebu_pcie *pcie; + struct device_node *np = dev->of_node; + struct device_node *child; + int num, i, ret; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->pdev = pdev; + platform_set_drvdata(pdev, pcie); + + /* Get the PCIe memory and I/O aperture */ + mvebu_mbus_get_pcie_mem_aperture(&pcie->mem); + if (resource_size(&pcie->mem) == 0) { + dev_err(dev, "invalid memory aperture size\n"); + return -EINVAL; + } + + mvebu_mbus_get_pcie_io_aperture(&pcie->io); + + if (resource_size(&pcie->io) != 0) { + pcie->realio.flags = pcie->io.flags; + pcie->realio.start = PCIBIOS_MIN_IO; + pcie->realio.end = min_t(resource_size_t, + IO_SPACE_LIMIT, + resource_size(&pcie->io)); + } else + pcie->realio = pcie->io; + + /* Get the bus range */ + ret = of_pci_parse_bus_range(np, &pcie->busn); + if (ret) { + dev_err(dev, "failed to parse bus-range property: %d\n", ret); + return ret; + } + + num = of_get_available_child_count(np); + + pcie->ports = devm_kcalloc(dev, num, sizeof(*pcie->ports), GFP_KERNEL); + if (!pcie->ports) + return -ENOMEM; + + i = 0; + for_each_available_child_of_node(np, child) { + struct mvebu_pcie_port *port = &pcie->ports[i]; + + ret = mvebu_pcie_parse_port(pcie, port, child); + if (ret < 0) { + of_node_put(child); + return ret; + } else if (ret == 0) { + continue; + } + + port->dn = child; + i++; + } + pcie->nports = i; + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; + + child = port->dn; + if (!child) + continue; + + ret = mvebu_pcie_powerup(port); + if (ret < 0) + continue; + + port->base = mvebu_pcie_map_registers(pdev, child, port); + if (IS_ERR(port->base)) { + dev_err(dev, "%s: cannot map registers\n", port->name); + port->base = NULL; + mvebu_pcie_powerdown(port); + continue; + } + + mvebu_pcie_set_local_dev_nr(port, 1); + mvebu_sw_pci_bridge_init(port); + } + + pcie->nports = i; + + for (i = 0; i < (IO_SPACE_LIMIT - SZ_64K); i += SZ_64K) + pci_ioremap_io(i, pcie->io.start + i); + + mvebu_pcie_enable(pcie); + + platform_set_drvdata(pdev, pcie); + + return 0; +} + +static const struct of_device_id mvebu_pcie_of_match_table[] = { + { .compatible = "marvell,armada-xp-pcie", }, + { .compatible = "marvell,armada-370-pcie", }, + { .compatible = "marvell,dove-pcie", }, + { .compatible = "marvell,kirkwood-pcie", }, + {}, +}; + +static const struct dev_pm_ops mvebu_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mvebu_pcie_suspend, mvebu_pcie_resume) +}; + +static struct platform_driver mvebu_pcie_driver = { + .driver = { + .name = "mvebu-pcie", + .of_match_table = mvebu_pcie_of_match_table, + /* driver unloading/unbinding currently not supported */ + .suppress_bind_attrs = true, + .pm = &mvebu_pcie_pm_ops, + }, + .probe = mvebu_pcie_probe, +}; +builtin_platform_driver(mvebu_pcie_driver); diff --git a/drivers/pci/controller/pci-rcar-gen2.c b/drivers/pci/controller/pci-rcar-gen2.c new file mode 100644 index 000000000000..326171cb1a97 --- /dev/null +++ b/drivers/pci/controller/pci-rcar-gen2.c @@ -0,0 +1,428 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pci-rcar-gen2: internal PCI bus support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * Author: Valentine Barshak + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* AHB-PCI Bridge PCI communication registers */ +#define RCAR_AHBPCI_PCICOM_OFFSET 0x800 + +#define RCAR_PCIAHB_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x00) +#define RCAR_PCIAHB_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x04) +#define RCAR_PCIAHB_PREFETCH0 0x0 +#define RCAR_PCIAHB_PREFETCH4 0x1 +#define RCAR_PCIAHB_PREFETCH8 0x2 +#define RCAR_PCIAHB_PREFETCH16 0x3 + +#define RCAR_AHBPCI_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x10) +#define RCAR_AHBPCI_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x14) +#define RCAR_AHBPCI_WIN_CTR_MEM (3 << 1) +#define RCAR_AHBPCI_WIN_CTR_CFG (5 << 1) +#define RCAR_AHBPCI_WIN1_HOST (1 << 30) +#define RCAR_AHBPCI_WIN1_DEVICE (1 << 31) + +#define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20) +#define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24) +#define RCAR_PCI_INT_SIGTABORT (1 << 0) +#define RCAR_PCI_INT_SIGRETABORT (1 << 1) +#define RCAR_PCI_INT_REMABORT (1 << 2) +#define RCAR_PCI_INT_PERR (1 << 3) +#define RCAR_PCI_INT_SIGSERR (1 << 4) +#define RCAR_PCI_INT_RESERR (1 << 5) +#define RCAR_PCI_INT_WIN1ERR (1 << 12) +#define RCAR_PCI_INT_WIN2ERR (1 << 13) +#define RCAR_PCI_INT_A (1 << 16) +#define RCAR_PCI_INT_B (1 << 17) +#define RCAR_PCI_INT_PME (1 << 19) +#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_REMABORT | \ + RCAR_PCI_INT_PERR | \ + RCAR_PCI_INT_SIGSERR | \ + RCAR_PCI_INT_RESERR | \ + RCAR_PCI_INT_WIN1ERR | \ + RCAR_PCI_INT_WIN2ERR) + +#define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30) +#define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0) +#define RCAR_AHB_BUS_MMODE_BYTE_BURST (1 << 1) +#define RCAR_AHB_BUS_MMODE_WR_INCR (1 << 2) +#define RCAR_AHB_BUS_MMODE_HBUS_REQ (1 << 7) +#define RCAR_AHB_BUS_SMODE_READYCTR (1 << 17) +#define RCAR_AHB_BUS_MODE (RCAR_AHB_BUS_MMODE_HTRANS | \ + RCAR_AHB_BUS_MMODE_BYTE_BURST | \ + RCAR_AHB_BUS_MMODE_WR_INCR | \ + RCAR_AHB_BUS_MMODE_HBUS_REQ | \ + RCAR_AHB_BUS_SMODE_READYCTR) + +#define RCAR_USBCTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x34) +#define RCAR_USBCTR_USBH_RST (1 << 0) +#define RCAR_USBCTR_PCICLK_MASK (1 << 1) +#define RCAR_USBCTR_PLL_RST (1 << 2) +#define RCAR_USBCTR_DIRPD (1 << 8) +#define RCAR_USBCTR_PCIAHB_WIN2_EN (1 << 9) +#define RCAR_USBCTR_PCIAHB_WIN1_256M (0 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_512M (1 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_1G (2 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_2G (3 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_MASK (3 << 10) + +#define RCAR_PCI_ARBITER_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x40) +#define RCAR_PCI_ARBITER_PCIREQ0 (1 << 0) +#define RCAR_PCI_ARBITER_PCIREQ1 (1 << 1) +#define RCAR_PCI_ARBITER_PCIBP_MODE (1 << 12) + +#define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48) + +struct rcar_pci_priv { + struct device *dev; + void __iomem *reg; + struct resource mem_res; + struct resource *cfg_res; + unsigned busnr; + int irq; + unsigned long window_size; + unsigned long window_addr; + unsigned long window_pci; +}; + +/* PCI configuration space operations */ +static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct pci_sys_data *sys = bus->sysdata; + struct rcar_pci_priv *priv = sys->private_data; + int slot, val; + + if (sys->busnr != bus->number || PCI_FUNC(devfn)) + return NULL; + + /* Only one EHCI/OHCI device built-in */ + slot = PCI_SLOT(devfn); + if (slot > 2) + return NULL; + + /* bridge logic only has registers to 0x40 */ + if (slot == 0x0 && where >= 0x40) + return NULL; + + val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG : + RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG; + + iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG); + return priv->reg + (slot >> 1) * 0x100 + where; +} + +/* PCI interrupt mapping */ +static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct pci_sys_data *sys = dev->bus->sysdata; + struct rcar_pci_priv *priv = sys->private_data; + int irq; + + irq = of_irq_parse_and_map_pci(dev, slot, pin); + if (!irq) + irq = priv->irq; + + return irq; +} + +#ifdef CONFIG_PCI_DEBUG +/* if debug enabled, then attach an error handler irq to the bridge */ + +static irqreturn_t rcar_pci_err_irq(int irq, void *pw) +{ + struct rcar_pci_priv *priv = pw; + struct device *dev = priv->dev; + u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG); + + if (status & RCAR_PCI_INT_ALLERRORS) { + dev_err(dev, "error irq: status %08x\n", status); + + /* clear the error(s) */ + iowrite32(status & RCAR_PCI_INT_ALLERRORS, + priv->reg + RCAR_PCI_INT_STATUS_REG); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) +{ + struct device *dev = priv->dev; + int ret; + u32 val; + + ret = devm_request_irq(dev, priv->irq, rcar_pci_err_irq, + IRQF_SHARED, "error irq", priv); + if (ret) { + dev_err(dev, "cannot claim IRQ for error handling\n"); + return; + } + + val = ioread32(priv->reg + RCAR_PCI_INT_ENABLE_REG); + val |= RCAR_PCI_INT_ALLERRORS; + iowrite32(val, priv->reg + RCAR_PCI_INT_ENABLE_REG); +} +#else +static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { } +#endif + +/* PCI host controller setup */ +static int rcar_pci_setup(int nr, struct pci_sys_data *sys) +{ + struct rcar_pci_priv *priv = sys->private_data; + struct device *dev = priv->dev; + void __iomem *reg = priv->reg; + u32 val; + int ret; + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + val = ioread32(reg + RCAR_PCI_UNIT_REV_REG); + dev_info(dev, "PCI: bus%u revision %x\n", sys->busnr, val); + + /* Disable Direct Power Down State and assert reset */ + val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD; + val |= RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST; + iowrite32(val, reg + RCAR_USBCTR_REG); + udelay(4); + + /* De-assert reset and reset PCIAHB window1 size */ + val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK | + RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST); + + /* Setup PCIAHB window1 size */ + switch (priv->window_size) { + case SZ_2G: + val |= RCAR_USBCTR_PCIAHB_WIN1_2G; + break; + case SZ_1G: + val |= RCAR_USBCTR_PCIAHB_WIN1_1G; + break; + case SZ_512M: + val |= RCAR_USBCTR_PCIAHB_WIN1_512M; + break; + default: + pr_warn("unknown window size %ld - defaulting to 256M\n", + priv->window_size); + priv->window_size = SZ_256M; + /* fall-through */ + case SZ_256M: + val |= RCAR_USBCTR_PCIAHB_WIN1_256M; + break; + } + iowrite32(val, reg + RCAR_USBCTR_REG); + + /* Configure AHB master and slave modes */ + iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG); + + /* Configure PCI arbiter */ + val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG); + val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 | + RCAR_PCI_ARBITER_PCIBP_MODE; + iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); + + /* PCI-AHB mapping */ + iowrite32(priv->window_addr | RCAR_PCIAHB_PREFETCH16, + reg + RCAR_PCIAHB_WIN1_CTR_REG); + + /* AHB-PCI mapping: OHCI/EHCI registers */ + val = priv->mem_res.start | RCAR_AHBPCI_WIN_CTR_MEM; + iowrite32(val, reg + RCAR_AHBPCI_WIN2_CTR_REG); + + /* Enable AHB-PCI bridge PCI configuration access */ + iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG, + reg + RCAR_AHBPCI_WIN1_CTR_REG); + /* Set PCI-AHB Window1 address */ + iowrite32(priv->window_pci | PCI_BASE_ADDRESS_MEM_PREFETCH, + reg + PCI_BASE_ADDRESS_1); + /* Set AHB-PCI bridge PCI communication area address */ + val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET; + iowrite32(val, reg + PCI_BASE_ADDRESS_0); + + val = ioread32(reg + PCI_COMMAND); + val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + iowrite32(val, reg + PCI_COMMAND); + + /* Enable PCI interrupts */ + iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME, + reg + RCAR_PCI_INT_ENABLE_REG); + + if (priv->irq > 0) + rcar_pci_setup_errirq(priv); + + /* Add PCI resources */ + pci_add_resource(&sys->resources, &priv->mem_res); + ret = devm_request_pci_bus_resources(dev, &sys->resources); + if (ret < 0) + return ret; + + /* Setup bus number based on platform device id / of bus-range */ + sys->busnr = priv->busnr; + return 1; +} + +static struct pci_ops rcar_pci_ops = { + .map_bus = rcar_pci_cfg_base, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci, + struct device_node *np) +{ + struct device *dev = pci->dev; + struct of_pci_range range; + struct of_pci_range_parser parser; + int index = 0; + + /* Failure to parse is ok as we fall back to defaults */ + if (of_pci_dma_range_parser_init(&parser, np)) + return 0; + + /* Get the dma-ranges from DT */ + for_each_of_pci_range(&parser, &range) { + /* Hardware only allows one inbound 32-bit range */ + if (index) + return -EINVAL; + + pci->window_addr = (unsigned long)range.cpu_addr; + pci->window_pci = (unsigned long)range.pci_addr; + pci->window_size = (unsigned long)range.size; + + /* Catch HW limitations */ + if (!(range.flags & IORESOURCE_PREFETCH)) { + dev_err(dev, "window must be prefetchable\n"); + return -EINVAL; + } + if (pci->window_addr) { + u32 lowaddr = 1 << (ffs(pci->window_addr) - 1); + + if (lowaddr < pci->window_size) { + dev_err(dev, "invalid window size/addr\n"); + return -EINVAL; + } + } + index++; + } + + return 0; +} + +static int rcar_pci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *cfg_res, *mem_res; + struct rcar_pci_priv *priv; + void __iomem *reg; + struct hw_pci hw; + void *hw_private[1]; + + cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(dev, cfg_res); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!mem_res || !mem_res->start) + return -ENODEV; + + if (mem_res->start & 0xFFFF) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(struct rcar_pci_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mem_res = *mem_res; + priv->cfg_res = cfg_res; + + priv->irq = platform_get_irq(pdev, 0); + priv->reg = reg; + priv->dev = dev; + + if (priv->irq < 0) { + dev_err(dev, "no valid irq found\n"); + return priv->irq; + } + + /* default window addr and size if not specified in DT */ + priv->window_addr = 0x40000000; + priv->window_pci = 0x40000000; + priv->window_size = SZ_1G; + + if (dev->of_node) { + struct resource busnr; + int ret; + + ret = of_pci_parse_bus_range(dev->of_node, &busnr); + if (ret < 0) { + dev_err(dev, "failed to parse bus-range\n"); + return ret; + } + + priv->busnr = busnr.start; + if (busnr.end != busnr.start) + dev_warn(dev, "only one bus number supported\n"); + + ret = rcar_pci_parse_map_dma_ranges(priv, dev->of_node); + if (ret < 0) { + dev_err(dev, "failed to parse dma-range\n"); + return ret; + } + } else { + priv->busnr = pdev->id; + } + + hw_private[0] = priv; + memset(&hw, 0, sizeof(hw)); + hw.nr_controllers = ARRAY_SIZE(hw_private); + hw.io_optional = 1; + hw.private_data = hw_private; + hw.map_irq = rcar_pci_map_irq; + hw.ops = &rcar_pci_ops; + hw.setup = rcar_pci_setup; + pci_common_init_dev(dev, &hw); + return 0; +} + +static const struct of_device_id rcar_pci_of_match[] = { + { .compatible = "renesas,pci-r8a7790", }, + { .compatible = "renesas,pci-r8a7791", }, + { .compatible = "renesas,pci-r8a7794", }, + { .compatible = "renesas,pci-rcar-gen2", }, + { }, +}; + +static struct platform_driver rcar_pci_driver = { + .driver = { + .name = "pci-rcar-gen2", + .suppress_bind_attrs = true, + .of_match_table = rcar_pci_of_match, + }, + .probe = rcar_pci_probe, +}; +builtin_platform_driver(rcar_pci_driver); diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c new file mode 100644 index 000000000000..f4f53d092e00 --- /dev/null +++ b/drivers/pci/controller/pci-tegra.c @@ -0,0 +1,2531 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for Tegra SoCs + * + * Copyright (c) 2010, CompuLab, Ltd. + * Author: Mike Rapoport + * + * Based on NVIDIA PCIe driver + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * Bits taken from arch/arm/mach-dove/pcie.c + * + * Author: Thierry Reding + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../pci.h" + +#define INT_PCI_MSI_NR (8 * 32) + +/* register definitions */ + +#define AFI_AXI_BAR0_SZ 0x00 +#define AFI_AXI_BAR1_SZ 0x04 +#define AFI_AXI_BAR2_SZ 0x08 +#define AFI_AXI_BAR3_SZ 0x0c +#define AFI_AXI_BAR4_SZ 0x10 +#define AFI_AXI_BAR5_SZ 0x14 + +#define AFI_AXI_BAR0_START 0x18 +#define AFI_AXI_BAR1_START 0x1c +#define AFI_AXI_BAR2_START 0x20 +#define AFI_AXI_BAR3_START 0x24 +#define AFI_AXI_BAR4_START 0x28 +#define AFI_AXI_BAR5_START 0x2c + +#define AFI_FPCI_BAR0 0x30 +#define AFI_FPCI_BAR1 0x34 +#define AFI_FPCI_BAR2 0x38 +#define AFI_FPCI_BAR3 0x3c +#define AFI_FPCI_BAR4 0x40 +#define AFI_FPCI_BAR5 0x44 + +#define AFI_CACHE_BAR0_SZ 0x48 +#define AFI_CACHE_BAR0_ST 0x4c +#define AFI_CACHE_BAR1_SZ 0x50 +#define AFI_CACHE_BAR1_ST 0x54 + +#define AFI_MSI_BAR_SZ 0x60 +#define AFI_MSI_FPCI_BAR_ST 0x64 +#define AFI_MSI_AXI_BAR_ST 0x68 + +#define AFI_MSI_VEC0 0x6c +#define AFI_MSI_VEC1 0x70 +#define AFI_MSI_VEC2 0x74 +#define AFI_MSI_VEC3 0x78 +#define AFI_MSI_VEC4 0x7c +#define AFI_MSI_VEC5 0x80 +#define AFI_MSI_VEC6 0x84 +#define AFI_MSI_VEC7 0x88 + +#define AFI_MSI_EN_VEC0 0x8c +#define AFI_MSI_EN_VEC1 0x90 +#define AFI_MSI_EN_VEC2 0x94 +#define AFI_MSI_EN_VEC3 0x98 +#define AFI_MSI_EN_VEC4 0x9c +#define AFI_MSI_EN_VEC5 0xa0 +#define AFI_MSI_EN_VEC6 0xa4 +#define AFI_MSI_EN_VEC7 0xa8 + +#define AFI_CONFIGURATION 0xac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0xb0 + +#define AFI_INTR_MASK 0xb4 +#define AFI_INTR_MASK_INT_MASK (1 << 0) +#define AFI_INTR_MASK_MSI_MASK (1 << 8) + +#define AFI_INTR_CODE 0xb8 +#define AFI_INTR_CODE_MASK 0xf +#define AFI_INTR_INI_SLAVE_ERROR 1 +#define AFI_INTR_INI_DECODE_ERROR 2 +#define AFI_INTR_TARGET_ABORT 3 +#define AFI_INTR_MASTER_ABORT 4 +#define AFI_INTR_INVALID_WRITE 5 +#define AFI_INTR_LEGACY 6 +#define AFI_INTR_FPCI_DECODE_ERROR 7 +#define AFI_INTR_AXI_DECODE_ERROR 8 +#define AFI_INTR_FPCI_TIMEOUT 9 +#define AFI_INTR_PE_PRSNT_SENSE 10 +#define AFI_INTR_PE_CLKREQ_SENSE 11 +#define AFI_INTR_CLKCLAMP_SENSE 12 +#define AFI_INTR_RDY4PD_SENSE 13 +#define AFI_INTR_P2P_ERROR 14 + +#define AFI_INTR_SIGNATURE 0xbc +#define AFI_UPPER_FPCI_ADDRESS 0xc0 +#define AFI_SM_INTR_ENABLE 0xc4 +#define AFI_SM_INTR_INTA_ASSERT (1 << 0) +#define AFI_SM_INTR_INTB_ASSERT (1 << 1) +#define AFI_SM_INTR_INTC_ASSERT (1 << 2) +#define AFI_SM_INTR_INTD_ASSERT (1 << 3) +#define AFI_SM_INTR_INTA_DEASSERT (1 << 4) +#define AFI_SM_INTR_INTB_DEASSERT (1 << 5) +#define AFI_SM_INTR_INTC_DEASSERT (1 << 6) +#define AFI_SM_INTR_INTD_DEASSERT (1 << 7) + +#define AFI_AFI_INTR_ENABLE 0xc8 +#define AFI_INTR_EN_INI_SLVERR (1 << 0) +#define AFI_INTR_EN_INI_DECERR (1 << 1) +#define AFI_INTR_EN_TGT_SLVERR (1 << 2) +#define AFI_INTR_EN_TGT_DECERR (1 << 3) +#define AFI_INTR_EN_TGT_WRERR (1 << 4) +#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) +#define AFI_INTR_EN_AXI_DECERR (1 << 6) +#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) +#define AFI_INTR_EN_PRSNT_SENSE (1 << 8) + +#define AFI_PCIE_PME 0xf0 + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) +#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_401 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_111 (0x2 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX2_CTRL 0x128 +#define AFI_PEX_CTRL_RST (1 << 0) +#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) +#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) + +#define AFI_PLLE_CONTROL 0x160 +#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) +#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) + +#define AFI_PEXBIAS_CTRL_0 0x168 + +#define RP_VEND_XP 0x00000f00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_VEND_CTL2 0x00000fa8 +#define RP_VEND_CTL2_PCA_ENABLE (1 << 7) + +#define RP_PRIV_MISC 0x00000fe0 +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xe << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xf << 0) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +#define PADS_CTL_SEL 0x0000009c + +#define PADS_CTL 0x000000a0 +#define PADS_CTL_IDDQ_1L (1 << 0) +#define PADS_CTL_TX_DATA_EN_1L (1 << 6) +#define PADS_CTL_RX_DATA_EN_1L (1 << 10) + +#define PADS_PLL_CTL_TEGRA20 0x000000b8 +#define PADS_PLL_CTL_TEGRA30 0x000000b4 +#define PADS_PLL_CTL_RST_B4SM (1 << 1) +#define PADS_PLL_CTL_LOCKDET (1 << 8) +#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16) +#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16) +#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20) +#define PADS_PLL_CTL_TXCLKREF_BUF_EN (1 << 22) + +#define PADS_REFCLK_CFG0 0x000000c8 +#define PADS_REFCLK_CFG1 0x000000cc +#define PADS_REFCLK_BIAS 0x000000d0 + +/* + * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit + * entries, one entry per PCIe port. These field definitions and desired + * values aren't in the TRM, but do come from NVIDIA. + */ +#define PADS_REFCLK_CFG_TERM_SHIFT 2 /* 6:2 */ +#define PADS_REFCLK_CFG_E_TERM_SHIFT 7 +#define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ +#define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ + +#define PME_ACK_TIMEOUT 10000 + +struct tegra_msi { + struct msi_controller chip; + DECLARE_BITMAP(used, INT_PCI_MSI_NR); + struct irq_domain *domain; + unsigned long pages; + struct mutex lock; + u64 phys; + int irq; +}; + +/* used to differentiate between Tegra SoC generations */ +struct tegra_pcie_port_soc { + struct { + u8 turnoff_bit; + u8 ack_bit; + } pme; +}; + +struct tegra_pcie_soc { + unsigned int num_ports; + const struct tegra_pcie_port_soc *ports; + unsigned int msi_base_shift; + u32 pads_pll_ctl; + u32 tx_ref_sel; + u32 pads_refclk_cfg0; + u32 pads_refclk_cfg1; + bool has_pex_clkreq_en; + bool has_pex_bias_ctrl; + bool has_intr_prsnt_sense; + bool has_cml_clk; + bool has_gen2; + bool force_pca_enable; + bool program_uphy; +}; + +static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip) +{ + return container_of(chip, struct tegra_msi, chip); +} + +struct tegra_pcie { + struct device *dev; + + void __iomem *pads; + void __iomem *afi; + void __iomem *cfg; + int irq; + + struct resource cs; + struct resource io; + struct resource pio; + struct resource mem; + struct resource prefetch; + struct resource busn; + + struct { + resource_size_t mem; + resource_size_t io; + } offset; + + struct clk *pex_clk; + struct clk *afi_clk; + struct clk *pll_e; + struct clk *cml_clk; + + struct reset_control *pex_rst; + struct reset_control *afi_rst; + struct reset_control *pcie_xrst; + + bool legacy_phy; + struct phy *phy; + + struct tegra_msi msi; + + struct list_head ports; + u32 xbar_config; + + struct regulator_bulk_data *supplies; + unsigned int num_supplies; + + const struct tegra_pcie_soc *soc; + struct dentry *debugfs; +}; + +struct tegra_pcie_port { + struct tegra_pcie *pcie; + struct device_node *np; + struct list_head list; + struct resource regs; + void __iomem *base; + unsigned int index; + unsigned int lanes; + + struct phy **phys; +}; + +struct tegra_pcie_bus { + struct list_head list; + unsigned int nr; +}; + +static inline void afi_writel(struct tegra_pcie *pcie, u32 value, + unsigned long offset) +{ + writel(value, pcie->afi + offset); +} + +static inline u32 afi_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->afi + offset); +} + +static inline void pads_writel(struct tegra_pcie *pcie, u32 value, + unsigned long offset) +{ + writel(value, pcie->pads + offset); +} + +static inline u32 pads_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->pads + offset); +} + +/* + * The configuration space mapping on Tegra is somewhat similar to the ECAM + * defined by PCIe. However it deviates a bit in how the 4 bits for extended + * register accesses are mapped: + * + * [27:24] extended register number + * [23:16] bus number + * [15:11] device number + * [10: 8] function number + * [ 7: 0] register number + * + * Mapping the whole extended configuration space would require 256 MiB of + * virtual address space, only a small part of which will actually be used. + * + * To work around this, a 4 KiB region is used to generate the required + * configuration transaction with relevant B:D:F and register offset values. + * This is achieved by dynamically programming base address and size of + * AFI_AXI_BAR used for end point config space mapping to make sure that the + * address (access to which generates correct config transaction) falls in + * this 4 KiB region. + */ +static unsigned int tegra_pcie_conf_offset(u8 bus, unsigned int devfn, + unsigned int where) +{ + return ((where & 0xf00) << 16) | (bus << 16) | (PCI_SLOT(devfn) << 11) | + (PCI_FUNC(devfn) << 8) | (where & 0xff); +} + +static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, + unsigned int devfn, + int where) +{ + struct tegra_pcie *pcie = bus->sysdata; + void __iomem *addr = NULL; + + if (bus->number == 0) { + unsigned int slot = PCI_SLOT(devfn); + struct tegra_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) { + if (port->index + 1 == slot) { + addr = port->base + (where & ~3); + break; + } + } + } else { + unsigned int offset; + u32 base; + + offset = tegra_pcie_conf_offset(bus->number, devfn, where); + + /* move 4 KiB window to offset within the FPCI region */ + base = 0xfe100000 + ((offset & ~(SZ_4K - 1)) >> 8); + afi_writel(pcie, base, AFI_FPCI_BAR0); + + /* move to correct offset within the 4 KiB page */ + addr = pcie->cfg + (offset & (SZ_4K - 1)); + } + + return addr; +} + +static int tegra_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + if (bus->number == 0) + return pci_generic_config_read32(bus, devfn, where, size, + value); + + return pci_generic_config_read(bus, devfn, where, size, value); +} + +static int tegra_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + if (bus->number == 0) + return pci_generic_config_write32(bus, devfn, where, size, + value); + + return pci_generic_config_write(bus, devfn, where, size, value); +} + +static struct pci_ops tegra_pcie_ops = { + .map_bus = tegra_pcie_map_bus, + .read = tegra_pcie_config_read, + .write = tegra_pcie_config_write, +}; + +static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) +{ + unsigned long ret = 0; + + switch (port->index) { + case 0: + ret = AFI_PEX0_CTRL; + break; + + case 1: + ret = AFI_PEX1_CTRL; + break; + + case 2: + ret = AFI_PEX2_CTRL; + break; + } + + return ret; +} + +static void tegra_pcie_port_reset(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* pulse reset signal */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + usleep_range(1000, 2000); + + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_enable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + const struct tegra_pcie_soc *soc = port->pcie->soc; + unsigned long value; + + /* enable reference clock */ + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_REFCLK_EN; + + if (soc->has_pex_clkreq_en) + value |= AFI_PEX_CTRL_CLKREQ_EN; + + value |= AFI_PEX_CTRL_OVERRIDE_EN; + + afi_writel(port->pcie, value, ctrl); + + tegra_pcie_port_reset(port); + + if (soc->force_pca_enable) { + value = readl(port->base + RP_VEND_CTL2); + value |= RP_VEND_CTL2_PCA_ENABLE; + writel(value, port->base + RP_VEND_CTL2); + } +} + +static void tegra_pcie_port_disable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + const struct tegra_pcie_soc *soc = port->pcie->soc; + unsigned long value; + + /* assert port reset */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + /* disable reference clock */ + value = afi_readl(port->pcie, ctrl); + + if (soc->has_pex_clkreq_en) + value &= ~AFI_PEX_CTRL_CLKREQ_EN; + + value &= ~AFI_PEX_CTRL_REFCLK_EN; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_free(struct tegra_pcie_port *port) +{ + struct tegra_pcie *pcie = port->pcie; + struct device *dev = pcie->dev; + + devm_iounmap(dev, port->base); + devm_release_mem_region(dev, port->regs.start, + resource_size(&port->regs)); + list_del(&port->list); + devm_kfree(dev, port); +} + +/* Tegra PCIE root complex wrongly reports device class */ +static void tegra_pcie_fixup_class(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_fixup_class); + +/* Tegra PCIE requires relaxed ordering */ +static void tegra_pcie_relax_enable(struct pci_dev *dev) +{ + pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); + +static int tegra_pcie_request_resources(struct tegra_pcie *pcie) +{ + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct list_head *windows = &host->windows; + struct device *dev = pcie->dev; + int err; + + pci_add_resource_offset(windows, &pcie->pio, pcie->offset.io); + pci_add_resource_offset(windows, &pcie->mem, pcie->offset.mem); + pci_add_resource_offset(windows, &pcie->prefetch, pcie->offset.mem); + pci_add_resource(windows, &pcie->busn); + + err = devm_request_pci_bus_resources(dev, windows); + if (err < 0) { + pci_free_resource_list(windows); + return err; + } + + pci_remap_iospace(&pcie->pio, pcie->io.start); + + return 0; +} + +static void tegra_pcie_free_resources(struct tegra_pcie *pcie) +{ + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct list_head *windows = &host->windows; + + pci_unmap_iospace(&pcie->pio); + pci_free_resource_list(windows); +} + +static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) +{ + struct tegra_pcie *pcie = pdev->bus->sysdata; + int irq; + + tegra_cpuidle_pcie_irqs_in_use(); + + irq = of_irq_parse_and_map_pci(pdev, slot, pin); + if (!irq) + irq = pcie->irq; + + return irq; +} + +static irqreturn_t tegra_pcie_isr(int irq, void *arg) +{ + const char *err_msg[] = { + "Unknown", + "AXI slave error", + "AXI decode error", + "Target abort", + "Master abort", + "Invalid write", + "Legacy interrupt", + "Response decoding error", + "AXI response decoding error", + "Transaction timeout", + "Slot present pin change", + "Slot clock request change", + "TMS clock ramp change", + "TMS ready for power down", + "Peer2Peer error", + }; + struct tegra_pcie *pcie = arg; + struct device *dev = pcie->dev; + u32 code, signature; + + code = afi_readl(pcie, AFI_INTR_CODE) & AFI_INTR_CODE_MASK; + signature = afi_readl(pcie, AFI_INTR_SIGNATURE); + afi_writel(pcie, 0, AFI_INTR_CODE); + + if (code == AFI_INTR_LEGACY) + return IRQ_NONE; + + if (code >= ARRAY_SIZE(err_msg)) + code = 0; + + /* + * do not pollute kernel log with master abort reports since they + * happen a lot during enumeration + */ + if (code == AFI_INTR_MASTER_ABORT) + dev_dbg(dev, "%s, signature: %08x\n", err_msg[code], signature); + else + dev_err(dev, "%s, signature: %08x\n", err_msg[code], signature); + + if (code == AFI_INTR_TARGET_ABORT || code == AFI_INTR_MASTER_ABORT || + code == AFI_INTR_FPCI_DECODE_ERROR) { + u32 fpci = afi_readl(pcie, AFI_UPPER_FPCI_ADDRESS) & 0xff; + u64 address = (u64)fpci << 32 | (signature & 0xfffffffc); + + if (code == AFI_INTR_MASTER_ABORT) + dev_dbg(dev, " FPCI address: %10llx\n", address); + else + dev_err(dev, " FPCI address: %10llx\n", address); + } + + return IRQ_HANDLED; +} + +/* + * FPCI map is as follows: + * - 0xfdfc000000: I/O space + * - 0xfdfe000000: type 0 configuration space + * - 0xfdff000000: type 1 configuration space + * - 0xfe00000000: type 0 extended configuration space + * - 0xfe10000000: type 1 extended configuration space + */ +static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) +{ + u32 fpci_bar, size, axi_address; + + /* Bar 0: type 1 extended configuration space */ + size = resource_size(&pcie->cs); + afi_writel(pcie, pcie->cs.start, AFI_AXI_BAR0_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); + + /* Bar 1: downstream IO bar */ + fpci_bar = 0xfdfc0000; + size = resource_size(&pcie->io); + axi_address = pcie->io.start; + afi_writel(pcie, axi_address, AFI_AXI_BAR1_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1); + + /* Bar 2: prefetchable memory BAR */ + fpci_bar = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = resource_size(&pcie->prefetch); + axi_address = pcie->prefetch.start; + afi_writel(pcie, axi_address, AFI_AXI_BAR2_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR2); + + /* Bar 3: non prefetchable memory BAR */ + fpci_bar = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = resource_size(&pcie->mem); + axi_address = pcie->mem.start; + afi_writel(pcie, axi_address, AFI_AXI_BAR3_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR3); + + /* NULL out the remaining BARs as they are not used */ + afi_writel(pcie, 0, AFI_AXI_BAR4_START); + afi_writel(pcie, 0, AFI_AXI_BAR4_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR4); + + afi_writel(pcie, 0, AFI_AXI_BAR5_START); + afi_writel(pcie, 0, AFI_AXI_BAR5_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR5); + + /* map all upstream transactions as uncached */ + afi_writel(pcie, 0, AFI_CACHE_BAR0_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); + afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); + + /* MSI translations are setup only when needed */ + afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); + afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); +} + +static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + u32 value; + + timeout = jiffies + msecs_to_jiffies(timeout); + + while (time_before(jiffies, timeout)) { + value = pads_readl(pcie, soc->pads_pll_ctl); + if (value & PADS_PLL_CTL_LOCKDET) + return 0; + } + + return -ETIMEDOUT; +} + +static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; + u32 value; + int err; + + /* initialize internal PHY, enable up to 16 PCIE lanes */ + pads_writel(pcie, 0x0, PADS_CTL_SEL); + + /* override IDDQ to 1 on all 4 lanes */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* + * Set up PHY PLL inputs select PLLE output as refclock, + * set TX ref sel to div10 (not div5). + */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); + value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel; + pads_writel(pcie, value, soc->pads_pll_ctl); + + /* reset PLL */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + usleep_range(20, 100); + + /* take PLL out of reset */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value |= PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + /* wait for the PLL to lock */ + err = tegra_pcie_pll_wait(pcie, 500); + if (err < 0) { + dev_err(dev, "PLL failed to lock: %d\n", err); + return err; + } + + /* turn off IDDQ override */ + value = pads_readl(pcie, PADS_CTL); + value &= ~PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* enable TX/RX data */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L; + pads_writel(pcie, value, PADS_CTL); + + return 0; +} + +static int tegra_pcie_phy_disable(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + u32 value; + + /* disable TX/RX data */ + value = pads_readl(pcie, PADS_CTL); + value &= ~(PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L); + pads_writel(pcie, value, PADS_CTL); + + /* override IDDQ */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* reset PLL */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + usleep_range(20, 100); + + return 0; +} + +static int tegra_pcie_port_phy_power_on(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + unsigned int i; + int err; + + for (i = 0; i < port->lanes; i++) { + err = phy_power_on(port->phys[i]); + if (err < 0) { + dev_err(dev, "failed to power on PHY#%u: %d\n", i, err); + return err; + } + } + + return 0; +} + +static int tegra_pcie_port_phy_power_off(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + unsigned int i; + int err; + + for (i = 0; i < port->lanes; i++) { + err = phy_power_off(port->phys[i]); + if (err < 0) { + dev_err(dev, "failed to power off PHY#%u: %d\n", i, + err); + return err; + } + } + + return 0; +} + +static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; + struct tegra_pcie_port *port; + int err; + + if (pcie->legacy_phy) { + if (pcie->phy) + err = phy_power_on(pcie->phy); + else + err = tegra_pcie_phy_enable(pcie); + + if (err < 0) + dev_err(dev, "failed to power on PHY: %d\n", err); + + return err; + } + + list_for_each_entry(port, &pcie->ports, list) { + err = tegra_pcie_port_phy_power_on(port); + if (err < 0) { + dev_err(dev, + "failed to power on PCIe port %u PHY: %d\n", + port->index, err); + return err; + } + } + + /* Configure the reference clock driver */ + pads_writel(pcie, soc->pads_refclk_cfg0, PADS_REFCLK_CFG0); + + if (soc->num_ports > 2) + pads_writel(pcie, soc->pads_refclk_cfg1, PADS_REFCLK_CFG1); + + return 0; +} + +static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct tegra_pcie_port *port; + int err; + + if (pcie->legacy_phy) { + if (pcie->phy) + err = phy_power_off(pcie->phy); + else + err = tegra_pcie_phy_disable(pcie); + + if (err < 0) + dev_err(dev, "failed to power off PHY: %d\n", err); + + return err; + } + + list_for_each_entry(port, &pcie->ports, list) { + err = tegra_pcie_port_phy_power_off(port); + if (err < 0) { + dev_err(dev, + "failed to power off PCIe port %u PHY: %d\n", + port->index, err); + return err; + } + } + + return 0; +} + +static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; + struct tegra_pcie_port *port; + unsigned long value; + int err; + + /* enable PLL power down */ + if (pcie->phy) { + value = afi_readl(pcie, AFI_PLLE_CONTROL); + value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; + value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; + afi_writel(pcie, value, AFI_PLLE_CONTROL); + } + + /* power down PCIe slot clock bias pad */ + if (soc->has_pex_bias_ctrl) + afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0); + + /* configure mode and disable all ports */ + value = afi_readl(pcie, AFI_PCIE_CONFIG); + value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; + value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config; + + list_for_each_entry(port, &pcie->ports, list) + value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + + afi_writel(pcie, value, AFI_PCIE_CONFIG); + + if (soc->has_gen2) { + value = afi_readl(pcie, AFI_FUSE); + value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(pcie, value, AFI_FUSE); + } else { + value = afi_readl(pcie, AFI_FUSE); + value |= AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(pcie, value, AFI_FUSE); + } + + if (soc->program_uphy) { + err = tegra_pcie_phy_power_on(pcie); + if (err < 0) { + dev_err(dev, "failed to power on PHY(s): %d\n", err); + return err; + } + } + + /* take the PCIe interface module out of reset */ + reset_control_deassert(pcie->pcie_xrst); + + /* finally enable PCIe */ + value = afi_readl(pcie, AFI_CONFIGURATION); + value |= AFI_CONFIGURATION_EN_FPCI; + afi_writel(pcie, value, AFI_CONFIGURATION); + + value = AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | + AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR | + AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR; + + if (soc->has_intr_prsnt_sense) + value |= AFI_INTR_EN_PRSNT_SENSE; + + afi_writel(pcie, value, AFI_AFI_INTR_ENABLE); + afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE); + + /* don't enable MSI for now, only when needed */ + afi_writel(pcie, AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); + + /* disable all exceptions */ + afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS); + + return 0; +} + +static void tegra_pcie_disable_controller(struct tegra_pcie *pcie) +{ + int err; + + reset_control_assert(pcie->pcie_xrst); + + if (pcie->soc->program_uphy) { + err = tegra_pcie_phy_power_off(pcie); + if (err < 0) + dev_err(pcie->dev, "failed to power off PHY(s): %d\n", + err); + } +} + +static void tegra_pcie_power_off(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; + int err; + + reset_control_assert(pcie->afi_rst); + reset_control_assert(pcie->pex_rst); + + clk_disable_unprepare(pcie->pll_e); + if (soc->has_cml_clk) + clk_disable_unprepare(pcie->cml_clk); + clk_disable_unprepare(pcie->afi_clk); + clk_disable_unprepare(pcie->pex_clk); + + if (!dev->pm_domain) + tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + + err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies); + if (err < 0) + dev_warn(dev, "failed to disable regulators: %d\n", err); +} + +static int tegra_pcie_power_on(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; + int err; + + reset_control_assert(pcie->pcie_xrst); + reset_control_assert(pcie->afi_rst); + reset_control_assert(pcie->pex_rst); + + if (!dev->pm_domain) + tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + + /* enable regulators */ + err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies); + if (err < 0) + dev_err(dev, "failed to enable regulators: %d\n", err); + + if (dev->pm_domain) { + err = clk_prepare_enable(pcie->pex_clk); + if (err) { + dev_err(dev, "failed to enable PEX clock: %d\n", err); + return err; + } + reset_control_deassert(pcie->pex_rst); + } else { + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, + pcie->pex_clk, + pcie->pex_rst); + if (err) { + dev_err(dev, "powerup sequence failed: %d\n", err); + return err; + } + } + + reset_control_deassert(pcie->afi_rst); + + err = clk_prepare_enable(pcie->afi_clk); + if (err < 0) { + dev_err(dev, "failed to enable AFI clock: %d\n", err); + return err; + } + + if (soc->has_cml_clk) { + err = clk_prepare_enable(pcie->cml_clk); + if (err < 0) { + dev_err(dev, "failed to enable CML clock: %d\n", err); + return err; + } + } + + err = clk_prepare_enable(pcie->pll_e); + if (err < 0) { + dev_err(dev, "failed to enable PLLE clock: %d\n", err); + return err; + } + + return 0; +} + +static int tegra_pcie_clocks_get(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; + + pcie->pex_clk = devm_clk_get(dev, "pex"); + if (IS_ERR(pcie->pex_clk)) + return PTR_ERR(pcie->pex_clk); + + pcie->afi_clk = devm_clk_get(dev, "afi"); + if (IS_ERR(pcie->afi_clk)) + return PTR_ERR(pcie->afi_clk); + + pcie->pll_e = devm_clk_get(dev, "pll_e"); + if (IS_ERR(pcie->pll_e)) + return PTR_ERR(pcie->pll_e); + + if (soc->has_cml_clk) { + pcie->cml_clk = devm_clk_get(dev, "cml"); + if (IS_ERR(pcie->cml_clk)) + return PTR_ERR(pcie->cml_clk); + } + + return 0; +} + +static int tegra_pcie_resets_get(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + + pcie->pex_rst = devm_reset_control_get_exclusive(dev, "pex"); + if (IS_ERR(pcie->pex_rst)) + return PTR_ERR(pcie->pex_rst); + + pcie->afi_rst = devm_reset_control_get_exclusive(dev, "afi"); + if (IS_ERR(pcie->afi_rst)) + return PTR_ERR(pcie->afi_rst); + + pcie->pcie_xrst = devm_reset_control_get_exclusive(dev, "pcie_x"); + if (IS_ERR(pcie->pcie_xrst)) + return PTR_ERR(pcie->pcie_xrst); + + return 0; +} + +static int tegra_pcie_phys_get_legacy(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + int err; + + pcie->phy = devm_phy_optional_get(dev, "pcie"); + if (IS_ERR(pcie->phy)) { + err = PTR_ERR(pcie->phy); + dev_err(dev, "failed to get PHY: %d\n", err); + return err; + } + + err = phy_init(pcie->phy); + if (err < 0) { + dev_err(dev, "failed to initialize PHY: %d\n", err); + return err; + } + + pcie->legacy_phy = true; + + return 0; +} + +static struct phy *devm_of_phy_optional_get_index(struct device *dev, + struct device_node *np, + const char *consumer, + unsigned int index) +{ + struct phy *phy; + char *name; + + name = kasprintf(GFP_KERNEL, "%s-%u", consumer, index); + if (!name) + return ERR_PTR(-ENOMEM); + + phy = devm_of_phy_get(dev, np, name); + kfree(name); + + if (IS_ERR(phy) && PTR_ERR(phy) == -ENODEV) + phy = NULL; + + return phy; +} + +static int tegra_pcie_port_get_phys(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + struct phy *phy; + unsigned int i; + int err; + + port->phys = devm_kcalloc(dev, sizeof(phy), port->lanes, GFP_KERNEL); + if (!port->phys) + return -ENOMEM; + + for (i = 0; i < port->lanes; i++) { + phy = devm_of_phy_optional_get_index(dev, port->np, "pcie", i); + if (IS_ERR(phy)) { + dev_err(dev, "failed to get PHY#%u: %ld\n", i, + PTR_ERR(phy)); + return PTR_ERR(phy); + } + + err = phy_init(phy); + if (err < 0) { + dev_err(dev, "failed to initialize PHY#%u: %d\n", i, + err); + return err; + } + + port->phys[i] = phy; + } + + return 0; +} + +static int tegra_pcie_phys_get(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + struct device_node *np = pcie->dev->of_node; + struct tegra_pcie_port *port; + int err; + + if (!soc->has_gen2 || of_find_property(np, "phys", NULL) != NULL) + return tegra_pcie_phys_get_legacy(pcie); + + list_for_each_entry(port, &pcie->ports, list) { + err = tegra_pcie_port_get_phys(port); + if (err < 0) + return err; + } + + return 0; +} + +static void tegra_pcie_phys_put(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port; + struct device *dev = pcie->dev; + int err, i; + + if (pcie->legacy_phy) { + err = phy_exit(pcie->phy); + if (err < 0) + dev_err(dev, "failed to teardown PHY: %d\n", err); + return; + } + + list_for_each_entry(port, &pcie->ports, list) { + for (i = 0; i < port->lanes; i++) { + err = phy_exit(port->phys[i]); + if (err < 0) + dev_err(dev, "failed to teardown PHY#%u: %d\n", + i, err); + } + } +} + + +static int tegra_pcie_get_resources(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *pads, *afi, *res; + const struct tegra_pcie_soc *soc = pcie->soc; + int err; + + err = tegra_pcie_clocks_get(pcie); + if (err) { + dev_err(dev, "failed to get clocks: %d\n", err); + return err; + } + + err = tegra_pcie_resets_get(pcie); + if (err) { + dev_err(dev, "failed to get resets: %d\n", err); + return err; + } + + if (soc->program_uphy) { + err = tegra_pcie_phys_get(pcie); + if (err < 0) { + dev_err(dev, "failed to get PHYs: %d\n", err); + return err; + } + } + + pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); + pcie->pads = devm_ioremap_resource(dev, pads); + if (IS_ERR(pcie->pads)) { + err = PTR_ERR(pcie->pads); + goto phys_put; + } + + afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); + pcie->afi = devm_ioremap_resource(dev, afi); + if (IS_ERR(pcie->afi)) { + err = PTR_ERR(pcie->afi); + goto phys_put; + } + + /* request configuration space, but remap later, on demand */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); + if (!res) { + err = -EADDRNOTAVAIL; + goto phys_put; + } + + pcie->cs = *res; + + /* constrain configuration space to 4 KiB */ + pcie->cs.end = pcie->cs.start + SZ_4K - 1; + + pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); + if (IS_ERR(pcie->cfg)) { + err = PTR_ERR(pcie->cfg); + goto phys_put; + } + + /* request interrupt */ + err = platform_get_irq_byname(pdev, "intr"); + if (err < 0) { + dev_err(dev, "failed to get IRQ: %d\n", err); + goto phys_put; + } + + pcie->irq = err; + + err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); + if (err) { + dev_err(dev, "failed to register IRQ: %d\n", err); + goto phys_put; + } + + return 0; + +phys_put: + if (soc->program_uphy) + tegra_pcie_phys_put(pcie); + return err; +} + +static int tegra_pcie_put_resources(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + + if (pcie->irq > 0) + free_irq(pcie->irq, pcie); + + if (soc->program_uphy) + tegra_pcie_phys_put(pcie); + + return 0; +} + +static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port) +{ + struct tegra_pcie *pcie = port->pcie; + const struct tegra_pcie_soc *soc = pcie->soc; + int err; + u32 val; + u8 ack_bit; + + val = afi_readl(pcie, AFI_PCIE_PME); + val |= (0x1 << soc->ports[port->index].pme.turnoff_bit); + afi_writel(pcie, val, AFI_PCIE_PME); + + ack_bit = soc->ports[port->index].pme.ack_bit; + err = readl_poll_timeout(pcie->afi + AFI_PCIE_PME, val, + val & (0x1 << ack_bit), 1, PME_ACK_TIMEOUT); + if (err) + dev_err(pcie->dev, "PME Ack is not received on port: %d\n", + port->index); + + usleep_range(10000, 11000); + + val = afi_readl(pcie, AFI_PCIE_PME); + val &= ~(0x1 << soc->ports[port->index].pme.turnoff_bit); + afi_writel(pcie, val, AFI_PCIE_PME); +} + +static int tegra_msi_alloc(struct tegra_msi *chip) +{ + int msi; + + mutex_lock(&chip->lock); + + msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR); + if (msi < INT_PCI_MSI_NR) + set_bit(msi, chip->used); + else + msi = -ENOSPC; + + mutex_unlock(&chip->lock); + + return msi; +} + +static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq) +{ + struct device *dev = chip->chip.dev; + + mutex_lock(&chip->lock); + + if (!test_bit(irq, chip->used)) + dev_err(dev, "trying to free unused MSI#%lu\n", irq); + else + clear_bit(irq, chip->used); + + mutex_unlock(&chip->lock); +} + +static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) +{ + struct tegra_pcie *pcie = data; + struct device *dev = pcie->dev; + struct tegra_msi *msi = &pcie->msi; + unsigned int i, processed = 0; + + for (i = 0; i < 8; i++) { + unsigned long reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); + + while (reg) { + unsigned int offset = find_first_bit(®, 32); + unsigned int index = i * 32 + offset; + unsigned int irq; + + /* clear the interrupt */ + afi_writel(pcie, 1 << offset, AFI_MSI_VEC0 + i * 4); + + irq = irq_find_mapping(msi->domain, index); + if (irq) { + if (test_bit(index, msi->used)) + generic_handle_irq(irq); + else + dev_info(dev, "unhandled MSI\n"); + } else { + /* + * that's weird who triggered this? + * just clear it + */ + dev_info(dev, "unexpected MSI\n"); + } + + /* see if there's any more pending in this vector */ + reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); + + processed++; + } + } + + return processed > 0 ? IRQ_HANDLED : IRQ_NONE; +} + +static int tegra_msi_setup_irq(struct msi_controller *chip, + struct pci_dev *pdev, struct msi_desc *desc) +{ + struct tegra_msi *msi = to_tegra_msi(chip); + struct msi_msg msg; + unsigned int irq; + int hwirq; + + hwirq = tegra_msi_alloc(msi); + if (hwirq < 0) + return hwirq; + + irq = irq_create_mapping(msi->domain, hwirq); + if (!irq) { + tegra_msi_free(msi, hwirq); + return -EINVAL; + } + + irq_set_msi_desc(irq, desc); + + msg.address_lo = lower_32_bits(msi->phys); + msg.address_hi = upper_32_bits(msi->phys); + msg.data = hwirq; + + pci_write_msi_msg(irq, &msg); + + return 0; +} + +static void tegra_msi_teardown_irq(struct msi_controller *chip, + unsigned int irq) +{ + struct tegra_msi *msi = to_tegra_msi(chip); + struct irq_data *d = irq_get_irq_data(irq); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + irq_dispose_mapping(irq); + tegra_msi_free(msi, hwirq); +} + +static struct irq_chip tegra_msi_irq_chip = { + .name = "Tegra PCIe MSI", + .irq_enable = pci_msi_unmask_irq, + .irq_disable = pci_msi_mask_irq, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + tegra_cpuidle_pcie_irqs_in_use(); + + return 0; +} + +static const struct irq_domain_ops msi_domain_ops = { + .map = tegra_msi_map, +}; + +static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) +{ + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct platform_device *pdev = to_platform_device(pcie->dev); + struct tegra_msi *msi = &pcie->msi; + struct device *dev = pcie->dev; + int err; + + mutex_init(&msi->lock); + + msi->chip.dev = dev; + msi->chip.setup_irq = tegra_msi_setup_irq; + msi->chip.teardown_irq = tegra_msi_teardown_irq; + + msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR, + &msi_domain_ops, &msi->chip); + if (!msi->domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + err = platform_get_irq_byname(pdev, "msi"); + if (err < 0) { + dev_err(dev, "failed to get IRQ: %d\n", err); + goto err; + } + + msi->irq = err; + + err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD, + tegra_msi_irq_chip.name, pcie); + if (err < 0) { + dev_err(dev, "failed to request IRQ: %d\n", err); + goto err; + } + + /* setup AFI/FPCI range */ + msi->pages = __get_free_pages(GFP_KERNEL, 0); + msi->phys = virt_to_phys((void *)msi->pages); + host->msi = &msi->chip; + + return 0; + +err: + irq_domain_remove(msi->domain); + return err; +} + +static void tegra_pcie_enable_msi(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + struct tegra_msi *msi = &pcie->msi; + u32 reg; + + afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); + afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); + /* this register is in 4K increments */ + afi_writel(pcie, 1, AFI_MSI_BAR_SZ); + + /* enable all MSI vectors */ + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC0); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC1); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC2); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC3); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC4); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC5); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC6); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC7); + + /* and unmask the MSI interrupt */ + reg = afi_readl(pcie, AFI_INTR_MASK); + reg |= AFI_INTR_MASK_MSI_MASK; + afi_writel(pcie, reg, AFI_INTR_MASK); +} + +static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie) +{ + struct tegra_msi *msi = &pcie->msi; + unsigned int i, irq; + + free_pages(msi->pages, 0); + + if (msi->irq > 0) + free_irq(msi->irq, pcie); + + for (i = 0; i < INT_PCI_MSI_NR; i++) { + irq = irq_find_mapping(msi->domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } + + irq_domain_remove(msi->domain); +} + +static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) +{ + u32 value; + + /* mask the MSI interrupt */ + value = afi_readl(pcie, AFI_INTR_MASK); + value &= ~AFI_INTR_MASK_MSI_MASK; + afi_writel(pcie, value, AFI_INTR_MASK); + + /* disable all MSI vectors */ + afi_writel(pcie, 0, AFI_MSI_EN_VEC0); + afi_writel(pcie, 0, AFI_MSI_EN_VEC1); + afi_writel(pcie, 0, AFI_MSI_EN_VEC2); + afi_writel(pcie, 0, AFI_MSI_EN_VEC3); + afi_writel(pcie, 0, AFI_MSI_EN_VEC4); + afi_writel(pcie, 0, AFI_MSI_EN_VEC5); + afi_writel(pcie, 0, AFI_MSI_EN_VEC6); + afi_writel(pcie, 0, AFI_MSI_EN_VEC7); + + return 0; +} + +static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, + u32 *xbar) +{ + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node; + + if (of_device_is_compatible(np, "nvidia,tegra186-pcie")) { + switch (lanes) { + case 0x010004: + dev_info(dev, "4x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_401; + return 0; + + case 0x010102: + dev_info(dev, "2x1, 1X1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211; + return 0; + + case 0x010101: + dev_info(dev, "1x1, 1x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_111; + return 0; + + default: + dev_info(dev, "wrong configuration updated in DT, " + "switching to default 2x1, 1x1, 1x1 " + "configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211; + return 0; + } + } else if (of_device_is_compatible(np, "nvidia,tegra124-pcie") || + of_device_is_compatible(np, "nvidia,tegra210-pcie")) { + switch (lanes) { + case 0x0000104: + dev_info(dev, "4x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1; + return 0; + + case 0x0000102: + dev_info(dev, "2x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1; + return 0; + } + } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { + switch (lanes) { + case 0x00000204: + dev_info(dev, "4x1, 2x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420; + return 0; + + case 0x00020202: + dev_info(dev, "2x3 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222; + return 0; + + case 0x00010104: + dev_info(dev, "4x1, 1x2 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411; + return 0; + } + } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) { + switch (lanes) { + case 0x00000004: + dev_info(dev, "single-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE; + return 0; + + case 0x00000202: + dev_info(dev, "dual-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; + return 0; + } + } + + return -EINVAL; +} + +/* + * Check whether a given set of supplies is available in a device tree node. + * This is used to check whether the new or the legacy device tree bindings + * should be used. + */ +static bool of_regulator_bulk_available(struct device_node *np, + struct regulator_bulk_data *supplies, + unsigned int num_supplies) +{ + char property[32]; + unsigned int i; + + for (i = 0; i < num_supplies; i++) { + snprintf(property, 32, "%s-supply", supplies[i].supply); + + if (of_find_property(np, property, NULL) == NULL) + return false; + } + + return true; +} + +/* + * Old versions of the device tree binding for this device used a set of power + * supplies that didn't match the hardware inputs. This happened to work for a + * number of cases but is not future proof. However to preserve backwards- + * compatibility with old device trees, this function will try to use the old + * set of supplies. + */ +static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node; + + if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) + pcie->num_supplies = 3; + else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) + pcie->num_supplies = 2; + + if (pcie->num_supplies == 0) { + dev_err(dev, "device %pOF not supported in legacy mode\n", np); + return -ENODEV; + } + + pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[0].supply = "pex-clk"; + pcie->supplies[1].supply = "vdd"; + + if (pcie->num_supplies > 2) + pcie->supplies[2].supply = "avdd"; + + return devm_regulator_bulk_get(dev, pcie->num_supplies, pcie->supplies); +} + +/* + * Obtains the list of regulators required for a particular generation of the + * IP block. + * + * This would've been nice to do simply by providing static tables for use + * with the regulator_bulk_*() API, but unfortunately Tegra30 is a bit quirky + * in that it has two pairs or AVDD_PEX and VDD_PEX supplies (PEXA and PEXB) + * and either seems to be optional depending on which ports are being used. + */ +static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) +{ + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node; + unsigned int i = 0; + + if (of_device_is_compatible(np, "nvidia,tegra186-pcie")) { + pcie->num_supplies = 4; + + pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[i++].supply = "dvdd-pex"; + pcie->supplies[i++].supply = "hvdd-pex-pll"; + pcie->supplies[i++].supply = "hvdd-pex"; + pcie->supplies[i++].supply = "vddio-pexctl-aud"; + } else if (of_device_is_compatible(np, "nvidia,tegra210-pcie")) { + pcie->num_supplies = 6; + + pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[i++].supply = "avdd-pll-uerefe"; + pcie->supplies[i++].supply = "hvddio-pex"; + pcie->supplies[i++].supply = "dvddio-pex"; + pcie->supplies[i++].supply = "dvdd-pex-pll"; + pcie->supplies[i++].supply = "hvdd-pex-pll-e"; + pcie->supplies[i++].supply = "vddio-pex-ctl"; + } else if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { + pcie->num_supplies = 7; + + pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[i++].supply = "avddio-pex"; + pcie->supplies[i++].supply = "dvddio-pex"; + pcie->supplies[i++].supply = "avdd-pex-pll"; + pcie->supplies[i++].supply = "hvdd-pex"; + pcie->supplies[i++].supply = "hvdd-pex-pll-e"; + pcie->supplies[i++].supply = "vddio-pex-ctl"; + pcie->supplies[i++].supply = "avdd-pll-erefe"; + } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { + bool need_pexa = false, need_pexb = false; + + /* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */ + if (lane_mask & 0x0f) + need_pexa = true; + + /* VDD_PEXB and AVDD_PEXB supply lanes 4 to 5 */ + if (lane_mask & 0x30) + need_pexb = true; + + pcie->num_supplies = 4 + (need_pexa ? 2 : 0) + + (need_pexb ? 2 : 0); + + pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[i++].supply = "avdd-pex-pll"; + pcie->supplies[i++].supply = "hvdd-pex"; + pcie->supplies[i++].supply = "vddio-pex-ctl"; + pcie->supplies[i++].supply = "avdd-plle"; + + if (need_pexa) { + pcie->supplies[i++].supply = "avdd-pexa"; + pcie->supplies[i++].supply = "vdd-pexa"; + } + + if (need_pexb) { + pcie->supplies[i++].supply = "avdd-pexb"; + pcie->supplies[i++].supply = "vdd-pexb"; + } + } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) { + pcie->num_supplies = 5; + + pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[0].supply = "avdd-pex"; + pcie->supplies[1].supply = "vdd-pex"; + pcie->supplies[2].supply = "avdd-pex-pll"; + pcie->supplies[3].supply = "avdd-plle"; + pcie->supplies[4].supply = "vddio-pex-clk"; + } + + if (of_regulator_bulk_available(dev->of_node, pcie->supplies, + pcie->num_supplies)) + return devm_regulator_bulk_get(dev, pcie->num_supplies, + pcie->supplies); + + /* + * If not all regulators are available for this new scheme, assume + * that the device tree complies with an older version of the device + * tree binding. + */ + dev_info(dev, "using legacy DT binding for power supplies\n"); + + devm_kfree(dev, pcie->supplies); + pcie->num_supplies = 0; + + return tegra_pcie_get_legacy_regulators(pcie); +} + +static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node, *port; + const struct tegra_pcie_soc *soc = pcie->soc; + struct of_pci_range_parser parser; + struct of_pci_range range; + u32 lanes = 0, mask = 0; + unsigned int lane = 0; + struct resource res; + int err; + + if (of_pci_range_parser_init(&parser, np)) { + dev_err(dev, "missing \"ranges\" property\n"); + return -EINVAL; + } + + for_each_of_pci_range(&parser, &range) { + err = of_pci_range_to_resource(&range, np, &res); + if (err < 0) + return err; + + switch (res.flags & IORESOURCE_TYPE_BITS) { + case IORESOURCE_IO: + /* Track the bus -> CPU I/O mapping offset. */ + pcie->offset.io = res.start - range.pci_addr; + + memcpy(&pcie->pio, &res, sizeof(res)); + pcie->pio.name = np->full_name; + + /* + * The Tegra PCIe host bridge uses this to program the + * mapping of the I/O space to the physical address, + * so we override the .start and .end fields here that + * of_pci_range_to_resource() converted to I/O space. + * We also set the IORESOURCE_MEM type to clarify that + * the resource is in the physical memory space. + */ + pcie->io.start = range.cpu_addr; + pcie->io.end = range.cpu_addr + range.size - 1; + pcie->io.flags = IORESOURCE_MEM; + pcie->io.name = "I/O"; + + memcpy(&res, &pcie->io, sizeof(res)); + break; + + case IORESOURCE_MEM: + /* + * Track the bus -> CPU memory mapping offset. This + * assumes that the prefetchable and non-prefetchable + * regions will be the last of type IORESOURCE_MEM in + * the ranges property. + * */ + pcie->offset.mem = res.start - range.pci_addr; + + if (res.flags & IORESOURCE_PREFETCH) { + memcpy(&pcie->prefetch, &res, sizeof(res)); + pcie->prefetch.name = "prefetchable"; + } else { + memcpy(&pcie->mem, &res, sizeof(res)); + pcie->mem.name = "non-prefetchable"; + } + break; + } + } + + err = of_pci_parse_bus_range(np, &pcie->busn); + if (err < 0) { + dev_err(dev, "failed to parse ranges property: %d\n", err); + pcie->busn.name = np->name; + pcie->busn.start = 0; + pcie->busn.end = 0xff; + pcie->busn.flags = IORESOURCE_BUS; + } + + /* parse root ports */ + for_each_child_of_node(np, port) { + struct tegra_pcie_port *rp; + unsigned int index; + u32 value; + + err = of_pci_get_devfn(port); + if (err < 0) { + dev_err(dev, "failed to parse address: %d\n", err); + return err; + } + + index = PCI_SLOT(err); + + if (index < 1 || index > soc->num_ports) { + dev_err(dev, "invalid port number: %d\n", index); + return -EINVAL; + } + + index--; + + err = of_property_read_u32(port, "nvidia,num-lanes", &value); + if (err < 0) { + dev_err(dev, "failed to parse # of lanes: %d\n", + err); + return err; + } + + if (value > 16) { + dev_err(dev, "invalid # of lanes: %u\n", value); + return -EINVAL; + } + + lanes |= value << (index << 3); + + if (!of_device_is_available(port)) { + lane += value; + continue; + } + + mask |= ((1 << value) - 1) << lane; + lane += value; + + rp = devm_kzalloc(dev, sizeof(*rp), GFP_KERNEL); + if (!rp) + return -ENOMEM; + + err = of_address_to_resource(port, 0, &rp->regs); + if (err < 0) { + dev_err(dev, "failed to parse address: %d\n", err); + return err; + } + + INIT_LIST_HEAD(&rp->list); + rp->index = index; + rp->lanes = value; + rp->pcie = pcie; + rp->np = port; + + rp->base = devm_pci_remap_cfg_resource(dev, &rp->regs); + if (IS_ERR(rp->base)) + return PTR_ERR(rp->base); + + list_add_tail(&rp->list, &pcie->ports); + } + + err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config); + if (err < 0) { + dev_err(dev, "invalid lane configuration\n"); + return err; + } + + err = tegra_pcie_get_regulators(pcie, mask); + if (err < 0) + return err; + + return 0; +} + +/* + * FIXME: If there are no PCIe cards attached, then calling this function + * can result in the increase of the bootup time as there are big timeout + * loops. + */ +#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ +static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) +{ + struct device *dev = port->pcie->dev; + unsigned int retries = 3; + unsigned long value; + + /* override presence detection */ + value = readl(port->base + RP_PRIV_MISC); + value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; + value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; + writel(value, port->base + RP_PRIV_MISC); + + do { + unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT; + + do { + value = readl(port->base + RP_VEND_XP); + + if (value & RP_VEND_XP_DL_UP) + break; + + usleep_range(1000, 2000); + } while (--timeout); + + if (!timeout) { + dev_err(dev, "link %u down, retrying\n", port->index); + goto retry; + } + + timeout = TEGRA_PCIE_LINKUP_TIMEOUT; + + do { + value = readl(port->base + RP_LINK_CONTROL_STATUS); + + if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + return true; + + usleep_range(1000, 2000); + } while (--timeout); + +retry: + tegra_pcie_port_reset(port); + } while (--retries); + + return false; +} + +static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct tegra_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + dev_info(dev, "probing port %u, using %u lanes\n", + port->index, port->lanes); + + tegra_pcie_port_enable(port); + + if (tegra_pcie_port_check_link(port)) + continue; + + dev_info(dev, "link %u down, ignoring\n", port->index); + + tegra_pcie_port_disable(port); + tegra_pcie_port_free(port); + } +} + +static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + tegra_pcie_port_disable(port); +} + +static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, +}; + +static const struct tegra_pcie_soc tegra20_pcie = { + .num_ports = 2, + .ports = tegra20_pcie_ports, + .msi_base_shift = 0, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, + .pads_refclk_cfg0 = 0xfa5cfa5c, + .has_pex_clkreq_en = false, + .has_pex_bias_ctrl = false, + .has_intr_prsnt_sense = false, + .has_cml_clk = false, + .has_gen2 = false, + .force_pca_enable = false, + .program_uphy = true, +}; + +static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, + { .pme.turnoff_bit = 16, .pme.ack_bit = 18 }, +}; + +static const struct tegra_pcie_soc tegra30_pcie = { + .num_ports = 3, + .ports = tegra30_pcie_ports, + .msi_base_shift = 8, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .pads_refclk_cfg0 = 0xfa5cfa5c, + .pads_refclk_cfg1 = 0xfa5cfa5c, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_intr_prsnt_sense = true, + .has_cml_clk = true, + .has_gen2 = false, + .force_pca_enable = false, + .program_uphy = true, +}; + +static const struct tegra_pcie_soc tegra124_pcie = { + .num_ports = 2, + .ports = tegra20_pcie_ports, + .msi_base_shift = 8, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .pads_refclk_cfg0 = 0x44ac44ac, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_intr_prsnt_sense = true, + .has_cml_clk = true, + .has_gen2 = true, + .force_pca_enable = false, + .program_uphy = true, +}; + +static const struct tegra_pcie_soc tegra210_pcie = { + .num_ports = 2, + .ports = tegra20_pcie_ports, + .msi_base_shift = 8, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .pads_refclk_cfg0 = 0x90b890b8, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_intr_prsnt_sense = true, + .has_cml_clk = true, + .has_gen2 = true, + .force_pca_enable = true, + .program_uphy = true, +}; + +static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, + { .pme.turnoff_bit = 12, .pme.ack_bit = 14 }, +}; + +static const struct tegra_pcie_soc tegra186_pcie = { + .num_ports = 3, + .ports = tegra186_pcie_ports, + .msi_base_shift = 8, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .pads_refclk_cfg0 = 0x80b880b8, + .pads_refclk_cfg1 = 0x000480b8, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_intr_prsnt_sense = true, + .has_cml_clk = false, + .has_gen2 = true, + .force_pca_enable = false, + .program_uphy = false, +}; + +static const struct of_device_id tegra_pcie_of_match[] = { + { .compatible = "nvidia,tegra186-pcie", .data = &tegra186_pcie }, + { .compatible = "nvidia,tegra210-pcie", .data = &tegra210_pcie }, + { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie }, + { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie }, + { .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie }, + { }, +}; + +static void *tegra_pcie_ports_seq_start(struct seq_file *s, loff_t *pos) +{ + struct tegra_pcie *pcie = s->private; + + if (list_empty(&pcie->ports)) + return NULL; + + seq_printf(s, "Index Status\n"); + + return seq_list_start(&pcie->ports, *pos); +} + +static void *tegra_pcie_ports_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct tegra_pcie *pcie = s->private; + + return seq_list_next(v, &pcie->ports, pos); +} + +static void tegra_pcie_ports_seq_stop(struct seq_file *s, void *v) +{ +} + +static int tegra_pcie_ports_seq_show(struct seq_file *s, void *v) +{ + bool up = false, active = false; + struct tegra_pcie_port *port; + unsigned int value; + + port = list_entry(v, struct tegra_pcie_port, list); + + value = readl(port->base + RP_VEND_XP); + + if (value & RP_VEND_XP_DL_UP) + up = true; + + value = readl(port->base + RP_LINK_CONTROL_STATUS); + + if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + active = true; + + seq_printf(s, "%2u ", port->index); + + if (up) + seq_printf(s, "up"); + + if (active) { + if (up) + seq_printf(s, ", "); + + seq_printf(s, "active"); + } + + seq_printf(s, "\n"); + return 0; +} + +static const struct seq_operations tegra_pcie_ports_seq_ops = { + .start = tegra_pcie_ports_seq_start, + .next = tegra_pcie_ports_seq_next, + .stop = tegra_pcie_ports_seq_stop, + .show = tegra_pcie_ports_seq_show, +}; + +static int tegra_pcie_ports_open(struct inode *inode, struct file *file) +{ + struct tegra_pcie *pcie = inode->i_private; + struct seq_file *s; + int err; + + err = seq_open(file, &tegra_pcie_ports_seq_ops); + if (err) + return err; + + s = file->private_data; + s->private = pcie; + + return 0; +} + +static const struct file_operations tegra_pcie_ports_ops = { + .owner = THIS_MODULE, + .open = tegra_pcie_ports_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) +{ + debugfs_remove_recursive(pcie->debugfs); + pcie->debugfs = NULL; +} + +static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) +{ + struct dentry *file; + + pcie->debugfs = debugfs_create_dir("pcie", NULL); + if (!pcie->debugfs) + return -ENOMEM; + + file = debugfs_create_file("ports", S_IFREG | S_IRUGO, pcie->debugfs, + pcie, &tegra_pcie_ports_ops); + if (!file) + goto remove; + + return 0; + +remove: + tegra_pcie_debugfs_exit(pcie); + return -ENOMEM; +} + +static int tegra_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pci_host_bridge *host; + struct tegra_pcie *pcie; + struct pci_bus *child; + int err; + + host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!host) + return -ENOMEM; + + pcie = pci_host_bridge_priv(host); + host->sysdata = pcie; + platform_set_drvdata(pdev, pcie); + + pcie->soc = of_device_get_match_data(dev); + INIT_LIST_HEAD(&pcie->ports); + pcie->dev = dev; + + err = tegra_pcie_parse_dt(pcie); + if (err < 0) + return err; + + err = tegra_pcie_get_resources(pcie); + if (err < 0) { + dev_err(dev, "failed to request resources: %d\n", err); + return err; + } + + err = tegra_pcie_msi_setup(pcie); + if (err < 0) { + dev_err(dev, "failed to enable MSI support: %d\n", err); + goto put_resources; + } + + pm_runtime_enable(pcie->dev); + err = pm_runtime_get_sync(pcie->dev); + if (err) { + dev_err(dev, "fail to enable pcie controller: %d\n", err); + goto teardown_msi; + } + + err = tegra_pcie_request_resources(pcie); + if (err) + goto pm_runtime_put; + + host->busnr = pcie->busn.start; + host->dev.parent = &pdev->dev; + host->ops = &tegra_pcie_ops; + host->map_irq = tegra_pcie_map_irq; + host->swizzle_irq = pci_common_swizzle; + + err = pci_scan_root_bus_bridge(host); + if (err < 0) { + dev_err(dev, "failed to register host: %d\n", err); + goto free_resources; + } + + pci_bus_size_bridges(host->bus); + pci_bus_assign_resources(host->bus); + + list_for_each_entry(child, &host->bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(host->bus); + + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + err = tegra_pcie_debugfs_init(pcie); + if (err < 0) + dev_err(dev, "failed to setup debugfs: %d\n", err); + } + + return 0; + +free_resources: + tegra_pcie_free_resources(pcie); +pm_runtime_put: + pm_runtime_put_sync(pcie->dev); + pm_runtime_disable(pcie->dev); +teardown_msi: + tegra_pcie_msi_teardown(pcie); +put_resources: + tegra_pcie_put_resources(pcie); + return err; +} + +static int tegra_pcie_remove(struct platform_device *pdev) +{ + struct tegra_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct tegra_pcie_port *port, *tmp; + + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_pcie_debugfs_exit(pcie); + + pci_stop_root_bus(host->bus); + pci_remove_root_bus(host->bus); + tegra_pcie_free_resources(pcie); + pm_runtime_put_sync(pcie->dev); + pm_runtime_disable(pcie->dev); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_msi_teardown(pcie); + + tegra_pcie_put_resources(pcie); + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + tegra_pcie_port_free(port); + + return 0; +} + +static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) +{ + struct tegra_pcie *pcie = dev_get_drvdata(dev); + struct tegra_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) + tegra_pcie_pme_turnoff(port); + + tegra_pcie_disable_ports(pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_disable_msi(pcie); + + tegra_pcie_disable_controller(pcie); + tegra_pcie_power_off(pcie); + + return 0; +} + +static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) +{ + struct tegra_pcie *pcie = dev_get_drvdata(dev); + int err; + + err = tegra_pcie_power_on(pcie); + if (err) { + dev_err(dev, "tegra pcie power on fail: %d\n", err); + return err; + } + err = tegra_pcie_enable_controller(pcie); + if (err) { + dev_err(dev, "tegra pcie controller enable fail: %d\n", err); + goto poweroff; + } + tegra_pcie_setup_translations(pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_enable_msi(pcie); + + tegra_pcie_enable_ports(pcie); + + return 0; + +poweroff: + tegra_pcie_power_off(pcie); + + return err; +} + +static const struct dev_pm_ops tegra_pcie_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend, + tegra_pcie_pm_resume) +}; + +static struct platform_driver tegra_pcie_driver = { + .driver = { + .name = "tegra-pcie", + .of_match_table = tegra_pcie_of_match, + .suppress_bind_attrs = true, + .pm = &tegra_pcie_pm_ops, + }, + .probe = tegra_pcie_probe, + .remove = tegra_pcie_remove, +}; +module_platform_driver(tegra_pcie_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/pci-thunder-ecam.c b/drivers/pci/controller/pci-thunder-ecam.c new file mode 100644 index 000000000000..32d1d7b81ef4 --- /dev/null +++ b/drivers/pci/controller/pci-thunder-ecam.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015, 2016 Cavium, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_PCI_HOST_THUNDER_ECAM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) + +static void set_val(u32 v, int where, int size, u32 *val) +{ + int shift = (where & 3) * 8; + + pr_debug("set_val %04x: %08x\n", (unsigned)(where & ~3), v); + v >>= shift; + if (size == 1) + v &= 0xff; + else if (size == 2) + v &= 0xffff; + *val = v; +} + +static int handle_ea_bar(u32 e0, int bar, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val) +{ + void __iomem *addr; + u32 v; + + /* Entries are 16-byte aligned; bits[2,3] select word in entry */ + int where_a = where & 0xc; + + if (where_a == 0) { + set_val(e0, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0x4) { + addr = bus->ops->map_bus(bus, devfn, bar); /* BAR 0 */ + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + v = readl(addr); + v &= ~0xf; + v |= 2; /* EA entry-1. Base-L */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0x8) { + u32 barl_orig; + u32 barl_rb; + + addr = bus->ops->map_bus(bus, devfn, bar); /* BAR 0 */ + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + barl_orig = readl(addr + 0); + writel(0xffffffff, addr + 0); + barl_rb = readl(addr + 0); + writel(barl_orig, addr + 0); + /* zeros in unsettable bits */ + v = ~barl_rb & ~3; + v |= 0xc; /* EA entry-2. Offset-L */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xc) { + addr = bus->ops->map_bus(bus, devfn, bar + 4); /* BAR 1 */ + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + v = readl(addr); /* EA entry-3. Base-H */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct pci_config_window *cfg = bus->sysdata; + int where_a = where & ~3; + void __iomem *addr; + u32 node_bits; + u32 v; + + /* EA Base[63:32] may be missing some bits ... */ + switch (where_a) { + case 0xa8: + case 0xbc: + case 0xd0: + case 0xe4: + break; + default: + return pci_generic_config_read(bus, devfn, where, size, val); + } + + addr = bus->ops->map_bus(bus, devfn, where_a); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + v = readl(addr); + + /* + * Bit 44 of the 64-bit Base must match the same bit in + * the config space access window. Since we are working with + * the high-order 32 bits, shift everything down by 32 bits. + */ + node_bits = (cfg->res.start >> 32) & (1 << 12); + + v |= node_bits; + set_val(v, where, size, val); + + return PCIBIOS_SUCCESSFUL; +} + +static int thunder_ecam_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + u32 v; + u32 vendor_device; + u32 class_rev; + void __iomem *addr; + int cfg_type; + int where_a = where & ~3; + + addr = bus->ops->map_bus(bus, devfn, 0xc); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + v = readl(addr); + + /* Check for non type-00 header */ + cfg_type = (v >> 16) & 0x7f; + + addr = bus->ops->map_bus(bus, devfn, 8); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + class_rev = readl(addr); + if (class_rev == 0xffffffff) + goto no_emulation; + + if ((class_rev & 0xff) >= 8) { + /* Pass-2 handling */ + if (cfg_type) + goto no_emulation; + return thunder_ecam_p2_config_read(bus, devfn, where, + size, val); + } + + /* + * All BARs have fixed addresses specified by the EA + * capability; they must return zero on read. + */ + if (cfg_type == 0 && + ((where >= 0x10 && where < 0x2c) || + (where >= 0x1a4 && where < 0x1bc))) { + /* BAR or SR-IOV BAR */ + *val = 0; + return PCIBIOS_SUCCESSFUL; + } + + addr = bus->ops->map_bus(bus, devfn, 0); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + vendor_device = readl(addr); + if (vendor_device == 0xffffffff) + goto no_emulation; + + pr_debug("%04x:%04x - Fix pass#: %08x, where: %03x, devfn: %03x\n", + vendor_device & 0xffff, vendor_device >> 16, class_rev, + (unsigned) where, devfn); + + /* Check for non type-00 header */ + if (cfg_type == 0) { + bool has_msix; + bool is_nic = (vendor_device == 0xa01e177d); + bool is_tns = (vendor_device == 0xa01f177d); + + addr = bus->ops->map_bus(bus, devfn, 0x70); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + /* E_CAP */ + v = readl(addr); + has_msix = (v & 0xff00) != 0; + + if (!has_msix && where_a == 0x70) { + v |= 0xbc00; /* next capability is EA at 0xbc */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xb0) { + addr = bus->ops->map_bus(bus, devfn, where_a); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + v = readl(addr); + if (v & 0xff00) + pr_err("Bad MSIX cap header: %08x\n", v); + v |= 0xbc00; /* next capability is EA at 0xbc */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xbc) { + if (is_nic) + v = 0x40014; /* EA last in chain, 4 entries */ + else if (is_tns) + v = 0x30014; /* EA last in chain, 3 entries */ + else if (has_msix) + v = 0x20014; /* EA last in chain, 2 entries */ + else + v = 0x10014; /* EA last in chain, 1 entry */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a >= 0xc0 && where_a < 0xd0) + /* EA entry-0. PP=0, BAR0 Size:3 */ + return handle_ea_bar(0x80ff0003, + 0x10, bus, devfn, where, + size, val); + if (where_a >= 0xd0 && where_a < 0xe0 && has_msix) + /* EA entry-1. PP=0, BAR4 Size:3 */ + return handle_ea_bar(0x80ff0043, + 0x20, bus, devfn, where, + size, val); + if (where_a >= 0xe0 && where_a < 0xf0 && is_tns) + /* EA entry-2. PP=0, BAR2, Size:3 */ + return handle_ea_bar(0x80ff0023, + 0x18, bus, devfn, where, + size, val); + if (where_a >= 0xe0 && where_a < 0xf0 && is_nic) + /* EA entry-2. PP=4, VF_BAR0 (9), Size:3 */ + return handle_ea_bar(0x80ff0493, + 0x1a4, bus, devfn, where, + size, val); + if (where_a >= 0xf0 && where_a < 0x100 && is_nic) + /* EA entry-3. PP=4, VF_BAR4 (d), Size:3 */ + return handle_ea_bar(0x80ff04d3, + 0x1b4, bus, devfn, where, + size, val); + } else if (cfg_type == 1) { + bool is_rsl_bridge = devfn == 0x08; + bool is_rad_bridge = devfn == 0xa0; + bool is_zip_bridge = devfn == 0xa8; + bool is_dfa_bridge = devfn == 0xb0; + bool is_nic_bridge = devfn == 0x10; + + if (where_a == 0x70) { + addr = bus->ops->map_bus(bus, devfn, where_a); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + v = readl(addr); + if (v & 0xff00) + pr_err("Bad PCIe cap header: %08x\n", v); + v |= 0xbc00; /* next capability is EA at 0xbc */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xbc) { + if (is_nic_bridge) + v = 0x10014; /* EA last in chain, 1 entry */ + else + v = 0x00014; /* EA last in chain, no entries */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xc0) { + if (is_rsl_bridge || is_nic_bridge) + v = 0x0101; /* subordinate:secondary = 1:1 */ + else if (is_rad_bridge) + v = 0x0202; /* subordinate:secondary = 2:2 */ + else if (is_zip_bridge) + v = 0x0303; /* subordinate:secondary = 3:3 */ + else if (is_dfa_bridge) + v = 0x0404; /* subordinate:secondary = 4:4 */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xc4 && is_nic_bridge) { + /* Enabled, not-Write, SP=ff, PP=05, BEI=6, ES=4 */ + v = 0x80ff0564; + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xc8 && is_nic_bridge) { + v = 0x00000002; /* Base-L 64-bit */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xcc && is_nic_bridge) { + v = 0xfffffffe; /* MaxOffset-L 64-bit */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xd0 && is_nic_bridge) { + v = 0x00008430; /* NIC Base-H */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + if (where_a == 0xd4 && is_nic_bridge) { + v = 0x0000000f; /* MaxOffset-H */ + set_val(v, where, size, val); + return PCIBIOS_SUCCESSFUL; + } + } +no_emulation: + return pci_generic_config_read(bus, devfn, where, size, val); +} + +static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + /* + * All BARs have fixed addresses; ignore BAR writes so they + * don't get corrupted. + */ + if ((where >= 0x10 && where < 0x2c) || + (where >= 0x1a4 && where < 0x1bc)) + /* BAR or SR-IOV BAR */ + return PCIBIOS_SUCCESSFUL; + + return pci_generic_config_write(bus, devfn, where, size, val); +} + +struct pci_ecam_ops pci_thunder_ecam_ops = { + .bus_shift = 20, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = thunder_ecam_config_read, + .write = thunder_ecam_config_write, + } +}; + +#ifdef CONFIG_PCI_HOST_THUNDER_ECAM + +static const struct of_device_id thunder_ecam_of_match[] = { + { .compatible = "cavium,pci-host-thunder-ecam" }, + { }, +}; + +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, +}; +builtin_platform_driver(thunder_ecam_driver); + +#endif +#endif diff --git a/drivers/pci/controller/pci-thunder-pem.c b/drivers/pci/controller/pci-thunder-pem.c new file mode 100644 index 000000000000..f127ce8bd4ef --- /dev/null +++ b/drivers/pci/controller/pci-thunder-pem.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015 - 2016 Cavium, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../pci.h" + +#if defined(CONFIG_PCI_HOST_THUNDER_PEM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) + +#define PEM_CFG_WR 0x28 +#define PEM_CFG_RD 0x30 + +struct thunder_pem_pci { + u32 ea_entry[3]; + void __iomem *pem_reg_base; +}; + +static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + u64 read_val, tmp_val; + struct pci_config_window *cfg = bus->sysdata; + struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv; + + if (devfn != 0 || where >= 2048) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + /* + * 32-bit accesses only. Write the address to the low order + * bits of PEM_CFG_RD, then trigger the read by reading back. + * The config data lands in the upper 32-bits of PEM_CFG_RD. + */ + read_val = where & ~3ull; + writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); + read_val >>= 32; + + /* + * The config space contains some garbage, fix it up. Also + * synthesize an EA capability for the BAR used by MSI-X. + */ + switch (where & ~3) { + case 0x40: + read_val &= 0xffff00ff; + read_val |= 0x00007000; /* Skip MSI CAP */ + break; + case 0x70: /* Express Cap */ + /* + * Change PME interrupt to vector 2 on T88 where it + * reads as 0, else leave it alone. + */ + if (!(read_val & (0x1f << 25))) + read_val |= (2u << 25); + break; + case 0xb0: /* MSI-X Cap */ + /* TableSize=2 or 4, Next Cap is EA */ + read_val &= 0xc00000ff; + /* + * If Express Cap(0x70) raw PME vector reads as 0 we are on + * T88 and TableSize is reported as 4, else TableSize + * is 2. + */ + writeq(0x70, pem_pci->pem_reg_base + PEM_CFG_RD); + tmp_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); + tmp_val >>= 32; + if (!(tmp_val & (0x1f << 25))) + read_val |= 0x0003bc00; + else + read_val |= 0x0001bc00; + break; + case 0xb4: + /* Table offset=0, BIR=0 */ + read_val = 0x00000000; + break; + case 0xb8: + /* BPA offset=0xf0000, BIR=0 */ + read_val = 0x000f0000; + break; + case 0xbc: + /* EA, 1 entry, no next Cap */ + read_val = 0x00010014; + break; + case 0xc0: + /* DW2 for type-1 */ + read_val = 0x00000000; + break; + case 0xc4: + /* Entry BEI=0, PP=0x00, SP=0xff, ES=3 */ + read_val = 0x80ff0003; + break; + case 0xc8: + read_val = pem_pci->ea_entry[0]; + break; + case 0xcc: + read_val = pem_pci->ea_entry[1]; + break; + case 0xd0: + read_val = pem_pci->ea_entry[2]; + break; + default: + break; + } + read_val >>= (8 * (where & 3)); + switch (size) { + case 1: + read_val &= 0xff; + break; + case 2: + read_val &= 0xffff; + break; + default: + break; + } + *val = read_val; + return PCIBIOS_SUCCESSFUL; +} + +static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct pci_config_window *cfg = bus->sysdata; + + if (bus->number < cfg->busr.start || + bus->number > cfg->busr.end) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * The first device on the bus is the PEM PCIe bridge. + * Special case its config access. + */ + if (bus->number == cfg->busr.start) + return thunder_pem_bridge_read(bus, devfn, where, size, val); + + return pci_generic_config_read(bus, devfn, where, size, val); +} + +/* + * Some of the w1c_bits below also include read-only or non-writable + * reserved bits, this makes the code simpler and is OK as the bits + * are not affected by writing zeros to them. + */ +static u32 thunder_pem_bridge_w1c_bits(u64 where_aligned) +{ + u32 w1c_bits = 0; + + switch (where_aligned) { + case 0x04: /* Command/Status */ + case 0x1c: /* Base and I/O Limit/Secondary Status */ + w1c_bits = 0xff000000; + break; + case 0x44: /* Power Management Control and Status */ + w1c_bits = 0xfffffe00; + break; + case 0x78: /* Device Control/Device Status */ + case 0x80: /* Link Control/Link Status */ + case 0x88: /* Slot Control/Slot Status */ + case 0x90: /* Root Status */ + case 0xa0: /* Link Control 2 Registers/Link Status 2 */ + w1c_bits = 0xffff0000; + break; + case 0x104: /* Uncorrectable Error Status */ + case 0x110: /* Correctable Error Status */ + case 0x130: /* Error Status */ + case 0x160: /* Link Control 4 */ + w1c_bits = 0xffffffff; + break; + default: + break; + } + return w1c_bits; +} + +/* Some bits must be written to one so they appear to be read-only. */ +static u32 thunder_pem_bridge_w1_bits(u64 where_aligned) +{ + u32 w1_bits; + + switch (where_aligned) { + case 0x1c: /* I/O Base / I/O Limit, Secondary Status */ + /* Force 32-bit I/O addressing. */ + w1_bits = 0x0101; + break; + case 0x24: /* Prefetchable Memory Base / Prefetchable Memory Limit */ + /* Force 64-bit addressing */ + w1_bits = 0x00010001; + break; + default: + w1_bits = 0; + break; + } + return w1_bits; +} + +static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct pci_config_window *cfg = bus->sysdata; + struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv; + u64 write_val, read_val; + u64 where_aligned = where & ~3ull; + u32 mask = 0; + + + if (devfn != 0 || where >= 2048) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * 32-bit accesses only. If the write is for a size smaller + * than 32-bits, we must first read the 32-bit value and merge + * in the desired bits and then write the whole 32-bits back + * out. + */ + switch (size) { + case 1: + writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD); + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); + read_val >>= 32; + mask = ~(0xff << (8 * (where & 3))); + read_val &= mask; + val = (val & 0xff) << (8 * (where & 3)); + val |= (u32)read_val; + break; + case 2: + writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD); + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); + read_val >>= 32; + mask = ~(0xffff << (8 * (where & 3))); + read_val &= mask; + val = (val & 0xffff) << (8 * (where & 3)); + val |= (u32)read_val; + break; + default: + break; + } + + /* + * By expanding the write width to 32 bits, we may + * inadvertently hit some W1C bits that were not intended to + * be written. Calculate the mask that must be applied to the + * data to be written to avoid these cases. + */ + if (mask) { + u32 w1c_bits = thunder_pem_bridge_w1c_bits(where); + + if (w1c_bits) { + mask &= w1c_bits; + val &= ~mask; + } + } + + /* + * Some bits must be read-only with value of one. Since the + * access method allows these to be cleared if a zero is + * written, force them to one before writing. + */ + val |= thunder_pem_bridge_w1_bits(where_aligned); + + /* + * Low order bits are the config address, the high order 32 + * bits are the data to be written. + */ + write_val = (((u64)val) << 32) | where_aligned; + writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR); + return PCIBIOS_SUCCESSFUL; +} + +static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct pci_config_window *cfg = bus->sysdata; + + if (bus->number < cfg->busr.start || + bus->number > cfg->busr.end) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * The first device on the bus is the PEM PCIe bridge. + * Special case its config access. + */ + if (bus->number == cfg->busr.start) + return thunder_pem_bridge_write(bus, devfn, where, size, val); + + + return pci_generic_config_write(bus, devfn, where, size, val); +} + +static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg, + struct resource *res_pem) +{ + struct thunder_pem_pci *pem_pci; + resource_size_t bar4_start; + + pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL); + if (!pem_pci) + return -ENOMEM; + + pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000); + if (!pem_pci->pem_reg_base) + return -ENOMEM; + + /* + * The MSI-X BAR for the PEM and AER interrupts is located at + * a fixed offset from the PEM register base. Generate a + * fragment of the synthesized Enhanced Allocation capability + * structure here for the BAR. + */ + bar4_start = res_pem->start + 0xf00000; + pem_pci->ea_entry[0] = (u32)bar4_start | 2; + pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u; + pem_pci->ea_entry[2] = (u32)(bar4_start >> 32); + + cfg->priv = pem_pci; + return 0; +} + +#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) + +#define PEM_RES_BASE 0x87e0c0000000UL +#define PEM_NODE_MASK GENMASK(45, 44) +#define PEM_INDX_MASK GENMASK(26, 24) +#define PEM_MIN_DOM_IN_NODE 4 +#define PEM_MAX_DOM_IN_NODE 10 + +static void thunder_pem_reserve_range(struct device *dev, int seg, + struct resource *r) +{ + resource_size_t start = r->start, end = r->end; + struct resource *res; + const char *regionid; + + regionid = kasprintf(GFP_KERNEL, "PEM RC:%d", seg); + if (!regionid) + return; + + res = request_mem_region(start, end - start + 1, regionid); + if (res) + res->flags &= ~IORESOURCE_BUSY; + else + kfree(regionid); + + dev_info(dev, "%pR %s reserved\n", r, + res ? "has been" : "could not be"); +} + +static void thunder_pem_legacy_fw(struct acpi_pci_root *root, + struct resource *res_pem) +{ + int node = acpi_get_node(root->device->handle); + int index; + + if (node == NUMA_NO_NODE) + node = 0; + + index = root->segment - PEM_MIN_DOM_IN_NODE; + index -= node * PEM_MAX_DOM_IN_NODE; + res_pem->start = PEM_RES_BASE | FIELD_PREP(PEM_NODE_MASK, node) | + FIELD_PREP(PEM_INDX_MASK, index); + res_pem->flags = IORESOURCE_MEM; +} + +static int thunder_pem_acpi_init(struct pci_config_window *cfg) +{ + struct device *dev = cfg->parent; + struct acpi_device *adev = to_acpi_device(dev); + struct acpi_pci_root *root = acpi_driver_data(adev); + struct resource *res_pem; + int ret; + + res_pem = devm_kzalloc(&adev->dev, sizeof(*res_pem), GFP_KERNEL); + if (!res_pem) + return -ENOMEM; + + ret = acpi_get_rc_resources(dev, "CAVA02B", root->segment, res_pem); + + /* + * If we fail to gather resources it means that we run with old + * FW where we need to calculate PEM-specific resources manually. + */ + if (ret) { + thunder_pem_legacy_fw(root, res_pem); + /* + * Reserve 64K size PEM specific resources. The full 16M range + * size is required for thunder_pem_init() call. + */ + res_pem->end = res_pem->start + SZ_64K - 1; + thunder_pem_reserve_range(dev, root->segment, res_pem); + res_pem->end = res_pem->start + SZ_16M - 1; + + /* Reserve PCI configuration space as well. */ + thunder_pem_reserve_range(dev, root->segment, &cfg->res); + } + + return thunder_pem_init(dev, cfg, res_pem); +} + +struct pci_ecam_ops thunder_pem_ecam_ops = { + .bus_shift = 24, + .init = thunder_pem_acpi_init, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = thunder_pem_config_read, + .write = thunder_pem_config_write, + } +}; + +#endif + +#ifdef CONFIG_PCI_HOST_THUNDER_PEM + +static int thunder_pem_platform_init(struct pci_config_window *cfg) +{ + struct device *dev = cfg->parent; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res_pem; + + if (!dev->of_node) + return -EINVAL; + + /* + * The second register range is the PEM bridge to the PCIe + * bus. It has a different config access method than those + * devices behind the bridge. + */ + res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_pem) { + dev_err(dev, "missing \"reg[1]\"property\n"); + return -EINVAL; + } + + return thunder_pem_init(dev, cfg, res_pem); +} + +static struct pci_ecam_ops pci_thunder_pem_ops = { + .bus_shift = 24, + .init = thunder_pem_platform_init, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = thunder_pem_config_read, + .write = thunder_pem_config_write, + } +}; + +static const struct of_device_id thunder_pem_of_match[] = { + { .compatible = "cavium,pci-host-thunder-pem" }, + { }, +}; + +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, +}; +builtin_platform_driver(thunder_pem_driver); + +#endif +#endif diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c new file mode 100644 index 000000000000..68b8bfbdb867 --- /dev/null +++ b/drivers/pci/controller/pci-v3-semi.c @@ -0,0 +1,963 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for V3 Semiconductor PCI Local Bus to PCI Bridge + * Copyright (C) 2017 Linus Walleij + * + * Based on the code from arch/arm/mach-integrator/pci_v3.c + * Copyright (C) 1999 ARM Limited + * Copyright (C) 2000-2001 Deep Blue Solutions Ltd + * + * Contributors to the old driver include: + * Russell King + * David A. Rusling (uHAL, ARM Firmware suite) + * Rob Herring + * Liviu Dudau + * Grant Likely + * Arnd Bergmann + * Bjorn Helgaas + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +#define V3_PCI_VENDOR 0x00000000 +#define V3_PCI_DEVICE 0x00000002 +#define V3_PCI_CMD 0x00000004 +#define V3_PCI_STAT 0x00000006 +#define V3_PCI_CC_REV 0x00000008 +#define V3_PCI_HDR_CFG 0x0000000C +#define V3_PCI_IO_BASE 0x00000010 +#define V3_PCI_BASE0 0x00000014 +#define V3_PCI_BASE1 0x00000018 +#define V3_PCI_SUB_VENDOR 0x0000002C +#define V3_PCI_SUB_ID 0x0000002E +#define V3_PCI_ROM 0x00000030 +#define V3_PCI_BPARAM 0x0000003C +#define V3_PCI_MAP0 0x00000040 +#define V3_PCI_MAP1 0x00000044 +#define V3_PCI_INT_STAT 0x00000048 +#define V3_PCI_INT_CFG 0x0000004C +#define V3_LB_BASE0 0x00000054 +#define V3_LB_BASE1 0x00000058 +#define V3_LB_MAP0 0x0000005E +#define V3_LB_MAP1 0x00000062 +#define V3_LB_BASE2 0x00000064 +#define V3_LB_MAP2 0x00000066 +#define V3_LB_SIZE 0x00000068 +#define V3_LB_IO_BASE 0x0000006E +#define V3_FIFO_CFG 0x00000070 +#define V3_FIFO_PRIORITY 0x00000072 +#define V3_FIFO_STAT 0x00000074 +#define V3_LB_ISTAT 0x00000076 +#define V3_LB_IMASK 0x00000077 +#define V3_SYSTEM 0x00000078 +#define V3_LB_CFG 0x0000007A +#define V3_PCI_CFG 0x0000007C +#define V3_DMA_PCI_ADR0 0x00000080 +#define V3_DMA_PCI_ADR1 0x00000090 +#define V3_DMA_LOCAL_ADR0 0x00000084 +#define V3_DMA_LOCAL_ADR1 0x00000094 +#define V3_DMA_LENGTH0 0x00000088 +#define V3_DMA_LENGTH1 0x00000098 +#define V3_DMA_CSR0 0x0000008B +#define V3_DMA_CSR1 0x0000009B +#define V3_DMA_CTLB_ADR0 0x0000008C +#define V3_DMA_CTLB_ADR1 0x0000009C +#define V3_DMA_DELAY 0x000000E0 +#define V3_MAIL_DATA 0x000000C0 +#define V3_PCI_MAIL_IEWR 0x000000D0 +#define V3_PCI_MAIL_IERD 0x000000D2 +#define V3_LB_MAIL_IEWR 0x000000D4 +#define V3_LB_MAIL_IERD 0x000000D6 +#define V3_MAIL_WR_STAT 0x000000D8 +#define V3_MAIL_RD_STAT 0x000000DA +#define V3_QBA_MAP 0x000000DC + +/* PCI STATUS bits */ +#define V3_PCI_STAT_PAR_ERR BIT(15) +#define V3_PCI_STAT_SYS_ERR BIT(14) +#define V3_PCI_STAT_M_ABORT_ERR BIT(13) +#define V3_PCI_STAT_T_ABORT_ERR BIT(12) + +/* LB ISTAT bits */ +#define V3_LB_ISTAT_MAILBOX BIT(7) +#define V3_LB_ISTAT_PCI_RD BIT(6) +#define V3_LB_ISTAT_PCI_WR BIT(5) +#define V3_LB_ISTAT_PCI_INT BIT(4) +#define V3_LB_ISTAT_PCI_PERR BIT(3) +#define V3_LB_ISTAT_I2O_QWR BIT(2) +#define V3_LB_ISTAT_DMA1 BIT(1) +#define V3_LB_ISTAT_DMA0 BIT(0) + +/* PCI COMMAND bits */ +#define V3_COMMAND_M_FBB_EN BIT(9) +#define V3_COMMAND_M_SERR_EN BIT(8) +#define V3_COMMAND_M_PAR_EN BIT(6) +#define V3_COMMAND_M_MASTER_EN BIT(2) +#define V3_COMMAND_M_MEM_EN BIT(1) +#define V3_COMMAND_M_IO_EN BIT(0) + +/* SYSTEM bits */ +#define V3_SYSTEM_M_RST_OUT BIT(15) +#define V3_SYSTEM_M_LOCK BIT(14) +#define V3_SYSTEM_UNLOCK 0xa05f + +/* PCI CFG bits */ +#define V3_PCI_CFG_M_I2O_EN BIT(15) +#define V3_PCI_CFG_M_IO_REG_DIS BIT(14) +#define V3_PCI_CFG_M_IO_DIS BIT(13) +#define V3_PCI_CFG_M_EN3V BIT(12) +#define V3_PCI_CFG_M_RETRY_EN BIT(10) +#define V3_PCI_CFG_M_AD_LOW1 BIT(9) +#define V3_PCI_CFG_M_AD_LOW0 BIT(8) +/* + * This is the value applied to C/BE[3:1], with bit 0 always held 0 + * during DMA access. + */ +#define V3_PCI_CFG_M_RTYPE_SHIFT 5 +#define V3_PCI_CFG_M_WTYPE_SHIFT 1 +#define V3_PCI_CFG_TYPE_DEFAULT 0x3 + +/* PCI BASE bits (PCI -> Local Bus) */ +#define V3_PCI_BASE_M_ADR_BASE 0xFFF00000U +#define V3_PCI_BASE_M_ADR_BASEL 0x000FFF00U +#define V3_PCI_BASE_M_PREFETCH BIT(3) +#define V3_PCI_BASE_M_TYPE (3 << 1) +#define V3_PCI_BASE_M_IO BIT(0) + +/* PCI MAP bits (PCI -> Local bus) */ +#define V3_PCI_MAP_M_MAP_ADR 0xFFF00000U +#define V3_PCI_MAP_M_RD_POST_INH BIT(15) +#define V3_PCI_MAP_M_ROM_SIZE (3 << 10) +#define V3_PCI_MAP_M_SWAP (3 << 8) +#define V3_PCI_MAP_M_ADR_SIZE 0x000000F0U +#define V3_PCI_MAP_M_REG_EN BIT(1) +#define V3_PCI_MAP_M_ENABLE BIT(0) + +/* LB_BASE0,1 bits (Local bus -> PCI) */ +#define V3_LB_BASE_ADR_BASE 0xfff00000U +#define V3_LB_BASE_SWAP (3 << 8) +#define V3_LB_BASE_ADR_SIZE (15 << 4) +#define V3_LB_BASE_PREFETCH BIT(3) +#define V3_LB_BASE_ENABLE BIT(0) + +#define V3_LB_BASE_ADR_SIZE_1MB (0 << 4) +#define V3_LB_BASE_ADR_SIZE_2MB (1 << 4) +#define V3_LB_BASE_ADR_SIZE_4MB (2 << 4) +#define V3_LB_BASE_ADR_SIZE_8MB (3 << 4) +#define V3_LB_BASE_ADR_SIZE_16MB (4 << 4) +#define V3_LB_BASE_ADR_SIZE_32MB (5 << 4) +#define V3_LB_BASE_ADR_SIZE_64MB (6 << 4) +#define V3_LB_BASE_ADR_SIZE_128MB (7 << 4) +#define V3_LB_BASE_ADR_SIZE_256MB (8 << 4) +#define V3_LB_BASE_ADR_SIZE_512MB (9 << 4) +#define V3_LB_BASE_ADR_SIZE_1GB (10 << 4) +#define V3_LB_BASE_ADR_SIZE_2GB (11 << 4) + +#define v3_addr_to_lb_base(a) ((a) & V3_LB_BASE_ADR_BASE) + +/* LB_MAP0,1 bits (Local bus -> PCI) */ +#define V3_LB_MAP_MAP_ADR 0xfff0U +#define V3_LB_MAP_TYPE (7 << 1) +#define V3_LB_MAP_AD_LOW_EN BIT(0) + +#define V3_LB_MAP_TYPE_IACK (0 << 1) +#define V3_LB_MAP_TYPE_IO (1 << 1) +#define V3_LB_MAP_TYPE_MEM (3 << 1) +#define V3_LB_MAP_TYPE_CONFIG (5 << 1) +#define V3_LB_MAP_TYPE_MEM_MULTIPLE (6 << 1) + +#define v3_addr_to_lb_map(a) (((a) >> 16) & V3_LB_MAP_MAP_ADR) + +/* LB_BASE2 bits (Local bus -> PCI IO) */ +#define V3_LB_BASE2_ADR_BASE 0xff00U +#define V3_LB_BASE2_SWAP_AUTO (3 << 6) +#define V3_LB_BASE2_ENABLE BIT(0) + +#define v3_addr_to_lb_base2(a) (((a) >> 16) & V3_LB_BASE2_ADR_BASE) + +/* LB_MAP2 bits (Local bus -> PCI IO) */ +#define V3_LB_MAP2_MAP_ADR 0xff00U + +#define v3_addr_to_lb_map2(a) (((a) >> 16) & V3_LB_MAP2_MAP_ADR) + +/* FIFO priority bits */ +#define V3_FIFO_PRIO_LOCAL BIT(12) +#define V3_FIFO_PRIO_LB_RD1_FLUSH_EOB BIT(10) +#define V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 BIT(11) +#define V3_FIFO_PRIO_LB_RD1_FLUSH_ANY (BIT(10)|BIT(11)) +#define V3_FIFO_PRIO_LB_RD0_FLUSH_EOB BIT(8) +#define V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 BIT(9) +#define V3_FIFO_PRIO_LB_RD0_FLUSH_ANY (BIT(8)|BIT(9)) +#define V3_FIFO_PRIO_PCI BIT(4) +#define V3_FIFO_PRIO_PCI_RD1_FLUSH_EOB BIT(2) +#define V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 BIT(3) +#define V3_FIFO_PRIO_PCI_RD1_FLUSH_ANY (BIT(2)|BIT(3)) +#define V3_FIFO_PRIO_PCI_RD0_FLUSH_EOB BIT(0) +#define V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1 BIT(1) +#define V3_FIFO_PRIO_PCI_RD0_FLUSH_ANY (BIT(0)|BIT(1)) + +/* Local bus configuration bits */ +#define V3_LB_CFG_LB_TO_64_CYCLES 0x0000 +#define V3_LB_CFG_LB_TO_256_CYCLES BIT(13) +#define V3_LB_CFG_LB_TO_512_CYCLES BIT(14) +#define V3_LB_CFG_LB_TO_1024_CYCLES (BIT(13)|BIT(14)) +#define V3_LB_CFG_LB_RST BIT(12) +#define V3_LB_CFG_LB_PPC_RDY BIT(11) +#define V3_LB_CFG_LB_LB_INT BIT(10) +#define V3_LB_CFG_LB_ERR_EN BIT(9) +#define V3_LB_CFG_LB_RDY_EN BIT(8) +#define V3_LB_CFG_LB_BE_IMODE BIT(7) +#define V3_LB_CFG_LB_BE_OMODE BIT(6) +#define V3_LB_CFG_LB_ENDIAN BIT(5) +#define V3_LB_CFG_LB_PARK_EN BIT(4) +#define V3_LB_CFG_LB_FBB_DIS BIT(2) + +/* ARM Integrator-specific extended control registers */ +#define INTEGRATOR_SC_PCI_OFFSET 0x18 +#define INTEGRATOR_SC_PCI_ENABLE BIT(0) +#define INTEGRATOR_SC_PCI_INTCLR BIT(1) +#define INTEGRATOR_SC_LBFADDR_OFFSET 0x20 +#define INTEGRATOR_SC_LBFCODE_OFFSET 0x24 + +struct v3_pci { + struct device *dev; + void __iomem *base; + void __iomem *config_base; + struct pci_bus *bus; + u32 config_mem; + u32 io_mem; + u32 non_pre_mem; + u32 pre_mem; + phys_addr_t io_bus_addr; + phys_addr_t non_pre_bus_addr; + phys_addr_t pre_bus_addr; + struct regmap *map; +}; + +/* + * The V3 PCI interface chip in Integrator provides several windows from + * local bus memory into the PCI memory areas. Unfortunately, there + * are not really enough windows for our usage, therefore we reuse + * one of the windows for access to PCI configuration space. On the + * Integrator/AP, the memory map is as follows: + * + * Local Bus Memory Usage + * + * 40000000 - 4FFFFFFF PCI memory. 256M non-prefetchable + * 50000000 - 5FFFFFFF PCI memory. 256M prefetchable + * 60000000 - 60FFFFFF PCI IO. 16M + * 61000000 - 61FFFFFF PCI Configuration. 16M + * + * There are three V3 windows, each described by a pair of V3 registers. + * These are LB_BASE0/LB_MAP0, LB_BASE1/LB_MAP1 and LB_BASE2/LB_MAP2. + * Base0 and Base1 can be used for any type of PCI memory access. Base2 + * can be used either for PCI I/O or for I20 accesses. By default, uHAL + * uses this only for PCI IO space. + * + * Normally these spaces are mapped using the following base registers: + * + * Usage Local Bus Memory Base/Map registers used + * + * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 + * Mem 50000000 - 5FFFFFFF LB_BASE1/LB_MAP1 + * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 + * Cfg 61000000 - 61FFFFFF + * + * This means that I20 and PCI configuration space accesses will fail. + * When PCI configuration accesses are needed (via the uHAL PCI + * configuration space primitives) we must remap the spaces as follows: + * + * Usage Local Bus Memory Base/Map registers used + * + * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 + * Mem 50000000 - 5FFFFFFF LB_BASE0/LB_MAP0 + * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 + * Cfg 61000000 - 61FFFFFF LB_BASE1/LB_MAP1 + * + * To make this work, the code depends on overlapping windows working. + * The V3 chip translates an address by checking its range within + * each of the BASE/MAP pairs in turn (in ascending register number + * order). It will use the first matching pair. So, for example, + * if the same address is mapped by both LB_BASE0/LB_MAP0 and + * LB_BASE1/LB_MAP1, the V3 will use the translation from + * LB_BASE0/LB_MAP0. + * + * To allow PCI Configuration space access, the code enlarges the + * window mapped by LB_BASE0/LB_MAP0 from 256M to 512M. This occludes + * the windows currently mapped by LB_BASE1/LB_MAP1 so that it can + * be remapped for use by configuration cycles. + * + * At the end of the PCI Configuration space accesses, + * LB_BASE1/LB_MAP1 is reset to map PCI Memory. Finally the window + * mapped by LB_BASE0/LB_MAP0 is reduced in size from 512M to 256M to + * reveal the now restored LB_BASE1/LB_MAP1 window. + * + * NOTE: We do not set up I2O mapping. I suspect that this is only + * for an intelligent (target) device. Using I2O disables most of + * the mappings into PCI memory. + */ +static void __iomem *v3_map_bus(struct pci_bus *bus, + unsigned int devfn, int offset) +{ + struct v3_pci *v3 = bus->sysdata; + unsigned int address, mapaddress, busnr; + + busnr = bus->number; + if (busnr == 0) { + int slot = PCI_SLOT(devfn); + + /* + * local bus segment so need a type 0 config cycle + * + * build the PCI configuration "address" with one-hot in + * A31-A11 + * + * mapaddress: + * 3:1 = config cycle (101) + * 0 = PCI A1 & A0 are 0 (0) + */ + address = PCI_FUNC(devfn) << 8; + mapaddress = V3_LB_MAP_TYPE_CONFIG; + + if (slot > 12) + /* + * high order bits are handled by the MAP register + */ + mapaddress |= BIT(slot - 5); + else + /* + * low order bits handled directly in the address + */ + address |= BIT(slot + 11); + } else { + /* + * not the local bus segment so need a type 1 config cycle + * + * address: + * 23:16 = bus number + * 15:11 = slot number (7:3 of devfn) + * 10:8 = func number (2:0 of devfn) + * + * mapaddress: + * 3:1 = config cycle (101) + * 0 = PCI A1 & A0 from host bus (1) + */ + mapaddress = V3_LB_MAP_TYPE_CONFIG | V3_LB_MAP_AD_LOW_EN; + address = (busnr << 16) | (devfn << 8); + } + + /* + * Set up base0 to see all 512Mbytes of memory space (not + * prefetchable), this frees up base1 for re-use by + * configuration memory + */ + writel(v3_addr_to_lb_base(v3->non_pre_mem) | + V3_LB_BASE_ADR_SIZE_512MB | V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE0); + + /* + * Set up base1/map1 to point into configuration space. + * The config mem is always 16MB. + */ + writel(v3_addr_to_lb_base(v3->config_mem) | + V3_LB_BASE_ADR_SIZE_16MB | V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE1); + writew(mapaddress, v3->base + V3_LB_MAP1); + + return v3->config_base + address + offset; +} + +static void v3_unmap_bus(struct v3_pci *v3) +{ + /* + * Reassign base1 for use by prefetchable PCI memory + */ + writel(v3_addr_to_lb_base(v3->pre_mem) | + V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_PREFETCH | + V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE1); + writew(v3_addr_to_lb_map(v3->pre_bus_addr) | + V3_LB_MAP_TYPE_MEM, /* was V3_LB_MAP_TYPE_MEM_MULTIPLE */ + v3->base + V3_LB_MAP1); + + /* + * And shrink base0 back to a 256M window (NOTE: MAP0 already correct) + */ + writel(v3_addr_to_lb_base(v3->non_pre_mem) | + V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE0); +} + +static int v3_pci_read_config(struct pci_bus *bus, unsigned int fn, + int config, int size, u32 *value) +{ + struct v3_pci *v3 = bus->sysdata; + int ret; + + dev_dbg(&bus->dev, + "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); + ret = pci_generic_config_read(bus, fn, config, size, value); + v3_unmap_bus(v3); + return ret; +} + +static int v3_pci_write_config(struct pci_bus *bus, unsigned int fn, + int config, int size, u32 value) +{ + struct v3_pci *v3 = bus->sysdata; + int ret; + + dev_dbg(&bus->dev, + "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); + ret = pci_generic_config_write(bus, fn, config, size, value); + v3_unmap_bus(v3); + return ret; +} + +static struct pci_ops v3_pci_ops = { + .map_bus = v3_map_bus, + .read = v3_pci_read_config, + .write = v3_pci_write_config, +}; + +static irqreturn_t v3_irq(int irq, void *data) +{ + struct v3_pci *v3 = data; + struct device *dev = v3->dev; + u32 status; + + status = readw(v3->base + V3_PCI_STAT); + if (status & V3_PCI_STAT_PAR_ERR) + dev_err(dev, "parity error interrupt\n"); + if (status & V3_PCI_STAT_SYS_ERR) + dev_err(dev, "system error interrupt\n"); + if (status & V3_PCI_STAT_M_ABORT_ERR) + dev_err(dev, "master abort error interrupt\n"); + if (status & V3_PCI_STAT_T_ABORT_ERR) + dev_err(dev, "target abort error interrupt\n"); + writew(status, v3->base + V3_PCI_STAT); + + status = readb(v3->base + V3_LB_ISTAT); + if (status & V3_LB_ISTAT_MAILBOX) + dev_info(dev, "PCI mailbox interrupt\n"); + if (status & V3_LB_ISTAT_PCI_RD) + dev_err(dev, "PCI target LB->PCI READ abort interrupt\n"); + if (status & V3_LB_ISTAT_PCI_WR) + dev_err(dev, "PCI target LB->PCI WRITE abort interrupt\n"); + if (status & V3_LB_ISTAT_PCI_INT) + dev_info(dev, "PCI pin interrupt\n"); + if (status & V3_LB_ISTAT_PCI_PERR) + dev_err(dev, "PCI parity error interrupt\n"); + if (status & V3_LB_ISTAT_I2O_QWR) + dev_info(dev, "I2O inbound post queue interrupt\n"); + if (status & V3_LB_ISTAT_DMA1) + dev_info(dev, "DMA channel 1 interrupt\n"); + if (status & V3_LB_ISTAT_DMA0) + dev_info(dev, "DMA channel 0 interrupt\n"); + /* Clear all possible interrupts on the local bus */ + writeb(0, v3->base + V3_LB_ISTAT); + if (v3->map) + regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET, + INTEGRATOR_SC_PCI_ENABLE | + INTEGRATOR_SC_PCI_INTCLR); + + return IRQ_HANDLED; +} + +static int v3_integrator_init(struct v3_pci *v3) +{ + unsigned int val; + + v3->map = + syscon_regmap_lookup_by_compatible("arm,integrator-ap-syscon"); + if (IS_ERR(v3->map)) { + dev_err(v3->dev, "no syscon\n"); + return -ENODEV; + } + + regmap_read(v3->map, INTEGRATOR_SC_PCI_OFFSET, &val); + /* Take the PCI bridge out of reset, clear IRQs */ + regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET, + INTEGRATOR_SC_PCI_ENABLE | + INTEGRATOR_SC_PCI_INTCLR); + + if (!(val & INTEGRATOR_SC_PCI_ENABLE)) { + /* If we were in reset we need to sleep a bit */ + msleep(230); + + /* Set the physical base for the controller itself */ + writel(0x6200, v3->base + V3_LB_IO_BASE); + + /* Wait for the mailbox to settle after reset */ + do { + writeb(0xaa, v3->base + V3_MAIL_DATA); + writeb(0x55, v3->base + V3_MAIL_DATA + 4); + } while (readb(v3->base + V3_MAIL_DATA) != 0xaa && + readb(v3->base + V3_MAIL_DATA) != 0x55); + } + + dev_info(v3->dev, "initialized PCI V3 Integrator/AP integration\n"); + + return 0; +} + +static int v3_pci_setup_resource(struct v3_pci *v3, + resource_size_t io_base, + struct pci_host_bridge *host, + struct resource_entry *win) +{ + struct device *dev = v3->dev; + struct resource *mem; + struct resource *io; + int ret; + + switch (resource_type(win->res)) { + case IORESOURCE_IO: + io = win->res; + io->name = "V3 PCI I/O"; + v3->io_mem = io_base; + v3->io_bus_addr = io->start - win->offset; + dev_dbg(dev, "I/O window %pR, bus addr %pap\n", + io, &v3->io_bus_addr); + ret = pci_remap_iospace(io, io_base); + if (ret) { + dev_warn(dev, + "error %d: failed to map resource %pR\n", + ret, io); + return ret; + } + /* Setup window 2 - PCI I/O */ + writel(v3_addr_to_lb_base2(v3->io_mem) | + V3_LB_BASE2_ENABLE, + v3->base + V3_LB_BASE2); + writew(v3_addr_to_lb_map2(v3->io_bus_addr), + v3->base + V3_LB_MAP2); + break; + case IORESOURCE_MEM: + mem = win->res; + if (mem->flags & IORESOURCE_PREFETCH) { + mem->name = "V3 PCI PRE-MEM"; + v3->pre_mem = mem->start; + v3->pre_bus_addr = mem->start - win->offset; + dev_dbg(dev, "PREFETCHABLE MEM window %pR, bus addr %pap\n", + mem, &v3->pre_bus_addr); + if (resource_size(mem) != SZ_256M) { + dev_err(dev, "prefetchable memory range is not 256MB\n"); + return -EINVAL; + } + if (v3->non_pre_mem && + (mem->start != v3->non_pre_mem + SZ_256M)) { + dev_err(dev, + "prefetchable memory is not adjacent to non-prefetchable memory\n"); + return -EINVAL; + } + /* Setup window 1 - PCI prefetchable memory */ + writel(v3_addr_to_lb_base(v3->pre_mem) | + V3_LB_BASE_ADR_SIZE_256MB | + V3_LB_BASE_PREFETCH | + V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE1); + writew(v3_addr_to_lb_map(v3->pre_bus_addr) | + V3_LB_MAP_TYPE_MEM, /* Was V3_LB_MAP_TYPE_MEM_MULTIPLE */ + v3->base + V3_LB_MAP1); + } else { + mem->name = "V3 PCI NON-PRE-MEM"; + v3->non_pre_mem = mem->start; + v3->non_pre_bus_addr = mem->start - win->offset; + dev_dbg(dev, "NON-PREFETCHABLE MEM window %pR, bus addr %pap\n", + mem, &v3->non_pre_bus_addr); + if (resource_size(mem) != SZ_256M) { + dev_err(dev, + "non-prefetchable memory range is not 256MB\n"); + return -EINVAL; + } + /* Setup window 0 - PCI non-prefetchable memory */ + writel(v3_addr_to_lb_base(v3->non_pre_mem) | + V3_LB_BASE_ADR_SIZE_256MB | + V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE0); + writew(v3_addr_to_lb_map(v3->non_pre_bus_addr) | + V3_LB_MAP_TYPE_MEM, + v3->base + V3_LB_MAP0); + } + break; + case IORESOURCE_BUS: + dev_dbg(dev, "BUS %pR\n", win->res); + host->busnr = win->res->start; + break; + default: + dev_info(dev, "Unknown resource type %lu\n", + resource_type(win->res)); + break; + } + + return 0; +} + +static int v3_get_dma_range_config(struct v3_pci *v3, + struct of_pci_range *range, + u32 *pci_base, u32 *pci_map) +{ + struct device *dev = v3->dev; + u64 cpu_end = range->cpu_addr + range->size - 1; + u64 pci_end = range->pci_addr + range->size - 1; + u32 val; + + if (range->pci_addr & ~V3_PCI_BASE_M_ADR_BASE) { + dev_err(dev, "illegal range, only PCI bits 31..20 allowed\n"); + return -EINVAL; + } + val = ((u32)range->pci_addr) & V3_PCI_BASE_M_ADR_BASE; + *pci_base = val; + + if (range->cpu_addr & ~V3_PCI_MAP_M_MAP_ADR) { + dev_err(dev, "illegal range, only CPU bits 31..20 allowed\n"); + return -EINVAL; + } + val = ((u32)range->cpu_addr) & V3_PCI_MAP_M_MAP_ADR; + + switch (range->size) { + case SZ_1M: + val |= V3_LB_BASE_ADR_SIZE_1MB; + break; + case SZ_2M: + val |= V3_LB_BASE_ADR_SIZE_2MB; + break; + case SZ_4M: + val |= V3_LB_BASE_ADR_SIZE_4MB; + break; + case SZ_8M: + val |= V3_LB_BASE_ADR_SIZE_8MB; + break; + case SZ_16M: + val |= V3_LB_BASE_ADR_SIZE_16MB; + break; + case SZ_32M: + val |= V3_LB_BASE_ADR_SIZE_32MB; + break; + case SZ_64M: + val |= V3_LB_BASE_ADR_SIZE_64MB; + break; + case SZ_128M: + val |= V3_LB_BASE_ADR_SIZE_128MB; + break; + case SZ_256M: + val |= V3_LB_BASE_ADR_SIZE_256MB; + break; + case SZ_512M: + val |= V3_LB_BASE_ADR_SIZE_512MB; + break; + case SZ_1G: + val |= V3_LB_BASE_ADR_SIZE_1GB; + break; + case SZ_2G: + val |= V3_LB_BASE_ADR_SIZE_2GB; + break; + default: + dev_err(v3->dev, "illegal dma memory chunk size\n"); + return -EINVAL; + break; + } + val |= V3_PCI_MAP_M_REG_EN | V3_PCI_MAP_M_ENABLE; + *pci_map = val; + + dev_dbg(dev, + "DMA MEM CPU: 0x%016llx -> 0x%016llx => " + "PCI: 0x%016llx -> 0x%016llx base %08x map %08x\n", + range->cpu_addr, cpu_end, + range->pci_addr, pci_end, + *pci_base, *pci_map); + + return 0; +} + +static int v3_pci_parse_map_dma_ranges(struct v3_pci *v3, + struct device_node *np) +{ + struct of_pci_range range; + struct of_pci_range_parser parser; + struct device *dev = v3->dev; + int i = 0; + + if (of_pci_dma_range_parser_init(&parser, np)) { + dev_err(dev, "missing dma-ranges property\n"); + return -EINVAL; + } + + /* + * Get the dma-ranges from the device tree + */ + for_each_of_pci_range(&parser, &range) { + int ret; + u32 pci_base, pci_map; + + ret = v3_get_dma_range_config(v3, &range, &pci_base, &pci_map); + if (ret) + return ret; + + if (i == 0) { + writel(pci_base, v3->base + V3_PCI_BASE0); + writel(pci_map, v3->base + V3_PCI_MAP0); + } else if (i == 1) { + writel(pci_base, v3->base + V3_PCI_BASE1); + writel(pci_map, v3->base + V3_PCI_MAP1); + } else { + dev_err(dev, "too many ranges, only two supported\n"); + dev_err(dev, "range %d ignored\n", i); + } + i++; + } + return 0; +} + +static int v3_pci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + resource_size_t io_base; + struct resource *regs; + struct resource_entry *win; + struct v3_pci *v3; + struct pci_host_bridge *host; + struct clk *clk; + u16 val; + int irq; + int ret; + LIST_HEAD(res); + + host = pci_alloc_host_bridge(sizeof(*v3)); + if (!host) + return -ENOMEM; + + host->dev.parent = dev; + host->ops = &v3_pci_ops; + host->busnr = 0; + host->msi = NULL; + host->map_irq = of_irq_parse_and_map_pci; + host->swizzle_irq = pci_common_swizzle; + v3 = pci_host_bridge_priv(host); + host->sysdata = v3; + v3->dev = dev; + + /* Get and enable host clock */ + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + dev_err(dev, "clock not found\n"); + return PTR_ERR(clk); + } + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "unable to enable clock\n"); + return ret; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + v3->base = devm_ioremap_resource(dev, regs); + if (IS_ERR(v3->base)) + return PTR_ERR(v3->base); + /* + * The hardware has a register with the physical base address + * of the V3 controller itself, verify that this is the same + * as the physical memory we've remapped it from. + */ + if (readl(v3->base + V3_LB_IO_BASE) != (regs->start >> 16)) + dev_err(dev, "V3_LB_IO_BASE = %08x but device is @%pR\n", + readl(v3->base + V3_LB_IO_BASE), regs); + + /* Configuration space is 16MB directly mapped */ + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (resource_size(regs) != SZ_16M) { + dev_err(dev, "config mem is not 16MB!\n"); + return -EINVAL; + } + v3->config_mem = regs->start; + v3->config_base = devm_ioremap_resource(dev, regs); + if (IS_ERR(v3->config_base)) + return PTR_ERR(v3->config_base); + + ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, + &io_base); + if (ret) + return ret; + + ret = devm_request_pci_bus_resources(dev, &res); + if (ret) + return ret; + + /* Get and request error IRQ resource */ + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "unable to obtain PCIv3 error IRQ\n"); + return -ENODEV; + } + ret = devm_request_irq(dev, irq, v3_irq, 0, + "PCIv3 error", v3); + if (ret < 0) { + dev_err(dev, + "unable to request PCIv3 error IRQ %d (%d)\n", + irq, ret); + return ret; + } + + /* + * Unlock V3 registers, but only if they were previously locked. + */ + if (readw(v3->base + V3_SYSTEM) & V3_SYSTEM_M_LOCK) + writew(V3_SYSTEM_UNLOCK, v3->base + V3_SYSTEM); + + /* Disable all slave access while we set up the windows */ + val = readw(v3->base + V3_PCI_CMD); + val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + writew(val, v3->base + V3_PCI_CMD); + + /* Put the PCI bus into reset */ + val = readw(v3->base + V3_SYSTEM); + val &= ~V3_SYSTEM_M_RST_OUT; + writew(val, v3->base + V3_SYSTEM); + + /* Retry until we're ready */ + val = readw(v3->base + V3_PCI_CFG); + val |= V3_PCI_CFG_M_RETRY_EN; + writew(val, v3->base + V3_PCI_CFG); + + /* Set up the local bus protocol */ + val = readw(v3->base + V3_LB_CFG); + val |= V3_LB_CFG_LB_BE_IMODE; /* Byte enable input */ + val |= V3_LB_CFG_LB_BE_OMODE; /* Byte enable output */ + val &= ~V3_LB_CFG_LB_ENDIAN; /* Little endian */ + val &= ~V3_LB_CFG_LB_PPC_RDY; /* TODO: when using on PPC403Gx, set to 1 */ + writew(val, v3->base + V3_LB_CFG); + + /* Enable the PCI bus master */ + val = readw(v3->base + V3_PCI_CMD); + val |= PCI_COMMAND_MASTER; + writew(val, v3->base + V3_PCI_CMD); + + /* Get the I/O and memory ranges from DT */ + resource_list_for_each_entry(win, &res) { + ret = v3_pci_setup_resource(v3, io_base, host, win); + if (ret) { + dev_err(dev, "error setting up resources\n"); + return ret; + } + } + ret = v3_pci_parse_map_dma_ranges(v3, np); + if (ret) + return ret; + + /* + * Disable PCI to host IO cycles, enable I/O buffers @3.3V, + * set AD_LOW0 to 1 if one of the LB_MAP registers choose + * to use this (should be unused). + */ + writel(0x00000000, v3->base + V3_PCI_IO_BASE); + val = V3_PCI_CFG_M_IO_REG_DIS | V3_PCI_CFG_M_IO_DIS | + V3_PCI_CFG_M_EN3V | V3_PCI_CFG_M_AD_LOW0; + /* + * DMA read and write from PCI bus commands types + */ + val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_RTYPE_SHIFT; + val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_WTYPE_SHIFT; + writew(val, v3->base + V3_PCI_CFG); + + /* + * Set the V3 FIFO such that writes have higher priority than + * reads, and local bus write causes local bus read fifo flush + * on aperture 1. Same for PCI. + */ + writew(V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 | + V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 | + V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 | + V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1, + v3->base + V3_FIFO_PRIORITY); + + + /* + * Clear any error interrupts, and enable parity and write error + * interrupts + */ + writeb(0, v3->base + V3_LB_ISTAT); + val = readw(v3->base + V3_LB_CFG); + val |= V3_LB_CFG_LB_LB_INT; + writew(val, v3->base + V3_LB_CFG); + writeb(V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR, + v3->base + V3_LB_IMASK); + + /* Special Integrator initialization */ + if (of_device_is_compatible(np, "arm,integrator-ap-pci")) { + ret = v3_integrator_init(v3); + if (ret) + return ret; + } + + /* Post-init: enable PCI memory and invalidate (master already on) */ + val = readw(v3->base + V3_PCI_CMD); + val |= PCI_COMMAND_MEMORY | PCI_COMMAND_INVALIDATE; + writew(val, v3->base + V3_PCI_CMD); + + /* Clear pending interrupts */ + writeb(0, v3->base + V3_LB_ISTAT); + /* Read or write errors and parity errors cause interrupts */ + writeb(V3_LB_ISTAT_PCI_RD | V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR, + v3->base + V3_LB_IMASK); + + /* Take the PCI bus out of reset so devices can initialize */ + val = readw(v3->base + V3_SYSTEM); + val |= V3_SYSTEM_M_RST_OUT; + writew(val, v3->base + V3_SYSTEM); + + /* + * Re-lock the system register. + */ + val = readw(v3->base + V3_SYSTEM); + val |= V3_SYSTEM_M_LOCK; + writew(val, v3->base + V3_SYSTEM); + + list_splice_init(&res, &host->windows); + ret = pci_scan_root_bus_bridge(host); + if (ret) { + dev_err(dev, "failed to register host: %d\n", ret); + return ret; + } + v3->bus = host->bus; + + pci_bus_assign_resources(v3->bus); + pci_bus_add_devices(v3->bus); + + return 0; +} + +static const struct of_device_id v3_pci_of_match[] = { + { + .compatible = "v3,v360epc-pci", + }, + {}, +}; + +static struct platform_driver v3_pci_driver = { + .driver = { + .name = "pci-v3-semi", + .of_match_table = of_match_ptr(v3_pci_of_match), + .suppress_bind_attrs = true, + }, + .probe = v3_pci_probe, +}; +builtin_platform_driver(v3_pci_driver); diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c new file mode 100644 index 000000000000..994f32061b32 --- /dev/null +++ b/drivers/pci/controller/pci-versatile.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2004 Koninklijke Philips Electronics NV + * + * Conversion to platform driver and DT: + * Copyright 2014 Linaro Ltd. + * + * 14/04/2005 Initial version, colin.king@philips.com + */ +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +static void __iomem *versatile_pci_base; +static void __iomem *versatile_cfg_base[2]; + +#define PCI_IMAP(m) (versatile_pci_base + ((m) * 4)) +#define PCI_SMAP(m) (versatile_pci_base + 0x14 + ((m) * 4)) +#define PCI_SELFID (versatile_pci_base + 0xc) + +#define VP_PCI_DEVICE_ID 0x030010ee +#define VP_PCI_CLASS_ID 0x0b400000 + +static u32 pci_slot_ignore; + +static int __init versatile_pci_slot_ignore(char *str) +{ + int retval; + int slot; + + while ((retval = get_option(&str, &slot))) { + if ((slot < 0) || (slot > 31)) + pr_err("Illegal slot value: %d\n", slot); + else + pci_slot_ignore |= (1 << slot); + } + return 1; +} +__setup("pci_slot_ignore=", versatile_pci_slot_ignore); + + +static void __iomem *versatile_map_bus(struct pci_bus *bus, + unsigned int devfn, int offset) +{ + unsigned int busnr = bus->number; + + if (pci_slot_ignore & (1 << PCI_SLOT(devfn))) + return NULL; + + return versatile_cfg_base[1] + ((busnr << 16) | (devfn << 8) | offset); +} + +static struct pci_ops pci_versatile_ops = { + .map_bus = versatile_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write, +}; + +static int versatile_pci_parse_request_of_pci_ranges(struct device *dev, + struct list_head *res) +{ + int err, mem = 1, res_valid = 0; + resource_size_t iobase; + struct resource_entry *win, *tmp; + + err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, res, &iobase); + if (err) + return err; + + err = devm_request_pci_bus_resources(dev, res); + if (err) + goto out_release_res; + + resource_list_for_each_entry_safe(win, tmp, res) { + struct resource *res = win->res; + + switch (resource_type(res)) { + case IORESOURCE_IO: + err = pci_remap_iospace(res, iobase); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, res); + resource_list_destroy_entry(win); + } + break; + case IORESOURCE_MEM: + res_valid |= !(res->flags & IORESOURCE_PREFETCH); + + writel(res->start >> 28, PCI_IMAP(mem)); + writel(PHYS_OFFSET >> 28, PCI_SMAP(mem)); + mem++; + + break; + } + } + + if (res_valid) + return 0; + + dev_err(dev, "non-prefetchable memory resource required\n"); + err = -EINVAL; + +out_release_res: + pci_free_resource_list(res); + return err; +} + +static int versatile_pci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + int ret, i, myslot = -1; + u32 val; + void __iomem *local_pci_cfg_base; + struct pci_bus *bus, *child; + struct pci_host_bridge *bridge; + LIST_HEAD(pci_res); + + bridge = devm_pci_alloc_host_bridge(dev, 0); + if (!bridge) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + versatile_pci_base = devm_ioremap_resource(dev, res); + if (IS_ERR(versatile_pci_base)) + return PTR_ERR(versatile_pci_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + versatile_cfg_base[0] = devm_ioremap_resource(dev, res); + if (IS_ERR(versatile_cfg_base[0])) + return PTR_ERR(versatile_cfg_base[0]); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + versatile_cfg_base[1] = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(versatile_cfg_base[1])) + return PTR_ERR(versatile_cfg_base[1]); + + ret = versatile_pci_parse_request_of_pci_ranges(dev, &pci_res); + if (ret) + return ret; + + /* + * We need to discover the PCI core first to configure itself + * before the main PCI probing is performed + */ + for (i = 0; i < 32; i++) { + if ((readl(versatile_cfg_base[0] + (i << 11) + PCI_VENDOR_ID) == VP_PCI_DEVICE_ID) && + (readl(versatile_cfg_base[0] + (i << 11) + PCI_CLASS_REVISION) == VP_PCI_CLASS_ID)) { + myslot = i; + break; + } + } + if (myslot == -1) { + dev_err(dev, "Cannot find PCI core!\n"); + return -EIO; + } + /* + * Do not to map Versatile FPGA PCI device into memory space + */ + pci_slot_ignore |= (1 << myslot); + + dev_info(dev, "PCI core found (slot %d)\n", myslot); + + writel(myslot, PCI_SELFID); + local_pci_cfg_base = versatile_cfg_base[1] + (myslot << 11); + + val = readl(local_pci_cfg_base + PCI_COMMAND); + val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; + writel(val, local_pci_cfg_base + PCI_COMMAND); + + /* + * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM + */ + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0); + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1); + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2); + + /* + * For many years the kernel and QEMU were symbiotically buggy + * in that they both assumed the same broken IRQ mapping. + * QEMU therefore attempts to auto-detect old broken kernels + * so that they still work on newer QEMU as they did on old + * QEMU. Since we now use the correct (ie matching-hardware) + * IRQ mapping we write a definitely different value to a + * PCI_INTERRUPT_LINE register to tell QEMU that we expect + * real hardware behaviour and it need not be backwards + * compatible for us. This write is harmless on real hardware. + */ + writel(0, versatile_cfg_base[0] + PCI_INTERRUPT_LINE); + + pci_add_flags(PCI_ENABLE_PROC_DOMAINS); + pci_add_flags(PCI_REASSIGN_ALL_BUS); + + list_splice_init(&pci_res, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = NULL; + bridge->busnr = 0; + bridge->ops = &pci_versatile_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret < 0) + return ret; + + bus = bridge->bus; + + pci_assign_unassigned_bus_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + pci_bus_add_devices(bus); + + return 0; +} + +static const struct of_device_id versatile_pci_of_match[] = { + { .compatible = "arm,versatile-pci", }, + { }, +}; +MODULE_DEVICE_TABLE(of, versatile_pci_of_match); + +static struct platform_driver versatile_pci_driver = { + .driver = { + .name = "versatile-pci", + .of_match_table = versatile_pci_of_match, + .suppress_bind_attrs = true, + }, + .probe = versatile_pci_probe, +}; +module_platform_driver(versatile_pci_driver); + +MODULE_DESCRIPTION("Versatile PCI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-xgene-msi.c b/drivers/pci/controller/pci-xgene-msi.c new file mode 100644 index 000000000000..f4c02da84e59 --- /dev/null +++ b/drivers/pci/controller/pci-xgene-msi.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * APM X-Gene MSI Driver + * + * Copyright (c) 2014, Applied Micro Circuits Corporation + * Author: Tanmay Inamdar + * Duc Dang + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSI_IR0 0x000000 +#define MSI_INT0 0x800000 +#define IDX_PER_GROUP 8 +#define IRQS_PER_IDX 16 +#define NR_HW_IRQS 16 +#define NR_MSI_VEC (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS) + +struct xgene_msi_group { + struct xgene_msi *msi; + int gic_irq; + u32 msi_grp; +}; + +struct xgene_msi { + struct device_node *node; + struct irq_domain *inner_domain; + struct irq_domain *msi_domain; + u64 msi_addr; + void __iomem *msi_regs; + unsigned long *bitmap; + struct mutex bitmap_lock; + struct xgene_msi_group *msi_groups; + int num_cpus; +}; + +/* Global data */ +static struct xgene_msi xgene_msi_ctrl; + +static struct irq_chip xgene_msi_top_irq_chip = { + .name = "X-Gene1 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 struct msi_domain_info xgene_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), + .chip = &xgene_msi_top_irq_chip, +}; + +/* + * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where + * n is group number (0..F), x is index of registers in each group (0..7) + * The register layout is as follows: + * MSI0IR0 base_addr + * MSI0IR1 base_addr + 0x10000 + * ... ... + * MSI0IR6 base_addr + 0x60000 + * MSI0IR7 base_addr + 0x70000 + * MSI1IR0 base_addr + 0x80000 + * MSI1IR1 base_addr + 0x90000 + * ... ... + * MSI1IR7 base_addr + 0xF0000 + * MSI2IR0 base_addr + 0x100000 + * ... ... + * MSIFIR0 base_addr + 0x780000 + * MSIFIR1 base_addr + 0x790000 + * ... ... + * MSIFIR7 base_addr + 0x7F0000 + * MSIINT0 base_addr + 0x800000 + * MSIINT1 base_addr + 0x810000 + * ... ... + * MSIINTF base_addr + 0x8F0000 + * + * Each index register supports 16 MSI vectors (0..15) to generate interrupt. + * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination + * registers. + * + * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate + * the MSI pending status caused by 1 of its 8 index registers. + */ + +/* MSInIRx read helper */ +static u32 xgene_msi_ir_read(struct xgene_msi *msi, + u32 msi_grp, u32 msir_idx) +{ + return readl_relaxed(msi->msi_regs + MSI_IR0 + + (msi_grp << 19) + (msir_idx << 16)); +} + +/* MSIINTn read helper */ +static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp) +{ + return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16)); +} + +/* + * With 2048 MSI vectors supported, the MSI message can be constructed using + * following scheme: + * - Divide into 8 256-vector groups + * Group 0: 0-255 + * Group 1: 256-511 + * Group 2: 512-767 + * ... + * Group 7: 1792-2047 + * - Each 256-vector group is divided into 16 16-vector groups + * As an example: 16 16-vector groups for 256-vector group 0-255 is + * Group 0: 0-15 + * Group 1: 16-32 + * ... + * Group 15: 240-255 + * - The termination address of MSI vector in 256-vector group n and 16-vector + * group x is the address of MSIxIRn + * - The data for MSI vector in 16-vector group x is x + */ +static u32 hwirq_to_reg_set(unsigned long hwirq) +{ + return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX)); +} + +static u32 hwirq_to_group(unsigned long hwirq) +{ + return (hwirq % NR_HW_IRQS); +} + +static u32 hwirq_to_msi_data(unsigned long hwirq) +{ + return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX); +} + +static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct xgene_msi *msi = irq_data_get_irq_chip_data(data); + u32 reg_set = hwirq_to_reg_set(data->hwirq); + u32 group = hwirq_to_group(data->hwirq); + u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16); + + msg->address_hi = upper_32_bits(target_addr); + msg->address_lo = lower_32_bits(target_addr); + msg->data = hwirq_to_msi_data(data->hwirq); +} + +/* + * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors. To maintain + * the expected behaviour of .set_affinity for each MSI interrupt, the 16 + * MSI GIC IRQs are statically allocated to 8 X-Gene v1 cores (2 GIC IRQs + * for each core). The MSI vector is moved fom 1 MSI GIC IRQ to another + * MSI GIC IRQ to steer its MSI interrupt to correct X-Gene v1 core. As a + * consequence, the total MSI vectors that X-Gene v1 supports will be + * reduced to 256 (2048/8) vectors. + */ +static int hwirq_to_cpu(unsigned long hwirq) +{ + return (hwirq % xgene_msi_ctrl.num_cpus); +} + +static unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq) +{ + return (hwirq - hwirq_to_cpu(hwirq)); +} + +static int xgene_msi_set_affinity(struct irq_data *irqdata, + const struct cpumask *mask, bool force) +{ + int target_cpu = cpumask_first(mask); + int curr_cpu; + + curr_cpu = hwirq_to_cpu(irqdata->hwirq); + if (curr_cpu == target_cpu) + return IRQ_SET_MASK_OK_DONE; + + /* Update MSI number to target the new CPU */ + irqdata->hwirq = hwirq_to_canonical_hwirq(irqdata->hwirq) + target_cpu; + + return IRQ_SET_MASK_OK; +} + +static struct irq_chip xgene_msi_bottom_irq_chip = { + .name = "MSI", + .irq_set_affinity = xgene_msi_set_affinity, + .irq_compose_msi_msg = xgene_compose_msi_msg, +}; + +static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct xgene_msi *msi = domain->host_data; + int msi_irq; + + mutex_lock(&msi->bitmap_lock); + + msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0, + msi->num_cpus, 0); + if (msi_irq < NR_MSI_VEC) + bitmap_set(msi->bitmap, msi_irq, msi->num_cpus); + else + msi_irq = -ENOSPC; + + mutex_unlock(&msi->bitmap_lock); + + if (msi_irq < 0) + return msi_irq; + + irq_domain_set_info(domain, virq, msi_irq, + &xgene_msi_bottom_irq_chip, domain->host_data, + handle_simple_irq, NULL, NULL); + + return 0; +} + +static void xgene_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct xgene_msi *msi = irq_data_get_irq_chip_data(d); + u32 hwirq; + + mutex_lock(&msi->bitmap_lock); + + hwirq = hwirq_to_canonical_hwirq(d->hwirq); + bitmap_clear(msi->bitmap, hwirq, msi->num_cpus); + + mutex_unlock(&msi->bitmap_lock); + + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +} + +static const struct irq_domain_ops msi_domain_ops = { + .alloc = xgene_irq_domain_alloc, + .free = xgene_irq_domain_free, +}; + +static int xgene_allocate_domains(struct xgene_msi *msi) +{ + msi->inner_domain = irq_domain_add_linear(NULL, NR_MSI_VEC, + &msi_domain_ops, msi); + if (!msi->inner_domain) + return -ENOMEM; + + msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(msi->node), + &xgene_msi_domain_info, + msi->inner_domain); + + if (!msi->msi_domain) { + irq_domain_remove(msi->inner_domain); + return -ENOMEM; + } + + return 0; +} + +static void xgene_free_domains(struct xgene_msi *msi) +{ + if (msi->msi_domain) + irq_domain_remove(msi->msi_domain); + if (msi->inner_domain) + irq_domain_remove(msi->inner_domain); +} + +static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi) +{ + int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long); + + xgene_msi->bitmap = kzalloc(size, GFP_KERNEL); + if (!xgene_msi->bitmap) + return -ENOMEM; + + mutex_init(&xgene_msi->bitmap_lock); + + xgene_msi->msi_groups = kcalloc(NR_HW_IRQS, + sizeof(struct xgene_msi_group), + GFP_KERNEL); + if (!xgene_msi->msi_groups) + return -ENOMEM; + + return 0; +} + +static void xgene_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct xgene_msi_group *msi_groups; + struct xgene_msi *xgene_msi; + unsigned int virq; + int msir_index, msir_val, hw_irq; + u32 intr_index, grp_select, msi_grp; + + chained_irq_enter(chip, desc); + + msi_groups = irq_desc_get_handler_data(desc); + xgene_msi = msi_groups->msi; + msi_grp = msi_groups->msi_grp; + + /* + * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt + * If bit x of this register is set (x is 0..7), one or more interupts + * corresponding to MSInIRx is set. + */ + grp_select = xgene_msi_int_read(xgene_msi, msi_grp); + while (grp_select) { + msir_index = ffs(grp_select) - 1; + /* + * Calculate MSInIRx address to read to check for interrupts + * (refer to termination address and data assignment + * described in xgene_compose_msi_msg() ) + */ + msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index); + while (msir_val) { + intr_index = ffs(msir_val) - 1; + /* + * Calculate MSI vector number (refer to the termination + * address and data assignment described in + * xgene_compose_msi_msg function) + */ + hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) * + NR_HW_IRQS) + msi_grp; + /* + * As we have multiple hw_irq that maps to single MSI, + * always look up the virq using the hw_irq as seen from + * CPU0 + */ + hw_irq = hwirq_to_canonical_hwirq(hw_irq); + virq = irq_find_mapping(xgene_msi->inner_domain, hw_irq); + WARN_ON(!virq); + if (virq != 0) + generic_handle_irq(virq); + msir_val &= ~(1 << intr_index); + } + grp_select &= ~(1 << msir_index); + + if (!grp_select) { + /* + * We handled all interrupts happened in this group, + * resample this group MSI_INTx register in case + * something else has been made pending in the meantime + */ + grp_select = xgene_msi_int_read(xgene_msi, msi_grp); + } + } + + chained_irq_exit(chip, desc); +} + +static enum cpuhp_state pci_xgene_online; + +static int xgene_msi_remove(struct platform_device *pdev) +{ + struct xgene_msi *msi = platform_get_drvdata(pdev); + + if (pci_xgene_online) + cpuhp_remove_state(pci_xgene_online); + cpuhp_remove_state(CPUHP_PCI_XGENE_DEAD); + + kfree(msi->msi_groups); + + kfree(msi->bitmap); + msi->bitmap = NULL; + + xgene_free_domains(msi); + + return 0; +} + +static int xgene_msi_hwirq_alloc(unsigned int cpu) +{ + struct xgene_msi *msi = &xgene_msi_ctrl; + struct xgene_msi_group *msi_group; + cpumask_var_t mask; + int i; + int err; + + for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) { + msi_group = &msi->msi_groups[i]; + if (!msi_group->gic_irq) + continue; + + irq_set_chained_handler(msi_group->gic_irq, + xgene_msi_isr); + err = irq_set_handler_data(msi_group->gic_irq, msi_group); + if (err) { + pr_err("failed to register GIC IRQ handler\n"); + return -EINVAL; + } + /* + * Statically allocate MSI GIC IRQs to each CPU core. + * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated + * to each core. + */ + if (alloc_cpumask_var(&mask, GFP_KERNEL)) { + cpumask_clear(mask); + cpumask_set_cpu(cpu, mask); + err = irq_set_affinity(msi_group->gic_irq, mask); + if (err) + pr_err("failed to set affinity for GIC IRQ"); + free_cpumask_var(mask); + } else { + pr_err("failed to alloc CPU mask for affinity\n"); + err = -EINVAL; + } + + if (err) { + irq_set_chained_handler_and_data(msi_group->gic_irq, + NULL, NULL); + return err; + } + } + + return 0; +} + +static int xgene_msi_hwirq_free(unsigned int cpu) +{ + struct xgene_msi *msi = &xgene_msi_ctrl; + struct xgene_msi_group *msi_group; + int i; + + for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) { + msi_group = &msi->msi_groups[i]; + if (!msi_group->gic_irq) + continue; + + irq_set_chained_handler_and_data(msi_group->gic_irq, NULL, + NULL); + } + return 0; +} + +static const struct of_device_id xgene_msi_match_table[] = { + {.compatible = "apm,xgene1-msi"}, + {}, +}; + +static int xgene_msi_probe(struct platform_device *pdev) +{ + struct resource *res; + int rc, irq_index; + struct xgene_msi *xgene_msi; + int virt_msir; + u32 msi_val, msi_idx; + + xgene_msi = &xgene_msi_ctrl; + + platform_set_drvdata(pdev, xgene_msi); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xgene_msi->msi_regs)) { + dev_err(&pdev->dev, "no reg space\n"); + rc = PTR_ERR(xgene_msi->msi_regs); + goto error; + } + xgene_msi->msi_addr = res->start; + xgene_msi->node = pdev->dev.of_node; + xgene_msi->num_cpus = num_possible_cpus(); + + rc = xgene_msi_init_allocator(xgene_msi); + if (rc) { + dev_err(&pdev->dev, "Error allocating MSI bitmap\n"); + goto error; + } + + rc = xgene_allocate_domains(xgene_msi); + if (rc) { + dev_err(&pdev->dev, "Failed to allocate MSI domain\n"); + goto error; + } + + for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { + virt_msir = platform_get_irq(pdev, irq_index); + if (virt_msir < 0) { + dev_err(&pdev->dev, "Cannot translate IRQ index %d\n", + irq_index); + rc = virt_msir; + goto error; + } + xgene_msi->msi_groups[irq_index].gic_irq = virt_msir; + xgene_msi->msi_groups[irq_index].msi_grp = irq_index; + xgene_msi->msi_groups[irq_index].msi = xgene_msi; + } + + /* + * MSInIRx registers are read-to-clear; before registering + * interrupt handlers, read all of them to clear spurious + * interrupts that may occur before the driver is probed. + */ + for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { + for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) + msi_val = xgene_msi_ir_read(xgene_msi, irq_index, + msi_idx); + /* Read MSIINTn to confirm */ + msi_val = xgene_msi_int_read(xgene_msi, irq_index); + if (msi_val) { + dev_err(&pdev->dev, "Failed to clear spurious IRQ\n"); + rc = -EINVAL; + goto error; + } + } + + rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "pci/xgene:online", + xgene_msi_hwirq_alloc, NULL); + if (rc < 0) + goto err_cpuhp; + pci_xgene_online = rc; + rc = cpuhp_setup_state(CPUHP_PCI_XGENE_DEAD, "pci/xgene:dead", NULL, + xgene_msi_hwirq_free); + if (rc) + goto err_cpuhp; + + dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n"); + + return 0; + +err_cpuhp: + dev_err(&pdev->dev, "failed to add CPU MSI notifier\n"); +error: + xgene_msi_remove(pdev); + return rc; +} + +static struct platform_driver xgene_msi_driver = { + .driver = { + .name = "xgene-msi", + .of_match_table = xgene_msi_match_table, + }, + .probe = xgene_msi_probe, + .remove = xgene_msi_remove, +}; + +static int __init xgene_pcie_msi_init(void) +{ + return platform_driver_register(&xgene_msi_driver); +} +subsys_initcall(xgene_pcie_msi_init); diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c new file mode 100644 index 000000000000..d854d67e873c --- /dev/null +++ b/drivers/pci/controller/pci-xgene.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0+ +/** + * APM X-Gene PCIe Driver + * + * Copyright (c) 2014 Applied Micro Circuits Corporation. + * + * Author: Tanmay Inamdar . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +#define PCIECORE_CTLANDSTATUS 0x50 +#define PIM1_1L 0x80 +#define IBAR2 0x98 +#define IR2MSK 0x9c +#define PIM2_1L 0xa0 +#define IBAR3L 0xb4 +#define IR3MSKL 0xbc +#define PIM3_1L 0xc4 +#define OMR1BARL 0x100 +#define OMR2BARL 0x118 +#define OMR3BARL 0x130 +#define CFGBARL 0x154 +#define CFGBARH 0x158 +#define CFGCTL 0x15c +#define RTDID 0x160 +#define BRIDGE_CFG_0 0x2000 +#define BRIDGE_CFG_4 0x2010 +#define BRIDGE_STATUS_0 0x2600 + +#define LINK_UP_MASK 0x00000100 +#define AXI_EP_CFG_ACCESS 0x10000 +#define EN_COHERENCY 0xF0000000 +#define EN_REG 0x00000001 +#define OB_LO_IO 0x00000002 +#define XGENE_PCIE_VENDORID 0x10E8 +#define XGENE_PCIE_DEVICEID 0xE004 +#define SZ_1T (SZ_1G*1024ULL) +#define PIPE_PHY_RATE_RD(src) ((0xc000 & (u32)(src)) >> 0xe) + +#define XGENE_V1_PCI_EXP_CAP 0x40 + +/* PCIe IP version */ +#define XGENE_PCIE_IP_VER_UNKN 0 +#define XGENE_PCIE_IP_VER_1 1 +#define XGENE_PCIE_IP_VER_2 2 + +#if defined(CONFIG_PCI_XGENE) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) +struct xgene_pcie_port { + struct device_node *node; + struct device *dev; + struct clk *clk; + void __iomem *csr_base; + void __iomem *cfg_base; + unsigned long cfg_addr; + bool link_up; + u32 version; +}; + +static u32 xgene_pcie_readl(struct xgene_pcie_port *port, u32 reg) +{ + return readl(port->csr_base + reg); +} + +static void xgene_pcie_writel(struct xgene_pcie_port *port, u32 reg, u32 val) +{ + writel(val, port->csr_base + reg); +} + +static inline u32 pcie_bar_low_val(u32 addr, u32 flags) +{ + return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags; +} + +static inline struct xgene_pcie_port *pcie_bus_to_port(struct pci_bus *bus) +{ + struct pci_config_window *cfg; + + if (acpi_disabled) + return (struct xgene_pcie_port *)(bus->sysdata); + + cfg = bus->sysdata; + return (struct xgene_pcie_port *)(cfg->priv); +} + +/* + * When the address bit [17:16] is 2'b01, the Configuration access will be + * treated as Type 1 and it will be forwarded to external PCIe device. + */ +static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus) +{ + struct xgene_pcie_port *port = pcie_bus_to_port(bus); + + if (bus->number >= (bus->primary + 1)) + return port->cfg_base + AXI_EP_CFG_ACCESS; + + return port->cfg_base; +} + +/* + * For Configuration request, RTDID register is used as Bus Number, + * Device Number and Function number of the header fields. + */ +static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn) +{ + struct xgene_pcie_port *port = pcie_bus_to_port(bus); + unsigned int b, d, f; + u32 rtdid_val = 0; + + b = bus->number; + d = PCI_SLOT(devfn); + f = PCI_FUNC(devfn); + + if (!pci_is_root_bus(bus)) + rtdid_val = (b << 8) | (d << 3) | f; + + xgene_pcie_writel(port, RTDID, rtdid_val); + /* read the register back to ensure flush */ + xgene_pcie_readl(port, RTDID); +} + +/* + * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as + * the translation from PCI bus to native BUS. Entire DDR region + * is mapped into PCIe space using these registers, so it can be + * reached by DMA from EP devices. The BAR0/1 of bridge should be + * hidden during enumeration to avoid the sizing and resource allocation + * by PCIe core. + */ +static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) +{ + if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) || + (offset == PCI_BASE_ADDRESS_1))) + return true; + + return false; +} + +static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int offset) +{ + if ((pci_is_root_bus(bus) && devfn != 0) || + xgene_pcie_hide_rc_bars(bus, offset)) + return NULL; + + xgene_pcie_set_rtdid_reg(bus, devfn); + return xgene_pcie_get_cfg_base(bus) + offset; +} + +static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct xgene_pcie_port *port = pcie_bus_to_port(bus); + + if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) != + PCIBIOS_SUCCESSFUL) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * The v1 controller has a bug in its Configuration Request + * Retry Status (CRS) logic: when CRS is enabled and we read the + * Vendor and Device ID of a non-existent device, the controller + * fabricates return data of 0xFFFF0001 ("device exists but is not + * ready") instead of 0xFFFFFFFF ("device does not exist"). This + * causes the PCI core to retry the read until it times out. + * Avoid this by not claiming to support CRS. + */ + if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) && + ((where & ~0x3) == XGENE_V1_PCI_EXP_CAP + PCI_EXP_RTCTL)) + *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); + + if (size <= 2) + *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); + + return PCIBIOS_SUCCESSFUL; +} +#endif + +#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) +static int xgene_get_csr_resource(struct acpi_device *adev, + struct resource *res) +{ + struct device *dev = &adev->dev; + struct resource_entry *entry; + struct list_head list; + unsigned long flags; + int ret; + + INIT_LIST_HEAD(&list); + flags = IORESOURCE_MEM; + ret = acpi_dev_get_resources(adev, &list, + acpi_dev_filter_resource_type_cb, + (void *) flags); + if (ret < 0) { + dev_err(dev, "failed to parse _CRS method, error code %d\n", + ret); + return ret; + } + + if (ret == 0) { + dev_err(dev, "no IO and memory resources present in _CRS\n"); + return -EINVAL; + } + + entry = list_first_entry(&list, struct resource_entry, node); + *res = *entry->res; + acpi_dev_free_resource_list(&list); + return 0; +} + +static int xgene_pcie_ecam_init(struct pci_config_window *cfg, u32 ipversion) +{ + struct device *dev = cfg->parent; + struct acpi_device *adev = to_acpi_device(dev); + struct xgene_pcie_port *port; + struct resource csr; + int ret; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + ret = xgene_get_csr_resource(adev, &csr); + if (ret) { + dev_err(dev, "can't get CSR resource\n"); + return ret; + } + port->csr_base = devm_pci_remap_cfg_resource(dev, &csr); + if (IS_ERR(port->csr_base)) + return PTR_ERR(port->csr_base); + + port->cfg_base = cfg->win; + port->version = ipversion; + + cfg->priv = port; + return 0; +} + +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 = { + .bus_shift = 16, + .init = xgene_v1_pcie_ecam_init, + .pci_ops = { + .map_bus = xgene_pcie_map_bus, + .read = xgene_pcie_config_read32, + .write = pci_generic_config_write, + } +}; + +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 = { + .bus_shift = 16, + .init = xgene_v2_pcie_ecam_init, + .pci_ops = { + .map_bus = xgene_pcie_map_bus, + .read = xgene_pcie_config_read32, + .write = pci_generic_config_write, + } +}; +#endif + +#if defined(CONFIG_PCI_XGENE) +static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr, + u32 flags, u64 size) +{ + u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags; + u32 val32 = 0; + u32 val; + + val32 = xgene_pcie_readl(port, addr); + val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16); + xgene_pcie_writel(port, addr, val); + + val32 = xgene_pcie_readl(port, addr + 0x04); + val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16); + xgene_pcie_writel(port, addr + 0x04, val); + + val32 = xgene_pcie_readl(port, addr + 0x04); + val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16); + xgene_pcie_writel(port, addr + 0x04, val); + + val32 = xgene_pcie_readl(port, addr + 0x08); + val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16); + xgene_pcie_writel(port, addr + 0x08, val); + + return mask; +} + +static void xgene_pcie_linkup(struct xgene_pcie_port *port, + u32 *lanes, u32 *speed) +{ + u32 val32; + + port->link_up = false; + val32 = xgene_pcie_readl(port, PCIECORE_CTLANDSTATUS); + if (val32 & LINK_UP_MASK) { + port->link_up = true; + *speed = PIPE_PHY_RATE_RD(val32); + val32 = xgene_pcie_readl(port, BRIDGE_STATUS_0); + *lanes = val32 >> 26; + } +} + +static int xgene_pcie_init_port(struct xgene_pcie_port *port) +{ + struct device *dev = port->dev; + int rc; + + port->clk = clk_get(dev, NULL); + if (IS_ERR(port->clk)) { + dev_err(dev, "clock not available\n"); + return -ENODEV; + } + + rc = clk_prepare_enable(port->clk); + if (rc) { + dev_err(dev, "clock enable failed\n"); + return rc; + } + + return 0; +} + +static int xgene_pcie_map_reg(struct xgene_pcie_port *port, + struct platform_device *pdev) +{ + struct device *dev = port->dev; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); + port->csr_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(port->csr_base)) + return PTR_ERR(port->csr_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + port->cfg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(port->cfg_base)) + return PTR_ERR(port->cfg_base); + port->cfg_addr = res->start; + + return 0; +} + +static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port, + struct resource *res, u32 offset, + u64 cpu_addr, u64 pci_addr) +{ + struct device *dev = port->dev; + resource_size_t size = resource_size(res); + u64 restype = resource_type(res); + u64 mask = 0; + u32 min_size; + u32 flag = EN_REG; + + if (restype == IORESOURCE_MEM) { + min_size = SZ_128M; + } else { + min_size = 128; + flag |= OB_LO_IO; + } + + if (size >= min_size) + mask = ~(size - 1) | flag; + else + dev_warn(dev, "res size 0x%llx less than minimum 0x%x\n", + (u64)size, min_size); + + xgene_pcie_writel(port, offset, lower_32_bits(cpu_addr)); + xgene_pcie_writel(port, offset + 0x04, upper_32_bits(cpu_addr)); + xgene_pcie_writel(port, offset + 0x08, lower_32_bits(mask)); + xgene_pcie_writel(port, offset + 0x0c, upper_32_bits(mask)); + xgene_pcie_writel(port, offset + 0x10, lower_32_bits(pci_addr)); + xgene_pcie_writel(port, offset + 0x14, upper_32_bits(pci_addr)); +} + +static void xgene_pcie_setup_cfg_reg(struct xgene_pcie_port *port) +{ + u64 addr = port->cfg_addr; + + xgene_pcie_writel(port, CFGBARL, lower_32_bits(addr)); + xgene_pcie_writel(port, CFGBARH, upper_32_bits(addr)); + xgene_pcie_writel(port, CFGCTL, EN_REG); +} + +static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, + struct list_head *res, + resource_size_t io_base) +{ + struct resource_entry *window; + struct device *dev = port->dev; + int ret; + + resource_list_for_each_entry(window, res) { + struct resource *res = window->res; + u64 restype = resource_type(res); + + dev_dbg(dev, "%pR\n", res); + + switch (restype) { + case IORESOURCE_IO: + xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base, + res->start - window->offset); + ret = pci_remap_iospace(res, io_base); + if (ret < 0) + return ret; + break; + case IORESOURCE_MEM: + if (res->flags & IORESOURCE_PREFETCH) + xgene_pcie_setup_ob_reg(port, res, OMR2BARL, + res->start, + res->start - + window->offset); + else + xgene_pcie_setup_ob_reg(port, res, OMR1BARL, + res->start, + res->start - + window->offset); + break; + case IORESOURCE_BUS: + break; + default: + dev_err(dev, "invalid resource %pR\n", res); + return -EINVAL; + } + } + xgene_pcie_setup_cfg_reg(port); + return 0; +} + +static void xgene_pcie_setup_pims(struct xgene_pcie_port *port, u32 pim_reg, + u64 pim, u64 size) +{ + xgene_pcie_writel(port, pim_reg, lower_32_bits(pim)); + xgene_pcie_writel(port, pim_reg + 0x04, + upper_32_bits(pim) | EN_COHERENCY); + xgene_pcie_writel(port, pim_reg + 0x10, lower_32_bits(size)); + xgene_pcie_writel(port, pim_reg + 0x14, upper_32_bits(size)); +} + +/* + * X-Gene PCIe support maximum 3 inbound memory regions + * This function helps to select a region based on size of region + */ +static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size) +{ + if ((size > 4) && (size < SZ_16M) && !(*ib_reg_mask & (1 << 1))) { + *ib_reg_mask |= (1 << 1); + return 1; + } + + if ((size > SZ_1K) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 0))) { + *ib_reg_mask |= (1 << 0); + return 0; + } + + if ((size > SZ_1M) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 2))) { + *ib_reg_mask |= (1 << 2); + return 2; + } + + return -EINVAL; +} + +static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, + struct of_pci_range *range, u8 *ib_reg_mask) +{ + void __iomem *cfg_base = port->cfg_base; + struct device *dev = port->dev; + void *bar_addr; + u32 pim_reg; + u64 cpu_addr = range->cpu_addr; + u64 pci_addr = range->pci_addr; + u64 size = range->size; + u64 mask = ~(size - 1) | EN_REG; + u32 flags = PCI_BASE_ADDRESS_MEM_TYPE_64; + u32 bar_low; + int region; + + region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size); + if (region < 0) { + dev_warn(dev, "invalid pcie dma-range config\n"); + return; + } + + if (range->flags & IORESOURCE_PREFETCH) + flags |= PCI_BASE_ADDRESS_MEM_PREFETCH; + + bar_low = pcie_bar_low_val((u32)cpu_addr, flags); + switch (region) { + case 0: + xgene_pcie_set_ib_mask(port, BRIDGE_CFG_4, flags, size); + bar_addr = cfg_base + PCI_BASE_ADDRESS_0; + writel(bar_low, bar_addr); + writel(upper_32_bits(cpu_addr), bar_addr + 0x4); + pim_reg = PIM1_1L; + break; + case 1: + xgene_pcie_writel(port, IBAR2, bar_low); + xgene_pcie_writel(port, IR2MSK, lower_32_bits(mask)); + pim_reg = PIM2_1L; + break; + case 2: + xgene_pcie_writel(port, IBAR3L, bar_low); + xgene_pcie_writel(port, IBAR3L + 0x4, upper_32_bits(cpu_addr)); + xgene_pcie_writel(port, IR3MSKL, lower_32_bits(mask)); + xgene_pcie_writel(port, IR3MSKL + 0x4, upper_32_bits(mask)); + pim_reg = PIM3_1L; + break; + } + + xgene_pcie_setup_pims(port, pim_reg, pci_addr, ~(size - 1)); +} + +static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port) +{ + struct device_node *np = port->node; + struct of_pci_range range; + struct of_pci_range_parser parser; + struct device *dev = port->dev; + u8 ib_reg_mask = 0; + + if (of_pci_dma_range_parser_init(&parser, np)) { + dev_err(dev, "missing dma-ranges property\n"); + return -EINVAL; + } + + /* Get the dma-ranges from DT */ + for_each_of_pci_range(&parser, &range) { + u64 end = range.cpu_addr + range.size - 1; + + dev_dbg(dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", + range.flags, range.cpu_addr, end, range.pci_addr); + xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask); + } + return 0; +} + +/* clear BAR configuration which was done by firmware */ +static void xgene_pcie_clear_config(struct xgene_pcie_port *port) +{ + int i; + + for (i = PIM1_1L; i <= CFGCTL; i += 4) + xgene_pcie_writel(port, i, 0); +} + +static int xgene_pcie_setup(struct xgene_pcie_port *port, struct list_head *res, + resource_size_t io_base) +{ + struct device *dev = port->dev; + u32 val, lanes = 0, speed = 0; + int ret; + + xgene_pcie_clear_config(port); + + /* setup the vendor and device IDs correctly */ + val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID; + xgene_pcie_writel(port, BRIDGE_CFG_0, val); + + ret = xgene_pcie_map_ranges(port, res, io_base); + if (ret) + return ret; + + ret = xgene_pcie_parse_map_dma_ranges(port); + if (ret) + return ret; + + xgene_pcie_linkup(port, &lanes, &speed); + if (!port->link_up) + dev_info(dev, "(rc) link down\n"); + else + dev_info(dev, "(rc) x%d gen-%d link up\n", lanes, speed + 1); + return 0; +} + +static struct pci_ops xgene_pcie_ops = { + .map_bus = xgene_pcie_map_bus, + .read = xgene_pcie_config_read32, + .write = pci_generic_config_write32, +}; + +static int xgene_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node; + struct xgene_pcie_port *port; + resource_size_t iobase = 0; + struct pci_bus *bus, *child; + struct pci_host_bridge *bridge; + int ret; + LIST_HEAD(res); + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port)); + if (!bridge) + return -ENOMEM; + + port = pci_host_bridge_priv(bridge); + + port->node = of_node_get(dn); + port->dev = dev; + + port->version = XGENE_PCIE_IP_VER_UNKN; + if (of_device_is_compatible(port->node, "apm,xgene-pcie")) + port->version = XGENE_PCIE_IP_VER_1; + + ret = xgene_pcie_map_reg(port, pdev); + if (ret) + return ret; + + ret = xgene_pcie_init_port(port); + if (ret) + return ret; + + ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, + &iobase); + if (ret) + return ret; + + ret = devm_request_pci_bus_resources(dev, &res); + if (ret) + goto error; + + ret = xgene_pcie_setup(port, &res, iobase); + if (ret) + goto error; + + list_splice_init(&res, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = port; + bridge->busnr = 0; + bridge->ops = &xgene_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret < 0) + goto error; + + bus = bridge->bus; + + pci_assign_unassigned_bus_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + pci_bus_add_devices(bus); + return 0; + +error: + pci_free_resource_list(&res); + return ret; +} + +static const struct of_device_id xgene_pcie_match_table[] = { + {.compatible = "apm,xgene-pcie",}, + {}, +}; + +static struct platform_driver xgene_pcie_driver = { + .driver = { + .name = "xgene-pcie", + .of_match_table = of_match_ptr(xgene_pcie_match_table), + .suppress_bind_attrs = true, + }, + .probe = xgene_pcie_probe, +}; +builtin_platform_driver(xgene_pcie_driver); +#endif diff --git a/drivers/pci/controller/pcie-altera-msi.c b/drivers/pci/controller/pcie-altera-msi.c new file mode 100644 index 000000000000..025ef7d9a046 --- /dev/null +++ b/drivers/pci/controller/pcie-altera-msi.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Altera PCIe MSI support + * + * Author: Ley Foon Tan + * + * Copyright Altera Corporation (C) 2013-2015. All rights reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSI_STATUS 0x0 +#define MSI_ERROR 0x4 +#define MSI_INTMASK 0x8 + +#define MAX_MSI_VECTORS 32 + +struct altera_msi { + DECLARE_BITMAP(used, MAX_MSI_VECTORS); + struct mutex lock; /* protect "used" bitmap */ + struct platform_device *pdev; + struct irq_domain *msi_domain; + struct irq_domain *inner_domain; + void __iomem *csr_base; + void __iomem *vector_base; + phys_addr_t vector_phy; + u32 num_of_vectors; + int irq; +}; + +static inline void msi_writel(struct altera_msi *msi, const u32 value, + const u32 reg) +{ + writel_relaxed(value, msi->csr_base + reg); +} + +static inline u32 msi_readl(struct altera_msi *msi, const u32 reg) +{ + return readl_relaxed(msi->csr_base + reg); +} + +static void altera_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct altera_msi *msi; + unsigned long status; + u32 bit; + u32 virq; + + chained_irq_enter(chip, desc); + msi = irq_desc_get_handler_data(desc); + + while ((status = msi_readl(msi, MSI_STATUS)) != 0) { + for_each_set_bit(bit, &status, msi->num_of_vectors) { + /* Dummy read from vector to clear the interrupt */ + readl_relaxed(msi->vector_base + (bit * sizeof(u32))); + + virq = irq_find_mapping(msi->inner_domain, bit); + if (virq) + generic_handle_irq(virq); + else + dev_err(&msi->pdev->dev, "unexpected MSI\n"); + } + } + + chained_irq_exit(chip, desc); +} + +static struct irq_chip altera_msi_irq_chip = { + .name = "Altera PCIe MSI", + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info altera_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), + .chip = &altera_msi_irq_chip, +}; + +static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct altera_msi *msi = irq_data_get_irq_chip_data(data); + phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32)); + + msg->address_lo = lower_32_bits(addr); + msg->address_hi = upper_32_bits(addr); + msg->data = data->hwirq; + + dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); +} + +static int altera_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static struct irq_chip altera_msi_bottom_irq_chip = { + .name = "Altera MSI", + .irq_compose_msi_msg = altera_compose_msi_msg, + .irq_set_affinity = altera_msi_set_affinity, +}; + +static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct altera_msi *msi = domain->host_data; + unsigned long bit; + u32 mask; + + WARN_ON(nr_irqs != 1); + mutex_lock(&msi->lock); + + bit = find_first_zero_bit(msi->used, msi->num_of_vectors); + if (bit >= msi->num_of_vectors) { + mutex_unlock(&msi->lock); + return -ENOSPC; + } + + set_bit(bit, msi->used); + + mutex_unlock(&msi->lock); + + irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip, + domain->host_data, handle_simple_irq, + NULL, NULL); + + mask = msi_readl(msi, MSI_INTMASK); + mask |= 1 << bit; + msi_writel(msi, mask, MSI_INTMASK); + + return 0; +} + +static void altera_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct altera_msi *msi = irq_data_get_irq_chip_data(d); + u32 mask; + + mutex_lock(&msi->lock); + + if (!test_bit(d->hwirq, msi->used)) { + dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n", + d->hwirq); + } else { + __clear_bit(d->hwirq, msi->used); + mask = msi_readl(msi, MSI_INTMASK); + mask &= ~(1 << d->hwirq); + msi_writel(msi, mask, MSI_INTMASK); + } + + mutex_unlock(&msi->lock); +} + +static const struct irq_domain_ops msi_domain_ops = { + .alloc = altera_irq_domain_alloc, + .free = altera_irq_domain_free, +}; + +static int altera_allocate_domains(struct altera_msi *msi) +{ + struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node); + + msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors, + &msi_domain_ops, msi); + if (!msi->inner_domain) { + dev_err(&msi->pdev->dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + msi->msi_domain = pci_msi_create_irq_domain(fwnode, + &altera_msi_domain_info, msi->inner_domain); + if (!msi->msi_domain) { + dev_err(&msi->pdev->dev, "failed to create MSI domain\n"); + irq_domain_remove(msi->inner_domain); + return -ENOMEM; + } + + return 0; +} + +static void altera_free_domains(struct altera_msi *msi) +{ + irq_domain_remove(msi->msi_domain); + irq_domain_remove(msi->inner_domain); +} + +static int altera_msi_remove(struct platform_device *pdev) +{ + struct altera_msi *msi = platform_get_drvdata(pdev); + + msi_writel(msi, 0, MSI_INTMASK); + irq_set_chained_handler(msi->irq, NULL); + irq_set_handler_data(msi->irq, NULL); + + altera_free_domains(msi); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static int altera_msi_probe(struct platform_device *pdev) +{ + struct altera_msi *msi; + struct device_node *np = pdev->dev.of_node; + struct resource *res; + int ret; + + msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi), + GFP_KERNEL); + if (!msi) + return -ENOMEM; + + mutex_init(&msi->lock); + msi->pdev = pdev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); + msi->csr_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(msi->csr_base)) { + dev_err(&pdev->dev, "failed to map csr memory\n"); + return PTR_ERR(msi->csr_base); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "vector_slave"); + msi->vector_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(msi->vector_base)) { + dev_err(&pdev->dev, "failed to map vector_slave memory\n"); + return PTR_ERR(msi->vector_base); + } + + msi->vector_phy = res->start; + + if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) { + dev_err(&pdev->dev, "failed to parse the number of vectors\n"); + return -EINVAL; + } + + ret = altera_allocate_domains(msi); + if (ret) + return ret; + + msi->irq = platform_get_irq(pdev, 0); + if (msi->irq < 0) { + dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq); + ret = msi->irq; + goto err; + } + + irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi); + platform_set_drvdata(pdev, msi); + + return 0; + +err: + altera_msi_remove(pdev); + return ret; +} + +static const struct of_device_id altera_msi_of_match[] = { + { .compatible = "altr,msi-1.0", NULL }, + { }, +}; + +static struct platform_driver altera_msi_driver = { + .driver = { + .name = "altera-msi", + .of_match_table = altera_msi_of_match, + }, + .probe = altera_msi_probe, + .remove = altera_msi_remove, +}; + +static int __init altera_msi_init(void) +{ + return platform_driver_register(&altera_msi_driver); +} +subsys_initcall(altera_msi_init); diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c new file mode 100644 index 000000000000..7d05e51205b3 --- /dev/null +++ b/drivers/pci/controller/pcie-altera.c @@ -0,0 +1,645 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright Altera Corporation (C) 2013-2015. All rights reserved + * + * Author: Ley Foon Tan + * Description: Altera PCIe host controller driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +#define RP_TX_REG0 0x2000 +#define RP_TX_REG1 0x2004 +#define RP_TX_CNTRL 0x2008 +#define RP_TX_EOP 0x2 +#define RP_TX_SOP 0x1 +#define RP_RXCPL_STATUS 0x2010 +#define RP_RXCPL_EOP 0x2 +#define RP_RXCPL_SOP 0x1 +#define RP_RXCPL_REG0 0x2014 +#define RP_RXCPL_REG1 0x2018 +#define P2A_INT_STATUS 0x3060 +#define P2A_INT_STS_ALL 0xf +#define P2A_INT_ENABLE 0x3070 +#define P2A_INT_ENA_ALL 0xf +#define RP_LTSSM 0x3c64 +#define RP_LTSSM_MASK 0x1f +#define LTSSM_L0 0xf + +#define PCIE_CAP_OFFSET 0x80 +/* TLP configuration type 0 and 1 */ +#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ +#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ +#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */ +#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */ +#define TLP_PAYLOAD_SIZE 0x01 +#define TLP_READ_TAG 0x1d +#define TLP_WRITE_TAG 0x10 +#define RP_DEVFN 0 +#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) +#define TLP_CFGRD_DW0(pcie, bus) \ + ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \ + : TLP_FMTTYPE_CFGRD1) << 24) | \ + TLP_PAYLOAD_SIZE) +#define TLP_CFGWR_DW0(pcie, bus) \ + ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGWR0 \ + : TLP_FMTTYPE_CFGWR1) << 24) | \ + TLP_PAYLOAD_SIZE) +#define TLP_CFG_DW1(pcie, tag, be) \ + (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) +#define TLP_CFG_DW2(bus, devfn, offset) \ + (((bus) << 24) | ((devfn) << 16) | (offset)) +#define TLP_COMP_STATUS(s) (((s) >> 13) & 7) +#define TLP_HDR_SIZE 3 +#define TLP_LOOP 500 + +#define LINK_UP_TIMEOUT HZ +#define LINK_RETRAIN_TIMEOUT HZ + +#define DWORD_MASK 3 + +struct altera_pcie { + struct platform_device *pdev; + void __iomem *cra_base; /* DT Cra */ + int irq; + u8 root_bus_nr; + struct irq_domain *irq_domain; + struct resource bus_range; + struct list_head resources; +}; + +struct tlp_rp_regpair_t { + u32 ctrl; + u32 reg0; + u32 reg1; +}; + +static inline void cra_writel(struct altera_pcie *pcie, const u32 value, + const u32 reg) +{ + writel_relaxed(value, pcie->cra_base + reg); +} + +static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg) +{ + return readl_relaxed(pcie->cra_base + reg); +} + +static bool altera_pcie_link_up(struct altera_pcie *pcie) +{ + return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); +} + +/* + * Altera PCIe port uses BAR0 of RC's configuration space as the translation + * from PCI bus to native BUS. Entire DDR region is mapped into PCIe space + * using these registers, so it can be reached by DMA from EP devices. + * This BAR0 will also access to MSI vector when receiving MSI/MSIX interrupt + * from EP devices, eventually trigger interrupt to GIC. The BAR0 of bridge + * should be hidden during enumeration to avoid the sizing and resource + * allocation by PCIe core. + */ +static bool altera_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn, + int offset) +{ + if (pci_is_root_bus(bus) && (devfn == 0) && + (offset == PCI_BASE_ADDRESS_0)) + return true; + + return false; +} + +static void tlp_write_tx(struct altera_pcie *pcie, + struct tlp_rp_regpair_t *tlp_rp_regdata) +{ + cra_writel(pcie, tlp_rp_regdata->reg0, RP_TX_REG0); + cra_writel(pcie, tlp_rp_regdata->reg1, RP_TX_REG1); + cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL); +} + +static bool altera_pcie_valid_device(struct altera_pcie *pcie, + struct pci_bus *bus, int dev) +{ + /* If there is no link, then there is no device */ + if (bus->number != pcie->root_bus_nr) { + if (!altera_pcie_link_up(pcie)) + return false; + } + + /* access only one slot on each root port */ + if (bus->number == pcie->root_bus_nr && dev > 0) + return false; + + return true; +} + +static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) +{ + int i; + bool sop = false; + u32 ctrl; + u32 reg0, reg1; + u32 comp_status = 1; + + /* + * Minimum 2 loops to read TLP headers and 1 loop to read data + * payload. + */ + for (i = 0; i < TLP_LOOP; i++) { + ctrl = cra_readl(pcie, RP_RXCPL_STATUS); + if ((ctrl & RP_RXCPL_SOP) || (ctrl & RP_RXCPL_EOP) || sop) { + reg0 = cra_readl(pcie, RP_RXCPL_REG0); + reg1 = cra_readl(pcie, RP_RXCPL_REG1); + + if (ctrl & RP_RXCPL_SOP) { + sop = true; + comp_status = TLP_COMP_STATUS(reg1); + } + + if (ctrl & RP_RXCPL_EOP) { + if (comp_status) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (value) + *value = reg0; + + return PCIBIOS_SUCCESSFUL; + } + } + udelay(5); + } + + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers, + u32 data, bool align) +{ + struct tlp_rp_regpair_t tlp_rp_regdata; + + tlp_rp_regdata.reg0 = headers[0]; + tlp_rp_regdata.reg1 = headers[1]; + tlp_rp_regdata.ctrl = RP_TX_SOP; + tlp_write_tx(pcie, &tlp_rp_regdata); + + if (align) { + tlp_rp_regdata.reg0 = headers[2]; + tlp_rp_regdata.reg1 = 0; + tlp_rp_regdata.ctrl = 0; + tlp_write_tx(pcie, &tlp_rp_regdata); + + tlp_rp_regdata.reg0 = data; + tlp_rp_regdata.reg1 = 0; + } else { + tlp_rp_regdata.reg0 = headers[2]; + tlp_rp_regdata.reg1 = data; + } + + tlp_rp_regdata.ctrl = RP_TX_EOP; + tlp_write_tx(pcie, &tlp_rp_regdata); +} + +static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, + int where, u8 byte_en, u32 *value) +{ + u32 headers[TLP_HDR_SIZE]; + + headers[0] = TLP_CFGRD_DW0(pcie, bus); + headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); + headers[2] = TLP_CFG_DW2(bus, devfn, where); + + tlp_write_packet(pcie, headers, 0, false); + + return tlp_read_packet(pcie, value); +} + +static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, + int where, u8 byte_en, u32 value) +{ + u32 headers[TLP_HDR_SIZE]; + int ret; + + headers[0] = TLP_CFGWR_DW0(pcie, bus); + headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en); + headers[2] = TLP_CFG_DW2(bus, devfn, where); + + /* check alignment to Qword */ + if ((where & 0x7) == 0) + tlp_write_packet(pcie, headers, value, true); + else + tlp_write_packet(pcie, headers, value, false); + + ret = tlp_read_packet(pcie, NULL); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + /* + * Monitor changes to PCI_PRIMARY_BUS register on root port + * and update local copy of root bus number accordingly. + */ + if ((bus == pcie->root_bus_nr) && (where == PCI_PRIMARY_BUS)) + pcie->root_bus_nr = (u8)(value); + + return PCIBIOS_SUCCESSFUL; +} + +static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int where, int size, + u32 *value) +{ + int ret; + u32 data; + u8 byte_en; + + switch (size) { + case 1: + byte_en = 1 << (where & 3); + break; + case 2: + byte_en = 3 << (where & 3); + break; + default: + byte_en = 0xf; + break; + } + + ret = tlp_cfg_dword_read(pcie, busno, devfn, + (where & ~DWORD_MASK), byte_en, &data); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + switch (size) { + case 1: + *value = (data >> (8 * (where & 0x3))) & 0xff; + break; + case 2: + *value = (data >> (8 * (where & 0x2))) & 0xffff; + break; + default: + *value = data; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int where, int size, + u32 value) +{ + u32 data32; + u32 shift = 8 * (where & 3); + u8 byte_en; + + switch (size) { + case 1: + data32 = (value & 0xff) << shift; + byte_en = 1 << (where & 3); + break; + case 2: + data32 = (value & 0xffff) << shift; + byte_en = 3 << (where & 3); + break; + default: + data32 = value; + byte_en = 0xf; + break; + } + + return tlp_cfg_dword_write(pcie, busno, devfn, (where & ~DWORD_MASK), + byte_en, data32); +} + +static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + struct altera_pcie *pcie = bus->sysdata; + + if (altera_pcie_hide_rc_bar(bus, devfn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn))) { + *value = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + return _altera_pcie_cfg_read(pcie, bus->number, devfn, where, size, + value); +} + +static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct altera_pcie *pcie = bus->sysdata; + + if (altera_pcie_hide_rc_bar(bus, devfn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn))) + return PCIBIOS_DEVICE_NOT_FOUND; + + return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size, + value); +} + +static struct pci_ops altera_pcie_ops = { + .read = altera_pcie_cfg_read, + .write = altera_pcie_cfg_write, +}; + +static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int offset, u16 *value) +{ + u32 data; + int ret; + + ret = _altera_pcie_cfg_read(pcie, busno, devfn, + PCIE_CAP_OFFSET + offset, sizeof(*value), + &data); + *value = data; + return ret; +} + +static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int offset, u16 value) +{ + return _altera_pcie_cfg_write(pcie, busno, devfn, + PCIE_CAP_OFFSET + offset, sizeof(value), + value); +} + +static void altera_wait_link_retrain(struct altera_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + u16 reg16; + unsigned long start_jiffies; + + /* Wait for link training end. */ + start_jiffies = jiffies; + for (;;) { + altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, + PCI_EXP_LNKSTA, ®16); + if (!(reg16 & PCI_EXP_LNKSTA_LT)) + break; + + if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) { + dev_err(dev, "link retrain timeout\n"); + break; + } + udelay(100); + } + + /* Wait for link is up */ + start_jiffies = jiffies; + for (;;) { + if (altera_pcie_link_up(pcie)) + break; + + if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { + dev_err(dev, "link up timeout\n"); + break; + } + udelay(100); + } +} + +static void altera_pcie_retrain(struct altera_pcie *pcie) +{ + u16 linkcap, linkstat, linkctl; + + if (!altera_pcie_link_up(pcie)) + return; + + /* + * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but + * current speed is 2.5 GB/s. + */ + altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKCAP, + &linkcap); + if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) + return; + + altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKSTA, + &linkstat); + if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, + PCI_EXP_LNKCTL, &linkctl); + linkctl |= PCI_EXP_LNKCTL_RL; + altera_write_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, + PCI_EXP_LNKCTL, linkctl); + + altera_wait_link_retrain(pcie); + } +} + +static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { + .map = altera_pcie_intx_map, + .xlate = pci_irqd_intx_xlate, +}; + +static void altera_pcie_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct altera_pcie *pcie; + struct device *dev; + unsigned long status; + u32 bit; + u32 virq; + + chained_irq_enter(chip, desc); + pcie = irq_desc_get_handler_data(desc); + dev = &pcie->pdev->dev; + + while ((status = cra_readl(pcie, P2A_INT_STATUS) + & P2A_INT_STS_ALL) != 0) { + for_each_set_bit(bit, &status, PCI_NUM_INTX) { + /* clear interrupts */ + cra_writel(pcie, 1 << bit, P2A_INT_STATUS); + + virq = irq_find_mapping(pcie->irq_domain, bit); + if (virq) + generic_handle_irq(virq); + else + dev_err(dev, "unexpected IRQ, INT%d\n", bit); + } + } + + chained_irq_exit(chip, desc); +} + +static int altera_pcie_parse_request_of_pci_ranges(struct altera_pcie *pcie) +{ + int err, res_valid = 0; + struct device *dev = &pcie->pdev->dev; + struct resource_entry *win; + + err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, + &pcie->resources, NULL); + if (err) + return err; + + err = devm_request_pci_bus_resources(dev, &pcie->resources); + if (err) + goto out_release_res; + + resource_list_for_each_entry(win, &pcie->resources) { + struct resource *res = win->res; + + if (resource_type(res) == IORESOURCE_MEM) + res_valid |= !(res->flags & IORESOURCE_PREFETCH); + } + + if (res_valid) + return 0; + + dev_err(dev, "non-prefetchable memory resource required\n"); + err = -EINVAL; + +out_release_res: + pci_free_resource_list(&pcie->resources); + return err; +} + +static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + + /* Setup INTx */ + pcie->irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX, + &intx_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return -ENOMEM; + } + + return 0; +} + +static int altera_pcie_parse_dt(struct altera_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct platform_device *pdev = pcie->pdev; + struct resource *cra; + + cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra"); + pcie->cra_base = devm_ioremap_resource(dev, cra); + if (IS_ERR(pcie->cra_base)) + return PTR_ERR(pcie->cra_base); + + /* setup IRQ */ + pcie->irq = platform_get_irq(pdev, 0); + if (pcie->irq < 0) { + dev_err(dev, "failed to get IRQ: %d\n", pcie->irq); + return pcie->irq; + } + + irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie); + return 0; +} + +static void altera_pcie_host_init(struct altera_pcie *pcie) +{ + altera_pcie_retrain(pcie); +} + +static int altera_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct altera_pcie *pcie; + struct pci_bus *bus; + struct pci_bus *child; + struct pci_host_bridge *bridge; + int ret; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!bridge) + return -ENOMEM; + + pcie = pci_host_bridge_priv(bridge); + pcie->pdev = pdev; + + ret = altera_pcie_parse_dt(pcie); + if (ret) { + dev_err(dev, "Parsing DT failed\n"); + return ret; + } + + INIT_LIST_HEAD(&pcie->resources); + + ret = altera_pcie_parse_request_of_pci_ranges(pcie); + if (ret) { + dev_err(dev, "Failed add resources\n"); + return ret; + } + + ret = altera_pcie_init_irq_domain(pcie); + if (ret) { + dev_err(dev, "Failed creating IRQ Domain\n"); + return ret; + } + + /* clear all interrupts */ + cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS); + /* enable all interrupts */ + cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); + altera_pcie_host_init(pcie); + + list_splice_init(&pcie->resources, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = pcie; + bridge->busnr = pcie->root_bus_nr; + bridge->ops = &altera_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_scan_root_bus_bridge(bridge); + if (ret < 0) + return ret; + + bus = bridge->bus; + + pci_assign_unassigned_bus_resources(bus); + + /* Configure PCI Express setting. */ + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(bus); + return ret; +} + +static const struct of_device_id altera_pcie_of_match[] = { + { .compatible = "altr,pcie-root-port-1.0", }, + {}, +}; + +static struct platform_driver altera_pcie_driver = { + .probe = altera_pcie_probe, + .driver = { + .name = "altera-pcie", + .of_match_table = altera_pcie_of_match, + .suppress_bind_attrs = true, + }, +}; + +builtin_platform_driver(altera_pcie_driver); diff --git a/drivers/pci/controller/pcie-cadence-ep.c b/drivers/pci/controller/pcie-cadence-ep.c new file mode 100644 index 000000000000..3d8283e450a9 --- /dev/null +++ b/drivers/pci/controller/pcie-cadence-ep.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Cadence +// Cadence PCIe endpoint controller driver. +// Author: Cyrille Pitchen + +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-cadence.h" + +#define CDNS_PCIE_EP_MIN_APERTURE 128 /* 128 bytes */ +#define CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE 0x1 +#define CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x3 + +/** + * struct cdns_pcie_ep - private data for this PCIe endpoint controller driver + * @pcie: Cadence PCIe controller + * @max_regions: maximum number of regions supported by hardware + * @ob_region_map: bitmask of mapped outbound regions + * @ob_addr: base addresses in the AXI bus where the outbound regions start + * @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ + * dedicated outbound regions is mapped. + * @irq_cpu_addr: base address in the CPU space where a write access triggers + * the sending of a memory write (MSI) / normal message (legacy + * IRQ) TLP through the PCIe bus. + * @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ + * dedicated outbound region. + * @irq_pci_fn: the latest PCI function that has updated the mapping of + * the MSI/legacy IRQ dedicated outbound region. + * @irq_pending: bitmask of asserted legacy IRQs. + */ +struct cdns_pcie_ep { + struct cdns_pcie pcie; + u32 max_regions; + unsigned long ob_region_map; + phys_addr_t *ob_addr; + phys_addr_t irq_phys_addr; + void __iomem *irq_cpu_addr; + u64 irq_pci_addr; + u8 irq_pci_fn; + u8 irq_pending; +}; + +static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, + struct pci_epf_header *hdr) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + + cdns_pcie_ep_fn_writew(pcie, fn, PCI_DEVICE_ID, hdr->deviceid); + cdns_pcie_ep_fn_writeb(pcie, fn, PCI_REVISION_ID, hdr->revid); + cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CLASS_PROG, hdr->progif_code); + cdns_pcie_ep_fn_writew(pcie, fn, PCI_CLASS_DEVICE, + hdr->subclass_code | hdr->baseclass_code << 8); + cdns_pcie_ep_fn_writeb(pcie, fn, PCI_CACHE_LINE_SIZE, + hdr->cache_line_size); + cdns_pcie_ep_fn_writew(pcie, fn, PCI_SUBSYSTEM_ID, hdr->subsys_id); + cdns_pcie_ep_fn_writeb(pcie, fn, PCI_INTERRUPT_PIN, hdr->interrupt_pin); + + /* + * Vendor ID can only be modified from function 0, all other functions + * use the same vendor ID as function 0. + */ + if (fn == 0) { + /* Update the vendor IDs. */ + u32 id = CDNS_PCIE_LM_ID_VENDOR(hdr->vendorid) | + CDNS_PCIE_LM_ID_SUBSYS(hdr->subsys_vendor_id); + + cdns_pcie_writel(pcie, CDNS_PCIE_LM_ID, id); + } + + return 0; +} + +static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, + struct pci_epf_bar *epf_bar) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + dma_addr_t bar_phys = epf_bar->phys_addr; + enum pci_barno bar = epf_bar->barno; + int flags = epf_bar->flags; + u32 addr0, addr1, reg, cfg, b, aperture, ctrl; + u64 sz; + + /* BAR size is 2^(aperture + 7) */ + sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE); + /* + * roundup_pow_of_two() returns an unsigned long, which is not suited + * for 64bit values. + */ + sz = 1ULL << fls64(sz - 1); + aperture = ilog2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */ + + if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS; + } else { + bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH); + bool is_64bits = sz > SZ_2G; + + if (is_64bits && (bar & 1)) + return -EINVAL; + + if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + + if (is_64bits && is_prefetch) + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; + else if (is_prefetch) + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS; + else if (is_64bits) + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS; + else + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS; + } + + addr0 = lower_32_bits(bar_phys); + addr1 = upper_32_bits(bar_phys); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), + addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), + addr1); + + if (bar < BAR_4) { + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); + b = bar; + } else { + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); + b = bar - BAR_4; + } + + cfg = cdns_pcie_readl(pcie, reg); + cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | + CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); + cfg |= (CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) | + CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl)); + cdns_pcie_writel(pcie, reg, cfg); + + return 0; +} + +static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, + struct pci_epf_bar *epf_bar) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + enum pci_barno bar = epf_bar->barno; + u32 reg, cfg, b, ctrl; + + if (bar < BAR_4) { + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn); + b = bar; + } else { + reg = CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn); + b = bar - BAR_4; + } + + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED; + cfg = cdns_pcie_readl(pcie, reg); + cfg &= ~(CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | + CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); + cfg |= CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl); + cdns_pcie_writel(pcie, reg, cfg); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0); +} + +static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr, + u64 pci_addr, size_t size) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + u32 r; + + r = find_first_zero_bit(&ep->ob_region_map, + sizeof(ep->ob_region_map) * BITS_PER_LONG); + if (r >= ep->max_regions - 1) { + dev_err(&epc->dev, "no free outbound region\n"); + return -EINVAL; + } + + cdns_pcie_set_outbound_region(pcie, fn, r, false, addr, pci_addr, size); + + set_bit(r, &ep->ob_region_map); + ep->ob_addr[r] = addr; + + return 0; +} + +static void cdns_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, + phys_addr_t addr) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + u32 r; + + for (r = 0; r < ep->max_regions - 1; r++) + if (ep->ob_addr[r] == addr) + break; + + if (r == ep->max_regions - 1) + return; + + cdns_pcie_reset_outbound_region(pcie, r); + + ep->ob_addr[r] = 0; + clear_bit(r, &ep->ob_region_map); +} + +static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 mmc) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; + u16 flags; + + /* + * Set the Multiple Message Capable bitfield into the Message Control + * register. + */ + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); + flags = (flags & ~PCI_MSI_FLAGS_QMASK) | (mmc << 1); + flags |= PCI_MSI_FLAGS_64BIT; + flags &= ~PCI_MSI_FLAGS_MASKBIT; + cdns_pcie_ep_fn_writew(pcie, fn, cap + PCI_MSI_FLAGS, flags); + + return 0; +} + +static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; + u16 flags, mmc, mme; + + /* Validate that the MSI feature is actually enabled. */ + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); + if (!(flags & PCI_MSI_FLAGS_ENABLE)) + return -EINVAL; + + /* + * Get the Multiple Message Enable bitfield from the Message Control + * register. + */ + mmc = (flags & PCI_MSI_FLAGS_QMASK) >> 1; + mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; + + return mme; +} + +static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, + u8 intx, bool is_asserted) +{ + struct cdns_pcie *pcie = &ep->pcie; + u32 r = ep->max_regions - 1; + u32 offset; + u16 status; + u8 msg_code; + + intx &= 3; + + /* Set the outbound region if needed. */ + if (unlikely(ep->irq_pci_addr != CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY || + ep->irq_pci_fn != fn)) { + /* Last region was reserved for IRQ writes. */ + cdns_pcie_set_outbound_region_for_normal_msg(pcie, fn, r, + ep->irq_phys_addr); + ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY; + ep->irq_pci_fn = fn; + } + + if (is_asserted) { + ep->irq_pending |= BIT(intx); + msg_code = MSG_CODE_ASSERT_INTA + intx; + } else { + ep->irq_pending &= ~BIT(intx); + msg_code = MSG_CODE_DEASSERT_INTA + intx; + } + + status = cdns_pcie_ep_fn_readw(pcie, fn, PCI_STATUS); + if (((status & PCI_STATUS_INTERRUPT) != 0) ^ (ep->irq_pending != 0)) { + status ^= PCI_STATUS_INTERRUPT; + cdns_pcie_ep_fn_writew(pcie, fn, PCI_STATUS, status); + } + + offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) | + CDNS_PCIE_NORMAL_MSG_CODE(msg_code) | + CDNS_PCIE_MSG_NO_DATA; + writel(0, ep->irq_cpu_addr + offset); +} + +static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 intx) +{ + u16 cmd; + + cmd = cdns_pcie_ep_fn_readw(&ep->pcie, fn, PCI_COMMAND); + if (cmd & PCI_COMMAND_INTX_DISABLE) + return -EINVAL; + + cdns_pcie_ep_assert_intx(ep, fn, intx, true); + /* + * The mdelay() value was taken from dra7xx_pcie_raise_legacy_irq() + * from drivers/pci/dwc/pci-dra7xx.c + */ + mdelay(1); + cdns_pcie_ep_assert_intx(ep, fn, intx, false); + return 0; +} + +static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, + u8 interrupt_num) +{ + struct cdns_pcie *pcie = &ep->pcie; + u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; + u16 flags, mme, data, data_mask; + u8 msi_count; + u64 pci_addr, pci_addr_mask = 0xff; + + /* Check whether the MSI feature has been enabled by the PCI host. */ + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS); + if (!(flags & PCI_MSI_FLAGS_ENABLE)) + return -EINVAL; + + /* Get the number of enabled MSIs */ + mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4; + msi_count = 1 << mme; + if (!interrupt_num || interrupt_num > msi_count) + return -EINVAL; + + /* Compute the data value to be written. */ + data_mask = msi_count - 1; + data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64); + data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask); + + /* Get the PCI address where to write the data into. */ + pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI); + pci_addr <<= 32; + pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO); + pci_addr &= GENMASK_ULL(63, 2); + + /* Set the outbound region if needed. */ + if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) || + ep->irq_pci_fn != fn)) { + /* Last region was reserved for IRQ writes. */ + cdns_pcie_set_outbound_region(pcie, fn, ep->max_regions - 1, + false, + ep->irq_phys_addr, + pci_addr & ~pci_addr_mask, + pci_addr_mask + 1); + ep->irq_pci_addr = (pci_addr & ~pci_addr_mask); + ep->irq_pci_fn = fn; + } + writew(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask)); + + return 0; +} + +static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, + enum pci_epc_irq_type type, u8 interrupt_num) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + + switch (type) { + case PCI_EPC_IRQ_LEGACY: + return cdns_pcie_ep_send_legacy_irq(ep, fn, 0); + + case PCI_EPC_IRQ_MSI: + return cdns_pcie_ep_send_msi_irq(ep, fn, interrupt_num); + + default: + break; + } + + return -EINVAL; +} + +static int cdns_pcie_ep_start(struct pci_epc *epc) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + struct pci_epf *epf; + u32 cfg; + + /* + * BIT(0) is hardwired to 1, hence function 0 is always enabled + * and can't be disabled anyway. + */ + cfg = BIT(0); + list_for_each_entry(epf, &epc->pci_epf, list) + cfg |= BIT(epf->func_no); + cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg); + + /* + * The PCIe links are automatically established by the controller + * once for all at powerup: the software can neither start nor stop + * those links later at runtime. + * + * Then we only have to notify the EP core that our links are already + * established. However we don't call directly pci_epc_linkup() because + * we've already locked the epc->lock. + */ + list_for_each_entry(epf, &epc->pci_epf, list) + pci_epf_linkup(epf); + + return 0; +} + +static const struct pci_epc_ops cdns_pcie_epc_ops = { + .write_header = cdns_pcie_ep_write_header, + .set_bar = cdns_pcie_ep_set_bar, + .clear_bar = cdns_pcie_ep_clear_bar, + .map_addr = cdns_pcie_ep_map_addr, + .unmap_addr = cdns_pcie_ep_unmap_addr, + .set_msi = cdns_pcie_ep_set_msi, + .get_msi = cdns_pcie_ep_get_msi, + .raise_irq = cdns_pcie_ep_raise_irq, + .start = cdns_pcie_ep_start, +}; + +static const struct of_device_id cdns_pcie_ep_of_match[] = { + { .compatible = "cdns,cdns-pcie-ep" }, + + { }, +}; + +static int cdns_pcie_ep_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct cdns_pcie_ep *ep; + struct cdns_pcie *pcie; + struct pci_epc *epc; + struct resource *res; + int ret; + + ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + pcie = &ep->pcie; + pcie->is_rc = false; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); + pcie->reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->reg_base)) { + dev_err(dev, "missing \"reg\"\n"); + return PTR_ERR(pcie->reg_base); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); + if (!res) { + dev_err(dev, "missing \"mem\"\n"); + return -EINVAL; + } + pcie->mem_res = res; + + ret = of_property_read_u32(np, "cdns,max-outbound-regions", + &ep->max_regions); + if (ret < 0) { + dev_err(dev, "missing \"cdns,max-outbound-regions\"\n"); + return ret; + } + ep->ob_addr = devm_kzalloc(dev, ep->max_regions * sizeof(*ep->ob_addr), + GFP_KERNEL); + if (!ep->ob_addr) + return -ENOMEM; + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync() failed\n"); + goto err_get_sync; + } + + /* Disable all but function 0 (anyway BIT(0) is hardwired to 1). */ + cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, BIT(0)); + + epc = devm_pci_epc_create(dev, &cdns_pcie_epc_ops); + if (IS_ERR(epc)) { + dev_err(dev, "failed to create epc device\n"); + ret = PTR_ERR(epc); + goto err_init; + } + + epc_set_drvdata(epc, ep); + + if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0) + epc->max_functions = 1; + + ret = pci_epc_mem_init(epc, pcie->mem_res->start, + resource_size(pcie->mem_res)); + if (ret < 0) { + dev_err(dev, "failed to initialize the memory space\n"); + goto err_init; + } + + ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr, + SZ_128K); + if (!ep->irq_cpu_addr) { + dev_err(dev, "failed to reserve memory space for MSI\n"); + ret = -ENOMEM; + goto free_epc_mem; + } + ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE; + + return 0; + + free_epc_mem: + pci_epc_mem_exit(epc); + + err_init: + pm_runtime_put_sync(dev); + + err_get_sync: + pm_runtime_disable(dev); + + return ret; +} + +static void cdns_pcie_ep_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + ret = pm_runtime_put_sync(dev); + if (ret < 0) + dev_dbg(dev, "pm_runtime_put_sync failed\n"); + + pm_runtime_disable(dev); + + /* The PCIe controller can't be disabled. */ +} + +static struct platform_driver cdns_pcie_ep_driver = { + .driver = { + .name = "cdns-pcie-ep", + .of_match_table = cdns_pcie_ep_of_match, + }, + .probe = cdns_pcie_ep_probe, + .shutdown = cdns_pcie_ep_shutdown, +}; +builtin_platform_driver(cdns_pcie_ep_driver); diff --git a/drivers/pci/controller/pcie-cadence-host.c b/drivers/pci/controller/pcie-cadence-host.c new file mode 100644 index 000000000000..a4ebbd37b553 --- /dev/null +++ b/drivers/pci/controller/pcie-cadence-host.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Cadence +// Cadence PCIe host controller driver. +// Author: Cyrille Pitchen + +#include +#include +#include +#include +#include + +#include "pcie-cadence.h" + +/** + * struct cdns_pcie_rc - private data for this PCIe Root Complex driver + * @pcie: Cadence PCIe controller + * @dev: pointer to PCIe device + * @cfg_res: start/end offsets in the physical system memory to map PCI + * configuration space accesses + * @bus_range: first/last buses behind the PCIe host controller + * @cfg_base: IO mapped window to access the PCI configuration space of a + * single function at a time + * @max_regions: maximum number of regions supported by the hardware + * @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address + * translation (nbits sets into the "no BAR match" register) + * @vendor_id: PCI vendor ID + * @device_id: PCI device ID + */ +struct cdns_pcie_rc { + struct cdns_pcie pcie; + struct device *dev; + struct resource *cfg_res; + struct resource *bus_range; + void __iomem *cfg_base; + u32 max_regions; + u32 no_bar_nbits; + u16 vendor_id; + u16 device_id; +}; + +static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); + struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge); + struct cdns_pcie *pcie = &rc->pcie; + unsigned int busn = bus->number; + u32 addr0, desc0; + + if (busn == rc->bus_range->start) { + /* + * Only the root port (devfn == 0) is connected to this bus. + * All other PCI devices are behind some bridge hence on another + * bus. + */ + if (devfn) + return NULL; + + return pcie->reg_base + (where & 0xfff); + } + + /* Update Output registers for AXI region 0. */ + addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) | + CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) | + CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(busn); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(0), addr0); + + /* Configuration Type 0 or Type 1 access. */ + desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | + CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); + /* + * The bus number was already set once for all in desc1 by + * cdns_pcie_host_init_address_translation(). + */ + if (busn == rc->bus_range->start + 1) + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0; + else + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1; + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(0), desc0); + + return rc->cfg_base + (where & 0xfff); +} + +static struct pci_ops cdns_pcie_host_ops = { + .map_bus = cdns_pci_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static const struct of_device_id cdns_pcie_host_of_match[] = { + { .compatible = "cdns,cdns-pcie-host" }, + + { }, +}; + +static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) +{ + struct cdns_pcie *pcie = &rc->pcie; + u32 value, ctrl; + + /* + * Set the root complex BAR configuration register: + * - disable both BAR0 and BAR1. + * - enable Prefetchable Memory Base and Limit registers in type 1 + * config space (64 bits). + * - enable IO Base and Limit registers in type 1 config + * space (32 bits). + */ + ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED; + value = CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(ctrl) | + CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(ctrl) | + CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE | + CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS | + CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE | + CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS; + cdns_pcie_writel(pcie, CDNS_PCIE_LM_RC_BAR_CFG, value); + + /* Set root port configuration space */ + if (rc->vendor_id != 0xffff) + cdns_pcie_rp_writew(pcie, PCI_VENDOR_ID, rc->vendor_id); + if (rc->device_id != 0xffff) + cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id); + + cdns_pcie_rp_writeb(pcie, PCI_CLASS_REVISION, 0); + cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0); + cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); + + return 0; +} + +static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) +{ + struct cdns_pcie *pcie = &rc->pcie; + struct resource *cfg_res = rc->cfg_res; + struct resource *mem_res = pcie->mem_res; + struct resource *bus_range = rc->bus_range; + struct device *dev = rc->dev; + struct device_node *np = dev->of_node; + struct of_pci_range_parser parser; + struct of_pci_range range; + u32 addr0, addr1, desc1; + u64 cpu_addr; + int r, err; + + /* + * Reserve region 0 for PCI configure space accesses: + * OB_REGION_PCI_ADDR0 and OB_REGION_DESC0 are updated dynamically by + * cdns_pci_map_bus(), other region registers are set here once for all. + */ + addr1 = 0; /* Should be programmed to zero. */ + desc1 = CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus_range->start); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(0), addr1); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(0), desc1); + + cpu_addr = cfg_res->start - mem_res->start; + addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(12) | + (lower_32_bits(cpu_addr) & GENMASK(31, 8)); + addr1 = upper_32_bits(cpu_addr); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(0), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(0), addr1); + + err = of_pci_range_parser_init(&parser, np); + if (err) + return err; + + r = 1; + for_each_of_pci_range(&parser, &range) { + bool is_io; + + if (r >= rc->max_regions) + break; + + if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM) + is_io = false; + else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO) + is_io = true; + else + continue; + + cdns_pcie_set_outbound_region(pcie, 0, r, is_io, + range.cpu_addr, + range.pci_addr, + range.size); + r++; + } + + /* + * Set Root Port no BAR match Inbound Translation registers: + * needed for MSI and DMA. + * Root Port BAR0 and BAR1 are disabled, hence no need to set their + * inbound translation registers. + */ + addr0 = CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(rc->no_bar_nbits); + addr1 = 0; + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR0(RP_NO_BAR), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR1(RP_NO_BAR), addr1); + + return 0; +} + +static int cdns_pcie_host_init(struct device *dev, + struct list_head *resources, + struct cdns_pcie_rc *rc) +{ + struct resource *bus_range = NULL; + int err; + + /* Parse our PCI ranges and request their resources */ + err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); + if (err) + return err; + + rc->bus_range = bus_range; + rc->pcie.bus = bus_range->start; + + err = cdns_pcie_host_init_root_port(rc); + if (err) + goto err_out; + + err = cdns_pcie_host_init_address_translation(rc); + if (err) + goto err_out; + + return 0; + + err_out: + pci_free_resource_list(resources); + return err; +} + +static int cdns_pcie_host_probe(struct platform_device *pdev) +{ + const char *type; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct pci_host_bridge *bridge; + struct list_head resources; + struct cdns_pcie_rc *rc; + struct cdns_pcie *pcie; + struct resource *res; + int ret; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); + if (!bridge) + return -ENOMEM; + + rc = pci_host_bridge_priv(bridge); + rc->dev = dev; + + pcie = &rc->pcie; + pcie->is_rc = true; + + rc->max_regions = 32; + of_property_read_u32(np, "cdns,max-outbound-regions", &rc->max_regions); + + rc->no_bar_nbits = 32; + of_property_read_u32(np, "cdns,no-bar-match-nbits", &rc->no_bar_nbits); + + rc->vendor_id = 0xffff; + of_property_read_u16(np, "vendor-id", &rc->vendor_id); + + rc->device_id = 0xffff; + of_property_read_u16(np, "device-id", &rc->device_id); + + type = of_get_property(np, "device_type", NULL); + if (!type || strcmp(type, "pci")) { + dev_err(dev, "invalid \"device_type\" %s\n", type); + return -EINVAL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); + pcie->reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->reg_base)) { + dev_err(dev, "missing \"reg\"\n"); + return PTR_ERR(pcie->reg_base); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + rc->cfg_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(rc->cfg_base)) { + dev_err(dev, "missing \"cfg\"\n"); + return PTR_ERR(rc->cfg_base); + } + rc->cfg_res = res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); + if (!res) { + dev_err(dev, "missing \"mem\"\n"); + return -EINVAL; + } + pcie->mem_res = res; + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync() failed\n"); + goto err_get_sync; + } + + ret = cdns_pcie_host_init(dev, &resources, rc); + if (ret) + goto err_init; + + list_splice_init(&resources, &bridge->windows); + bridge->dev.parent = dev; + bridge->busnr = pcie->bus; + bridge->ops = &cdns_pcie_host_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + ret = pci_host_probe(bridge); + if (ret < 0) + goto err_host_probe; + + return 0; + + err_host_probe: + pci_free_resource_list(&resources); + + err_init: + pm_runtime_put_sync(dev); + + err_get_sync: + pm_runtime_disable(dev); + + return ret; +} + +static struct platform_driver cdns_pcie_host_driver = { + .driver = { + .name = "cdns-pcie-host", + .of_match_table = cdns_pcie_host_of_match, + }, + .probe = cdns_pcie_host_probe, +}; +builtin_platform_driver(cdns_pcie_host_driver); diff --git a/drivers/pci/controller/pcie-cadence.c b/drivers/pci/controller/pcie-cadence.c new file mode 100644 index 000000000000..138d113eb45d --- /dev/null +++ b/drivers/pci/controller/pcie-cadence.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Cadence +// Cadence PCIe controller driver. +// Author: Cyrille Pitchen + +#include + +#include "pcie-cadence.h" + +void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, + u32 r, bool is_io, + u64 cpu_addr, u64 pci_addr, size_t size) +{ + /* + * roundup_pow_of_two() returns an unsigned long, which is not suited + * for 64bit values. + */ + u64 sz = 1ULL << fls64(size - 1); + int nbits = ilog2(sz); + u32 addr0, addr1, desc0, desc1; + + if (nbits < 8) + nbits = 8; + + /* Set the PCI address */ + addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) | + (lower_32_bits(pci_addr) & GENMASK(31, 8)); + addr1 = upper_32_bits(pci_addr); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), addr1); + + /* Set the PCIe header descriptor */ + if (is_io) + desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO; + else + desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM; + desc1 = 0; + + /* + * Whatever Bit [23] is set or not inside DESC0 register of the outbound + * PCIe descriptor, the PCI function number must be set into + * Bits [26:24] of DESC0 anyway. + * + * In Root Complex mode, the function number is always 0 but in Endpoint + * mode, the PCIe controller may support more than one function. This + * function number needs to be set properly into the outbound PCIe + * descriptor. + * + * Besides, setting Bit [23] is mandatory when in Root Complex mode: + * then the driver must provide the bus, resp. device, number in + * Bits [7:0] of DESC1, resp. Bits[31:27] of DESC0. Like the function + * number, the device number is always 0 in Root Complex mode. + * + * However when in Endpoint mode, we can clear Bit [23] of DESC0, hence + * the PCIe controller will use the captured values for the bus and + * device numbers. + */ + if (pcie->is_rc) { + /* The device and function numbers are always 0. */ + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | + CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); + desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); + } else { + /* + * Use captured values for bus and device numbers but still + * need to set the function number. + */ + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(fn); + } + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), desc0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); + + /* Set the CPU address */ + cpu_addr -= pcie->mem_res->start; + addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) | + (lower_32_bits(cpu_addr) & GENMASK(31, 8)); + addr1 = upper_32_bits(cpu_addr); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), addr1); +} + +void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, + u32 r, u64 cpu_addr) +{ + u32 addr0, addr1, desc0, desc1; + + desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG; + desc1 = 0; + + /* See cdns_pcie_set_outbound_region() comments above. */ + if (pcie->is_rc) { + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | + CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); + desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); + } else { + desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(fn); + } + + /* Set the CPU address */ + cpu_addr -= pcie->mem_res->start; + addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(17) | + (lower_32_bits(cpu_addr) & GENMASK(31, 8)); + addr1 = upper_32_bits(cpu_addr); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), 0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), 0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), desc0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), addr1); +} + +void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r) +{ + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), 0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), 0); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), 0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), 0); + + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), 0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), 0); +} diff --git a/drivers/pci/controller/pcie-cadence.h b/drivers/pci/controller/pcie-cadence.h new file mode 100644 index 000000000000..4bb27333b05c --- /dev/null +++ b/drivers/pci/controller/pcie-cadence.h @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Cadence +// Cadence PCIe controller driver. +// Author: Cyrille Pitchen + +#ifndef _PCIE_CADENCE_H +#define _PCIE_CADENCE_H + +#include +#include + +/* + * Local Management Registers + */ +#define CDNS_PCIE_LM_BASE 0x00100000 + +/* Vendor ID Register */ +#define CDNS_PCIE_LM_ID (CDNS_PCIE_LM_BASE + 0x0044) +#define CDNS_PCIE_LM_ID_VENDOR_MASK GENMASK(15, 0) +#define CDNS_PCIE_LM_ID_VENDOR_SHIFT 0 +#define CDNS_PCIE_LM_ID_VENDOR(vid) \ + (((vid) << CDNS_PCIE_LM_ID_VENDOR_SHIFT) & CDNS_PCIE_LM_ID_VENDOR_MASK) +#define CDNS_PCIE_LM_ID_SUBSYS_MASK GENMASK(31, 16) +#define CDNS_PCIE_LM_ID_SUBSYS_SHIFT 16 +#define CDNS_PCIE_LM_ID_SUBSYS(sub) \ + (((sub) << CDNS_PCIE_LM_ID_SUBSYS_SHIFT) & CDNS_PCIE_LM_ID_SUBSYS_MASK) + +/* Root Port Requestor ID Register */ +#define CDNS_PCIE_LM_RP_RID (CDNS_PCIE_LM_BASE + 0x0228) +#define CDNS_PCIE_LM_RP_RID_MASK GENMASK(15, 0) +#define CDNS_PCIE_LM_RP_RID_SHIFT 0 +#define CDNS_PCIE_LM_RP_RID_(rid) \ + (((rid) << CDNS_PCIE_LM_RP_RID_SHIFT) & CDNS_PCIE_LM_RP_RID_MASK) + +/* Endpoint Bus and Device Number Register */ +#define CDNS_PCIE_LM_EP_ID (CDNS_PCIE_LM_BASE + 0x022c) +#define CDNS_PCIE_LM_EP_ID_DEV_MASK GENMASK(4, 0) +#define CDNS_PCIE_LM_EP_ID_DEV_SHIFT 0 +#define CDNS_PCIE_LM_EP_ID_BUS_MASK GENMASK(15, 8) +#define CDNS_PCIE_LM_EP_ID_BUS_SHIFT 8 + +/* Endpoint Function f BAR b Configuration Registers */ +#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn) \ + (CDNS_PCIE_LM_BASE + 0x0240 + (fn) * 0x0008) +#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn) \ + (CDNS_PCIE_LM_BASE + 0x0244 + (fn) * 0x0008) +#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \ + (GENMASK(4, 0) << ((b) * 8)) +#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \ + (((a) << ((b) * 8)) & CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b)) +#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b) \ + (GENMASK(7, 5) << ((b) * 8)) +#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, c) \ + (((c) << ((b) * 8 + 5)) & CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)) + +/* Endpoint Function Configuration Register */ +#define CDNS_PCIE_LM_EP_FUNC_CFG (CDNS_PCIE_LM_BASE + 0x02c0) + +/* Root Complex BAR Configuration Register */ +#define CDNS_PCIE_LM_RC_BAR_CFG (CDNS_PCIE_LM_BASE + 0x0300) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK GENMASK(5, 0) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE(a) \ + (((a) << 0) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK GENMASK(8, 6) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(c) \ + (((c) << 6) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK GENMASK(13, 9) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE(a) \ + (((a) << 9) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK GENMASK(16, 14) +#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(c) \ + (((c) << 14) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK) +#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE BIT(17) +#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_32BITS 0 +#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS BIT(18) +#define CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE BIT(19) +#define CDNS_PCIE_LM_RC_BAR_CFG_IO_16BITS 0 +#define CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS BIT(20) +#define CDNS_PCIE_LM_RC_BAR_CFG_CHECK_ENABLE BIT(31) + +/* BAR control values applicable to both Endpoint Function and Root Complex */ +#define CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED 0x0 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS 0x1 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS 0x4 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS 0x6 +#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7 + + +/* + * Endpoint Function Registers (PCI configuration space for endpoint functions) + */ +#define CDNS_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12)) + +#define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90 + +/* + * Root Port Registers (PCI configuration space for the root port function) + */ +#define CDNS_PCIE_RP_BASE 0x00200000 + + +/* + * Address Translation Registers + */ +#define CDNS_PCIE_AT_BASE 0x00400000 + +/* Region r Outbound AXI to PCIe Address Translation Register 0 */ +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r) \ + (CDNS_PCIE_AT_BASE + 0x0000 + ((r) & 0x1f) * 0x0020) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK GENMASK(5, 0) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) \ + (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(19, 12) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \ + (((devfn) << 12) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(27, 20) +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(bus) \ + (((bus) << 20) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK) + +/* Region r Outbound AXI to PCIe Address Translation Register 1 */ +#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r) \ + (CDNS_PCIE_AT_BASE + 0x0004 + ((r) & 0x1f) * 0x0020) + +/* Region r Outbound PCIe Descriptor Register 0 */ +#define CDNS_PCIE_AT_OB_REGION_DESC0(r) \ + (CDNS_PCIE_AT_BASE + 0x0008 + ((r) & 0x1f) * 0x0020) +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(3, 0) +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM 0x2 +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO 0x6 +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 0xa +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 0xb +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG 0xc +#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_VENDOR_MSG 0xd +/* Bit 23 MUST be set in RC mode. */ +#define CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID BIT(23) +#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK GENMASK(31, 24) +#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(devfn) \ + (((devfn) << 24) & CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK) + +/* Region r Outbound PCIe Descriptor Register 1 */ +#define CDNS_PCIE_AT_OB_REGION_DESC1(r) \ + (CDNS_PCIE_AT_BASE + 0x000c + ((r) & 0x1f) * 0x0020) +#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK GENMASK(7, 0) +#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus) \ + ((bus) & CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK) + +/* Region r AXI Region Base Address Register 0 */ +#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r) \ + (CDNS_PCIE_AT_BASE + 0x0018 + ((r) & 0x1f) * 0x0020) +#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK GENMASK(5, 0) +#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) \ + (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK) + +/* Region r AXI Region Base Address Register 1 */ +#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r) \ + (CDNS_PCIE_AT_BASE + 0x001c + ((r) & 0x1f) * 0x0020) + +/* Root Port BAR Inbound PCIe to AXI Address Translation Register */ +#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0(bar) \ + (CDNS_PCIE_AT_BASE + 0x0800 + (bar) * 0x0008) +#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK GENMASK(5, 0) +#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(nbits) \ + (((nbits) - 1) & CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK) +#define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \ + (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008) + +enum cdns_pcie_rp_bar { + RP_BAR0, + RP_BAR1, + RP_NO_BAR +}; + +/* Endpoint Function BAR Inbound PCIe to AXI Address Translation Register */ +#define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ + (CDNS_PCIE_AT_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008) +#define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \ + (CDNS_PCIE_AT_BASE + 0x0844 + (fn) * 0x0040 + (bar) * 0x0008) + +/* Normal/Vendor specific message access: offset inside some outbound region */ +#define CDNS_PCIE_NORMAL_MSG_ROUTING_MASK GENMASK(7, 5) +#define CDNS_PCIE_NORMAL_MSG_ROUTING(route) \ + (((route) << 5) & CDNS_PCIE_NORMAL_MSG_ROUTING_MASK) +#define CDNS_PCIE_NORMAL_MSG_CODE_MASK GENMASK(15, 8) +#define CDNS_PCIE_NORMAL_MSG_CODE(code) \ + (((code) << 8) & CDNS_PCIE_NORMAL_MSG_CODE_MASK) +#define CDNS_PCIE_MSG_NO_DATA BIT(16) + +enum cdns_pcie_msg_code { + MSG_CODE_ASSERT_INTA = 0x20, + MSG_CODE_ASSERT_INTB = 0x21, + MSG_CODE_ASSERT_INTC = 0x22, + MSG_CODE_ASSERT_INTD = 0x23, + MSG_CODE_DEASSERT_INTA = 0x24, + MSG_CODE_DEASSERT_INTB = 0x25, + MSG_CODE_DEASSERT_INTC = 0x26, + MSG_CODE_DEASSERT_INTD = 0x27, +}; + +enum cdns_pcie_msg_routing { + /* Route to Root Complex */ + MSG_ROUTING_TO_RC, + + /* Use Address Routing */ + MSG_ROUTING_BY_ADDR, + + /* Use ID Routing */ + MSG_ROUTING_BY_ID, + + /* Route as Broadcast Message from Root Complex */ + MSG_ROUTING_BCAST, + + /* Local message; terminate at receiver (INTx messages) */ + MSG_ROUTING_LOCAL, + + /* Gather & route to Root Complex (PME_TO_Ack message) */ + MSG_ROUTING_GATHER, +}; + +/** + * struct cdns_pcie - private data for Cadence PCIe controller drivers + * @reg_base: IO mapped register base + * @mem_res: start/end offsets in the physical system memory to map PCI accesses + * @is_rc: tell whether the PCIe controller mode is Root Complex or Endpoint. + * @bus: In Root Complex mode, the bus number + */ +struct cdns_pcie { + void __iomem *reg_base; + struct resource *mem_res; + bool is_rc; + u8 bus; +}; + +/* Register access */ +static inline void cdns_pcie_writeb(struct cdns_pcie *pcie, u32 reg, u8 value) +{ + writeb(value, pcie->reg_base + reg); +} + +static inline void cdns_pcie_writew(struct cdns_pcie *pcie, u32 reg, u16 value) +{ + writew(value, pcie->reg_base + reg); +} + +static inline void cdns_pcie_writel(struct cdns_pcie *pcie, u32 reg, u32 value) +{ + writel(value, pcie->reg_base + reg); +} + +static inline u32 cdns_pcie_readl(struct cdns_pcie *pcie, u32 reg) +{ + return readl(pcie->reg_base + reg); +} + +/* Root Port register access */ +static inline void cdns_pcie_rp_writeb(struct cdns_pcie *pcie, + u32 reg, u8 value) +{ + writeb(value, pcie->reg_base + CDNS_PCIE_RP_BASE + reg); +} + +static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, + u32 reg, u16 value) +{ + writew(value, pcie->reg_base + CDNS_PCIE_RP_BASE + reg); +} + +/* Endpoint Function register access */ +static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn, + u32 reg, u8 value) +{ + writeb(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); +} + +static inline void cdns_pcie_ep_fn_writew(struct cdns_pcie *pcie, u8 fn, + u32 reg, u16 value) +{ + writew(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); +} + +static inline void cdns_pcie_ep_fn_writel(struct cdns_pcie *pcie, u8 fn, + u32 reg, u16 value) +{ + writel(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); +} + +static inline u8 cdns_pcie_ep_fn_readb(struct cdns_pcie *pcie, u8 fn, u32 reg) +{ + return readb(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); +} + +static inline u16 cdns_pcie_ep_fn_readw(struct cdns_pcie *pcie, u8 fn, u32 reg) +{ + return readw(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); +} + +static inline u32 cdns_pcie_ep_fn_readl(struct cdns_pcie *pcie, u8 fn, u32 reg) +{ + return readl(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); +} + +void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, + u32 r, bool is_io, + u64 cpu_addr, u64 pci_addr, size_t size); + +void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, + u32 r, u64 cpu_addr); + +void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); + +#endif /* _PCIE_CADENCE_H */ diff --git a/drivers/pci/controller/pcie-iproc-bcma.c b/drivers/pci/controller/pcie-iproc-bcma.c new file mode 100644 index 000000000000..aa55b064f64d --- /dev/null +++ b/drivers/pci/controller/pcie-iproc-bcma.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015 Broadcom Corporation + * Copyright (C) 2015 Hauke Mehrtens + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-iproc.h" + + +/* NS: CLASS field is R/O, and set to wrong 0x200 value */ +static void bcma_pcie2_fixup_class(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8011, bcma_pcie2_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class); + +static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct iproc_pcie *pcie = dev->sysdata; + struct bcma_device *bdev = container_of(pcie->dev, struct bcma_device, dev); + + return bcma_core_irq(bdev, 5); +} + +static int iproc_pcie_bcma_probe(struct bcma_device *bdev) +{ + struct device *dev = &bdev->dev; + struct iproc_pcie *pcie; + LIST_HEAD(resources); + struct pci_host_bridge *bridge; + int ret; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!bridge) + return -ENOMEM; + + pcie = pci_host_bridge_priv(bridge); + + pcie->dev = dev; + + pcie->type = IPROC_PCIE_PAXB_BCMA; + pcie->base = bdev->io_addr; + if (!pcie->base) { + dev_err(dev, "no controller registers\n"); + return -ENOMEM; + } + + pcie->base_addr = bdev->addr; + + pcie->mem.start = bdev->addr_s[0]; + pcie->mem.end = bdev->addr_s[0] + SZ_128M - 1; + pcie->mem.name = "PCIe MEM space"; + pcie->mem.flags = IORESOURCE_MEM; + pci_add_resource(&resources, &pcie->mem); + + pcie->map_irq = iproc_pcie_bcma_map_irq; + + ret = iproc_pcie_setup(pcie, &resources); + if (ret) { + dev_err(dev, "PCIe controller setup failed\n"); + pci_free_resource_list(&resources); + return ret; + } + + bcma_set_drvdata(bdev, pcie); + return 0; +} + +static void iproc_pcie_bcma_remove(struct bcma_device *bdev) +{ + struct iproc_pcie *pcie = bcma_get_drvdata(bdev); + + iproc_pcie_remove(pcie); +} + +static const struct bcma_device_id iproc_pcie_bcma_table[] = { + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_PCIEG2, BCMA_ANY_REV, BCMA_ANY_CLASS), + {}, +}; +MODULE_DEVICE_TABLE(bcma, iproc_pcie_bcma_table); + +static struct bcma_driver iproc_pcie_bcma_driver = { + .name = KBUILD_MODNAME, + .id_table = iproc_pcie_bcma_table, + .probe = iproc_pcie_bcma_probe, + .remove = iproc_pcie_bcma_remove, +}; + +static int __init iproc_pcie_bcma_init(void) +{ + return bcma_driver_register(&iproc_pcie_bcma_driver); +} +module_init(iproc_pcie_bcma_init); + +static void __exit iproc_pcie_bcma_exit(void) +{ + bcma_driver_unregister(&iproc_pcie_bcma_driver); +} +module_exit(iproc_pcie_bcma_exit); + +MODULE_AUTHOR("Hauke Mehrtens"); +MODULE_DESCRIPTION("Broadcom iProc PCIe BCMA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pcie-iproc-msi.c b/drivers/pci/controller/pcie-iproc-msi.c new file mode 100644 index 000000000000..9deb56989d72 --- /dev/null +++ b/drivers/pci/controller/pcie-iproc-msi.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015 Broadcom Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-iproc.h" + +#define IPROC_MSI_INTR_EN_SHIFT 11 +#define IPROC_MSI_INTR_EN BIT(IPROC_MSI_INTR_EN_SHIFT) +#define IPROC_MSI_INT_N_EVENT_SHIFT 1 +#define IPROC_MSI_INT_N_EVENT BIT(IPROC_MSI_INT_N_EVENT_SHIFT) +#define IPROC_MSI_EQ_EN_SHIFT 0 +#define IPROC_MSI_EQ_EN BIT(IPROC_MSI_EQ_EN_SHIFT) + +#define IPROC_MSI_EQ_MASK 0x3f + +/* Max number of GIC interrupts */ +#define NR_HW_IRQS 6 + +/* Number of entries in each event queue */ +#define EQ_LEN 64 + +/* Size of each event queue memory region */ +#define EQ_MEM_REGION_SIZE SZ_4K + +/* Size of each MSI address region */ +#define MSI_MEM_REGION_SIZE SZ_4K + +enum iproc_msi_reg { + IPROC_MSI_EQ_PAGE = 0, + IPROC_MSI_EQ_PAGE_UPPER, + IPROC_MSI_PAGE, + IPROC_MSI_PAGE_UPPER, + IPROC_MSI_CTRL, + IPROC_MSI_EQ_HEAD, + IPROC_MSI_EQ_TAIL, + IPROC_MSI_INTS_EN, + IPROC_MSI_REG_SIZE, +}; + +struct iproc_msi; + +/** + * iProc MSI group + * + * One MSI group is allocated per GIC interrupt, serviced by one iProc MSI + * event queue. + * + * @msi: pointer to iProc MSI data + * @gic_irq: GIC interrupt + * @eq: Event queue number + */ +struct iproc_msi_grp { + struct iproc_msi *msi; + int gic_irq; + unsigned int eq; +}; + +/** + * iProc event queue based MSI + * + * Only meant to be used on platforms without MSI support integrated into the + * GIC. + * + * @pcie: pointer to iProc PCIe data + * @reg_offsets: MSI register offsets + * @grps: MSI groups + * @nr_irqs: number of total interrupts connected to GIC + * @nr_cpus: number of toal CPUs + * @has_inten_reg: indicates the MSI interrupt enable register needs to be + * set explicitly (required for some legacy platforms) + * @bitmap: MSI vector bitmap + * @bitmap_lock: lock to protect access to the MSI bitmap + * @nr_msi_vecs: total number of MSI vectors + * @inner_domain: inner IRQ domain + * @msi_domain: MSI IRQ domain + * @nr_eq_region: required number of 4K aligned memory region for MSI event + * queues + * @nr_msi_region: required number of 4K aligned address region for MSI posted + * writes + * @eq_cpu: pointer to allocated memory region for MSI event queues + * @eq_dma: DMA address of MSI event queues + * @msi_addr: MSI address + */ +struct iproc_msi { + struct iproc_pcie *pcie; + const u16 (*reg_offsets)[IPROC_MSI_REG_SIZE]; + struct iproc_msi_grp *grps; + int nr_irqs; + int nr_cpus; + bool has_inten_reg; + unsigned long *bitmap; + struct mutex bitmap_lock; + unsigned int nr_msi_vecs; + struct irq_domain *inner_domain; + struct irq_domain *msi_domain; + unsigned int nr_eq_region; + unsigned int nr_msi_region; + void *eq_cpu; + dma_addr_t eq_dma; + phys_addr_t msi_addr; +}; + +static const u16 iproc_msi_reg_paxb[NR_HW_IRQS][IPROC_MSI_REG_SIZE] = { + { 0x200, 0x2c0, 0x204, 0x2c4, 0x210, 0x250, 0x254, 0x208 }, + { 0x200, 0x2c0, 0x204, 0x2c4, 0x214, 0x258, 0x25c, 0x208 }, + { 0x200, 0x2c0, 0x204, 0x2c4, 0x218, 0x260, 0x264, 0x208 }, + { 0x200, 0x2c0, 0x204, 0x2c4, 0x21c, 0x268, 0x26c, 0x208 }, + { 0x200, 0x2c0, 0x204, 0x2c4, 0x220, 0x270, 0x274, 0x208 }, + { 0x200, 0x2c0, 0x204, 0x2c4, 0x224, 0x278, 0x27c, 0x208 }, +}; + +static const u16 iproc_msi_reg_paxc[NR_HW_IRQS][IPROC_MSI_REG_SIZE] = { + { 0xc00, 0xc04, 0xc08, 0xc0c, 0xc40, 0xc50, 0xc60 }, + { 0xc10, 0xc14, 0xc18, 0xc1c, 0xc44, 0xc54, 0xc64 }, + { 0xc20, 0xc24, 0xc28, 0xc2c, 0xc48, 0xc58, 0xc68 }, + { 0xc30, 0xc34, 0xc38, 0xc3c, 0xc4c, 0xc5c, 0xc6c }, +}; + +static inline u32 iproc_msi_read_reg(struct iproc_msi *msi, + enum iproc_msi_reg reg, + unsigned int eq) +{ + struct iproc_pcie *pcie = msi->pcie; + + return readl_relaxed(pcie->base + msi->reg_offsets[eq][reg]); +} + +static inline void iproc_msi_write_reg(struct iproc_msi *msi, + enum iproc_msi_reg reg, + int eq, u32 val) +{ + struct iproc_pcie *pcie = msi->pcie; + + writel_relaxed(val, pcie->base + msi->reg_offsets[eq][reg]); +} + +static inline u32 hwirq_to_group(struct iproc_msi *msi, unsigned long hwirq) +{ + return (hwirq % msi->nr_irqs); +} + +static inline unsigned int iproc_msi_addr_offset(struct iproc_msi *msi, + unsigned long hwirq) +{ + if (msi->nr_msi_region > 1) + return hwirq_to_group(msi, hwirq) * MSI_MEM_REGION_SIZE; + else + return hwirq_to_group(msi, hwirq) * sizeof(u32); +} + +static inline unsigned int iproc_msi_eq_offset(struct iproc_msi *msi, u32 eq) +{ + if (msi->nr_eq_region > 1) + return eq * EQ_MEM_REGION_SIZE; + else + return eq * EQ_LEN * sizeof(u32); +} + +static struct irq_chip iproc_msi_irq_chip = { + .name = "iProc-MSI", +}; + +static struct msi_domain_info iproc_msi_domain_info = { + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, + .chip = &iproc_msi_irq_chip, +}; + +/* + * In iProc PCIe core, each MSI group is serviced by a GIC interrupt and a + * dedicated event queue. Each MSI group can support up to 64 MSI vectors. + * + * The number of MSI groups varies between different iProc SoCs. The total + * number of CPU cores also varies. To support MSI IRQ affinity, we + * distribute GIC interrupts across all available CPUs. MSI vector is moved + * from one GIC interrupt to another to steer to the target CPU. + * + * Assuming: + * - the number of MSI groups is M + * - the number of CPU cores is N + * - M is always a multiple of N + * + * Total number of raw MSI vectors = M * 64 + * Total number of supported MSI vectors = (M * 64) / N + */ +static inline int hwirq_to_cpu(struct iproc_msi *msi, unsigned long hwirq) +{ + return (hwirq % msi->nr_cpus); +} + +static inline unsigned long hwirq_to_canonical_hwirq(struct iproc_msi *msi, + unsigned long hwirq) +{ + return (hwirq - hwirq_to_cpu(msi, hwirq)); +} + +static int iproc_msi_irq_set_affinity(struct irq_data *data, + const struct cpumask *mask, bool force) +{ + struct iproc_msi *msi = irq_data_get_irq_chip_data(data); + int target_cpu = cpumask_first(mask); + int curr_cpu; + + curr_cpu = hwirq_to_cpu(msi, data->hwirq); + if (curr_cpu == target_cpu) + return IRQ_SET_MASK_OK_DONE; + + /* steer MSI to the target CPU */ + data->hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq) + target_cpu; + + return IRQ_SET_MASK_OK; +} + +static void iproc_msi_irq_compose_msi_msg(struct irq_data *data, + struct msi_msg *msg) +{ + struct iproc_msi *msi = irq_data_get_irq_chip_data(data); + dma_addr_t addr; + + addr = msi->msi_addr + iproc_msi_addr_offset(msi, data->hwirq); + msg->address_lo = lower_32_bits(addr); + msg->address_hi = upper_32_bits(addr); + msg->data = data->hwirq << 5; +} + +static struct irq_chip iproc_msi_bottom_irq_chip = { + .name = "MSI", + .irq_set_affinity = iproc_msi_irq_set_affinity, + .irq_compose_msi_msg = iproc_msi_irq_compose_msi_msg, +}; + +static int iproc_msi_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) +{ + struct iproc_msi *msi = domain->host_data; + int hwirq, i; + + mutex_lock(&msi->bitmap_lock); + + /* Allocate 'nr_cpus' number of MSI vectors each time */ + hwirq = bitmap_find_next_zero_area(msi->bitmap, msi->nr_msi_vecs, 0, + msi->nr_cpus, 0); + if (hwirq < msi->nr_msi_vecs) { + bitmap_set(msi->bitmap, hwirq, msi->nr_cpus); + } else { + mutex_unlock(&msi->bitmap_lock); + return -ENOSPC; + } + + mutex_unlock(&msi->bitmap_lock); + + for (i = 0; i < nr_irqs; i++) { + irq_domain_set_info(domain, virq + i, hwirq + i, + &iproc_msi_bottom_irq_chip, + domain->host_data, handle_simple_irq, + NULL, NULL); + } + + return hwirq; +} + +static void iproc_msi_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct iproc_msi *msi = irq_data_get_irq_chip_data(data); + unsigned int hwirq; + + mutex_lock(&msi->bitmap_lock); + + hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq); + bitmap_clear(msi->bitmap, hwirq, msi->nr_cpus); + + mutex_unlock(&msi->bitmap_lock); + + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +} + +static const struct irq_domain_ops msi_domain_ops = { + .alloc = iproc_msi_irq_domain_alloc, + .free = iproc_msi_irq_domain_free, +}; + +static inline u32 decode_msi_hwirq(struct iproc_msi *msi, u32 eq, u32 head) +{ + u32 *msg, hwirq; + unsigned int offs; + + offs = iproc_msi_eq_offset(msi, eq) + head * sizeof(u32); + msg = (u32 *)(msi->eq_cpu + offs); + hwirq = readl(msg); + hwirq = (hwirq >> 5) + (hwirq & 0x1f); + + /* + * Since we have multiple hwirq mapped to a single MSI vector, + * now we need to derive the hwirq at CPU0. It can then be used to + * mapped back to virq. + */ + return hwirq_to_canonical_hwirq(msi, hwirq); +} + +static void iproc_msi_handler(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct iproc_msi_grp *grp; + struct iproc_msi *msi; + u32 eq, head, tail, nr_events; + unsigned long hwirq; + int virq; + + chained_irq_enter(chip, desc); + + grp = irq_desc_get_handler_data(desc); + msi = grp->msi; + eq = grp->eq; + + /* + * iProc MSI event queue is tracked by head and tail pointers. Head + * pointer indicates the next entry (MSI data) to be consumed by SW in + * the queue and needs to be updated by SW. iProc MSI core uses the + * tail pointer as the next data insertion point. + * + * Entries between head and tail pointers contain valid MSI data. MSI + * data is guaranteed to be in the event queue memory before the tail + * pointer is updated by the iProc MSI core. + */ + head = iproc_msi_read_reg(msi, IPROC_MSI_EQ_HEAD, + eq) & IPROC_MSI_EQ_MASK; + do { + tail = iproc_msi_read_reg(msi, IPROC_MSI_EQ_TAIL, + eq) & IPROC_MSI_EQ_MASK; + + /* + * Figure out total number of events (MSI data) to be + * processed. + */ + nr_events = (tail < head) ? + (EQ_LEN - (head - tail)) : (tail - head); + if (!nr_events) + break; + + /* process all outstanding events */ + while (nr_events--) { + hwirq = decode_msi_hwirq(msi, eq, head); + virq = irq_find_mapping(msi->inner_domain, hwirq); + generic_handle_irq(virq); + + head++; + head %= EQ_LEN; + } + + /* + * Now all outstanding events have been processed. Update the + * head pointer. + */ + iproc_msi_write_reg(msi, IPROC_MSI_EQ_HEAD, eq, head); + + /* + * Now go read the tail pointer again to see if there are new + * oustanding events that came in during the above window. + */ + } while (true); + + chained_irq_exit(chip, desc); +} + +static void iproc_msi_enable(struct iproc_msi *msi) +{ + int i, eq; + u32 val; + + /* Program memory region for each event queue */ + for (i = 0; i < msi->nr_eq_region; i++) { + dma_addr_t addr = msi->eq_dma + (i * EQ_MEM_REGION_SIZE); + + iproc_msi_write_reg(msi, IPROC_MSI_EQ_PAGE, i, + lower_32_bits(addr)); + iproc_msi_write_reg(msi, IPROC_MSI_EQ_PAGE_UPPER, i, + upper_32_bits(addr)); + } + + /* Program address region for MSI posted writes */ + for (i = 0; i < msi->nr_msi_region; i++) { + phys_addr_t addr = msi->msi_addr + (i * MSI_MEM_REGION_SIZE); + + iproc_msi_write_reg(msi, IPROC_MSI_PAGE, i, + lower_32_bits(addr)); + iproc_msi_write_reg(msi, IPROC_MSI_PAGE_UPPER, i, + upper_32_bits(addr)); + } + + for (eq = 0; eq < msi->nr_irqs; eq++) { + /* Enable MSI event queue */ + val = IPROC_MSI_INTR_EN | IPROC_MSI_INT_N_EVENT | + IPROC_MSI_EQ_EN; + iproc_msi_write_reg(msi, IPROC_MSI_CTRL, eq, val); + + /* + * Some legacy platforms require the MSI interrupt enable + * register to be set explicitly. + */ + if (msi->has_inten_reg) { + val = iproc_msi_read_reg(msi, IPROC_MSI_INTS_EN, eq); + val |= BIT(eq); + iproc_msi_write_reg(msi, IPROC_MSI_INTS_EN, eq, val); + } + } +} + +static void iproc_msi_disable(struct iproc_msi *msi) +{ + u32 eq, val; + + for (eq = 0; eq < msi->nr_irqs; eq++) { + if (msi->has_inten_reg) { + val = iproc_msi_read_reg(msi, IPROC_MSI_INTS_EN, eq); + val &= ~BIT(eq); + iproc_msi_write_reg(msi, IPROC_MSI_INTS_EN, eq, val); + } + + val = iproc_msi_read_reg(msi, IPROC_MSI_CTRL, eq); + val &= ~(IPROC_MSI_INTR_EN | IPROC_MSI_INT_N_EVENT | + IPROC_MSI_EQ_EN); + iproc_msi_write_reg(msi, IPROC_MSI_CTRL, eq, val); + } +} + +static int iproc_msi_alloc_domains(struct device_node *node, + struct iproc_msi *msi) +{ + msi->inner_domain = irq_domain_add_linear(NULL, msi->nr_msi_vecs, + &msi_domain_ops, msi); + if (!msi->inner_domain) + return -ENOMEM; + + msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), + &iproc_msi_domain_info, + msi->inner_domain); + if (!msi->msi_domain) { + irq_domain_remove(msi->inner_domain); + return -ENOMEM; + } + + return 0; +} + +static void iproc_msi_free_domains(struct iproc_msi *msi) +{ + if (msi->msi_domain) + irq_domain_remove(msi->msi_domain); + + if (msi->inner_domain) + irq_domain_remove(msi->inner_domain); +} + +static void iproc_msi_irq_free(struct iproc_msi *msi, unsigned int cpu) +{ + int i; + + for (i = cpu; i < msi->nr_irqs; i += msi->nr_cpus) { + irq_set_chained_handler_and_data(msi->grps[i].gic_irq, + NULL, NULL); + } +} + +static int iproc_msi_irq_setup(struct iproc_msi *msi, unsigned int cpu) +{ + int i, ret; + cpumask_var_t mask; + struct iproc_pcie *pcie = msi->pcie; + + for (i = cpu; i < msi->nr_irqs; i += msi->nr_cpus) { + irq_set_chained_handler_and_data(msi->grps[i].gic_irq, + iproc_msi_handler, + &msi->grps[i]); + /* Dedicate GIC interrupt to each CPU core */ + if (alloc_cpumask_var(&mask, GFP_KERNEL)) { + cpumask_clear(mask); + cpumask_set_cpu(cpu, mask); + ret = irq_set_affinity(msi->grps[i].gic_irq, mask); + if (ret) + dev_err(pcie->dev, + "failed to set affinity for IRQ%d\n", + msi->grps[i].gic_irq); + free_cpumask_var(mask); + } else { + dev_err(pcie->dev, "failed to alloc CPU mask\n"); + ret = -EINVAL; + } + + if (ret) { + /* Free all configured/unconfigured IRQs */ + iproc_msi_irq_free(msi, cpu); + return ret; + } + } + + return 0; +} + +int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node) +{ + struct iproc_msi *msi; + int i, ret; + unsigned int cpu; + + if (!of_device_is_compatible(node, "brcm,iproc-msi")) + return -ENODEV; + + if (!of_find_property(node, "msi-controller", NULL)) + return -ENODEV; + + if (pcie->msi) + return -EBUSY; + + msi = devm_kzalloc(pcie->dev, sizeof(*msi), GFP_KERNEL); + if (!msi) + return -ENOMEM; + + msi->pcie = pcie; + pcie->msi = msi; + msi->msi_addr = pcie->base_addr; + mutex_init(&msi->bitmap_lock); + msi->nr_cpus = num_possible_cpus(); + + msi->nr_irqs = of_irq_count(node); + if (!msi->nr_irqs) { + dev_err(pcie->dev, "found no MSI GIC interrupt\n"); + return -ENODEV; + } + + if (msi->nr_irqs > NR_HW_IRQS) { + dev_warn(pcie->dev, "too many MSI GIC interrupts defined %d\n", + msi->nr_irqs); + msi->nr_irqs = NR_HW_IRQS; + } + + if (msi->nr_irqs < msi->nr_cpus) { + dev_err(pcie->dev, + "not enough GIC interrupts for MSI affinity\n"); + return -EINVAL; + } + + if (msi->nr_irqs % msi->nr_cpus != 0) { + msi->nr_irqs -= msi->nr_irqs % msi->nr_cpus; + dev_warn(pcie->dev, "Reducing number of interrupts to %d\n", + msi->nr_irqs); + } + + switch (pcie->type) { + case IPROC_PCIE_PAXB_BCMA: + case IPROC_PCIE_PAXB: + msi->reg_offsets = iproc_msi_reg_paxb; + msi->nr_eq_region = 1; + msi->nr_msi_region = 1; + break; + case IPROC_PCIE_PAXC: + msi->reg_offsets = iproc_msi_reg_paxc; + msi->nr_eq_region = msi->nr_irqs; + msi->nr_msi_region = msi->nr_irqs; + break; + default: + dev_err(pcie->dev, "incompatible iProc PCIe interface\n"); + return -EINVAL; + } + + if (of_find_property(node, "brcm,pcie-msi-inten", NULL)) + msi->has_inten_reg = true; + + msi->nr_msi_vecs = msi->nr_irqs * EQ_LEN; + msi->bitmap = devm_kcalloc(pcie->dev, BITS_TO_LONGS(msi->nr_msi_vecs), + sizeof(*msi->bitmap), GFP_KERNEL); + if (!msi->bitmap) + return -ENOMEM; + + msi->grps = devm_kcalloc(pcie->dev, msi->nr_irqs, sizeof(*msi->grps), + GFP_KERNEL); + if (!msi->grps) + return -ENOMEM; + + for (i = 0; i < msi->nr_irqs; i++) { + unsigned int irq = irq_of_parse_and_map(node, i); + + if (!irq) { + dev_err(pcie->dev, "unable to parse/map interrupt\n"); + ret = -ENODEV; + goto free_irqs; + } + msi->grps[i].gic_irq = irq; + msi->grps[i].msi = msi; + msi->grps[i].eq = i; + } + + /* Reserve memory for event queue and make sure memories are zeroed */ + msi->eq_cpu = dma_zalloc_coherent(pcie->dev, + msi->nr_eq_region * EQ_MEM_REGION_SIZE, + &msi->eq_dma, GFP_KERNEL); + if (!msi->eq_cpu) { + ret = -ENOMEM; + goto free_irqs; + } + + ret = iproc_msi_alloc_domains(node, msi); + if (ret) { + dev_err(pcie->dev, "failed to create MSI domains\n"); + goto free_eq_dma; + } + + for_each_online_cpu(cpu) { + ret = iproc_msi_irq_setup(msi, cpu); + if (ret) + goto free_msi_irq; + } + + iproc_msi_enable(msi); + + return 0; + +free_msi_irq: + for_each_online_cpu(cpu) + iproc_msi_irq_free(msi, cpu); + iproc_msi_free_domains(msi); + +free_eq_dma: + dma_free_coherent(pcie->dev, msi->nr_eq_region * EQ_MEM_REGION_SIZE, + msi->eq_cpu, msi->eq_dma); + +free_irqs: + for (i = 0; i < msi->nr_irqs; i++) { + if (msi->grps[i].gic_irq) + irq_dispose_mapping(msi->grps[i].gic_irq); + } + pcie->msi = NULL; + return ret; +} +EXPORT_SYMBOL(iproc_msi_init); + +void iproc_msi_exit(struct iproc_pcie *pcie) +{ + struct iproc_msi *msi = pcie->msi; + unsigned int i, cpu; + + if (!msi) + return; + + iproc_msi_disable(msi); + + for_each_online_cpu(cpu) + iproc_msi_irq_free(msi, cpu); + + iproc_msi_free_domains(msi); + + dma_free_coherent(pcie->dev, msi->nr_eq_region * EQ_MEM_REGION_SIZE, + msi->eq_cpu, msi->eq_dma); + + for (i = 0; i < msi->nr_irqs; i++) { + if (msi->grps[i].gic_irq) + irq_dispose_mapping(msi->grps[i].gic_irq); + } +} +EXPORT_SYMBOL(iproc_msi_exit); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c new file mode 100644 index 000000000000..f30f5f3fb5c1 --- /dev/null +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015 Broadcom Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" +#include "pcie-iproc.h" + +static const struct of_device_id iproc_pcie_of_match_table[] = { + { + .compatible = "brcm,iproc-pcie", + .data = (int *)IPROC_PCIE_PAXB, + }, { + .compatible = "brcm,iproc-pcie-paxb-v2", + .data = (int *)IPROC_PCIE_PAXB_V2, + }, { + .compatible = "brcm,iproc-pcie-paxc", + .data = (int *)IPROC_PCIE_PAXC, + }, { + .compatible = "brcm,iproc-pcie-paxc-v2", + .data = (int *)IPROC_PCIE_PAXC_V2, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table); + +static int iproc_pcie_pltfm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iproc_pcie *pcie; + struct device_node *np = dev->of_node; + struct resource reg; + resource_size_t iobase = 0; + LIST_HEAD(resources); + struct pci_host_bridge *bridge; + int ret; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!bridge) + return -ENOMEM; + + pcie = pci_host_bridge_priv(bridge); + + pcie->dev = dev; + pcie->type = (enum iproc_pcie_type) of_device_get_match_data(dev); + + ret = of_address_to_resource(np, 0, ®); + if (ret < 0) { + dev_err(dev, "unable to obtain controller resources\n"); + return ret; + } + + pcie->base = devm_pci_remap_cfgspace(dev, reg.start, + resource_size(®)); + if (!pcie->base) { + dev_err(dev, "unable to map controller registers\n"); + return -ENOMEM; + } + pcie->base_addr = reg.start; + + if (of_property_read_bool(np, "brcm,pcie-ob")) { + u32 val; + + ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset", + &val); + if (ret) { + dev_err(dev, + "missing brcm,pcie-ob-axi-offset property\n"); + return ret; + } + pcie->ob.axi_offset = val; + pcie->need_ob_cfg = true; + } + + /* + * DT nodes are not used by all platforms that use the iProc PCIe + * core driver. For platforms that require explict inbound mapping + * configuration, "dma-ranges" would have been present in DT + */ + pcie->need_ib_cfg = of_property_read_bool(np, "dma-ranges"); + + /* PHY use is optional */ + pcie->phy = devm_phy_get(dev, "pcie-phy"); + if (IS_ERR(pcie->phy)) { + if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) + return -EPROBE_DEFER; + pcie->phy = NULL; + } + + ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &resources, + &iobase); + if (ret) { + dev_err(dev, "unable to get PCI host bridge resources\n"); + return ret; + } + + /* PAXC doesn't support legacy IRQs, skip mapping */ + switch (pcie->type) { + case IPROC_PCIE_PAXC: + case IPROC_PCIE_PAXC_V2: + break; + default: + pcie->map_irq = of_irq_parse_and_map_pci; + } + + ret = iproc_pcie_setup(pcie, &resources); + if (ret) { + dev_err(dev, "PCIe controller setup failed\n"); + pci_free_resource_list(&resources); + return ret; + } + + platform_set_drvdata(pdev, pcie); + return 0; +} + +static int iproc_pcie_pltfm_remove(struct platform_device *pdev) +{ + struct iproc_pcie *pcie = platform_get_drvdata(pdev); + + return iproc_pcie_remove(pcie); +} + +static void iproc_pcie_pltfm_shutdown(struct platform_device *pdev) +{ + struct iproc_pcie *pcie = platform_get_drvdata(pdev); + + iproc_pcie_shutdown(pcie); +} + +static struct platform_driver iproc_pcie_pltfm_driver = { + .driver = { + .name = "iproc-pcie", + .of_match_table = of_match_ptr(iproc_pcie_of_match_table), + }, + .probe = iproc_pcie_pltfm_probe, + .remove = iproc_pcie_pltfm_remove, + .shutdown = iproc_pcie_pltfm_shutdown, +}; +module_platform_driver(iproc_pcie_pltfm_driver); + +MODULE_AUTHOR("Ray Jui "); +MODULE_DESCRIPTION("Broadcom iPROC PCIe platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c new file mode 100644 index 000000000000..3c76c5fa4f32 --- /dev/null +++ b/drivers/pci/controller/pcie-iproc.c @@ -0,0 +1,1432 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014 Hauke Mehrtens + * Copyright (C) 2015 Broadcom Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-iproc.h" + +#define EP_PERST_SOURCE_SELECT_SHIFT 2 +#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT) +#define EP_MODE_SURVIVE_PERST_SHIFT 1 +#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT) +#define RC_PCIE_RST_OUTPUT_SHIFT 0 +#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT) +#define PAXC_RESET_MASK 0x7f + +#define GIC_V3_CFG_SHIFT 0 +#define GIC_V3_CFG BIT(GIC_V3_CFG_SHIFT) + +#define MSI_ENABLE_CFG_SHIFT 0 +#define MSI_ENABLE_CFG BIT(MSI_ENABLE_CFG_SHIFT) + +#define CFG_IND_ADDR_MASK 0x00001ffc + +#define CFG_ADDR_BUS_NUM_SHIFT 20 +#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000 +#define CFG_ADDR_DEV_NUM_SHIFT 15 +#define CFG_ADDR_DEV_NUM_MASK 0x000f8000 +#define CFG_ADDR_FUNC_NUM_SHIFT 12 +#define CFG_ADDR_FUNC_NUM_MASK 0x00007000 +#define CFG_ADDR_REG_NUM_SHIFT 2 +#define CFG_ADDR_REG_NUM_MASK 0x00000ffc +#define CFG_ADDR_CFG_TYPE_SHIFT 0 +#define CFG_ADDR_CFG_TYPE_MASK 0x00000003 + +#define SYS_RC_INTX_MASK 0xf + +#define PCIE_PHYLINKUP_SHIFT 3 +#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT) +#define PCIE_DL_ACTIVE_SHIFT 2 +#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT) + +#define APB_ERR_EN_SHIFT 0 +#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT) + +#define CFG_RETRY_STATUS 0xffff0001 +#define CFG_RETRY_STATUS_TIMEOUT_US 500000 /* 500 milliseconds */ + +/* derive the enum index of the outbound/inbound mapping registers */ +#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2) + +/* + * Maximum number of outbound mapping window sizes that can be supported by any + * OARR/OMAP mapping pair + */ +#define MAX_NUM_OB_WINDOW_SIZES 4 + +#define OARR_VALID_SHIFT 0 +#define OARR_VALID BIT(OARR_VALID_SHIFT) +#define OARR_SIZE_CFG_SHIFT 1 + +/* + * Maximum number of inbound mapping region sizes that can be supported by an + * IARR + */ +#define MAX_NUM_IB_REGION_SIZES 9 + +#define IMAP_VALID_SHIFT 0 +#define IMAP_VALID BIT(IMAP_VALID_SHIFT) + +#define IPROC_PCI_EXP_CAP 0xac + +#define IPROC_PCIE_REG_INVALID 0xffff + +/** + * iProc PCIe outbound mapping controller specific parameters + * + * @window_sizes: list of supported outbound mapping window sizes in MB + * @nr_sizes: number of supported outbound mapping window sizes + */ +struct iproc_pcie_ob_map { + resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES]; + unsigned int nr_sizes; +}; + +static const struct iproc_pcie_ob_map paxb_ob_map[] = { + { + /* OARR0/OMAP0 */ + .window_sizes = { 128, 256 }, + .nr_sizes = 2, + }, + { + /* OARR1/OMAP1 */ + .window_sizes = { 128, 256 }, + .nr_sizes = 2, + }, +}; + +static const struct iproc_pcie_ob_map paxb_v2_ob_map[] = { + { + /* OARR0/OMAP0 */ + .window_sizes = { 128, 256 }, + .nr_sizes = 2, + }, + { + /* OARR1/OMAP1 */ + .window_sizes = { 128, 256 }, + .nr_sizes = 2, + }, + { + /* OARR2/OMAP2 */ + .window_sizes = { 128, 256, 512, 1024 }, + .nr_sizes = 4, + }, + { + /* OARR3/OMAP3 */ + .window_sizes = { 128, 256, 512, 1024 }, + .nr_sizes = 4, + }, +}; + +/** + * iProc PCIe inbound mapping type + */ +enum iproc_pcie_ib_map_type { + /* for DDR memory */ + IPROC_PCIE_IB_MAP_MEM = 0, + + /* for device I/O memory */ + IPROC_PCIE_IB_MAP_IO, + + /* invalid or unused */ + IPROC_PCIE_IB_MAP_INVALID +}; + +/** + * iProc PCIe inbound mapping controller specific parameters + * + * @type: inbound mapping region type + * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or + * SZ_1G + * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or + * GB, depedning on the size unit + * @nr_sizes: number of supported inbound mapping region sizes + * @nr_windows: number of supported inbound mapping windows for the region + * @imap_addr_offset: register offset between the upper and lower 32-bit + * IMAP address registers + * @imap_window_offset: register offset between each IMAP window + */ +struct iproc_pcie_ib_map { + enum iproc_pcie_ib_map_type type; + unsigned int size_unit; + resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES]; + unsigned int nr_sizes; + unsigned int nr_windows; + u16 imap_addr_offset; + u16 imap_window_offset; +}; + +static const struct iproc_pcie_ib_map paxb_v2_ib_map[] = { + { + /* IARR0/IMAP0 */ + .type = IPROC_PCIE_IB_MAP_IO, + .size_unit = SZ_1K, + .region_sizes = { 32 }, + .nr_sizes = 1, + .nr_windows = 8, + .imap_addr_offset = 0x40, + .imap_window_offset = 0x4, + }, + { + /* IARR1/IMAP1 (currently unused) */ + .type = IPROC_PCIE_IB_MAP_INVALID, + }, + { + /* IARR2/IMAP2 */ + .type = IPROC_PCIE_IB_MAP_MEM, + .size_unit = SZ_1M, + .region_sizes = { 64, 128, 256, 512, 1024, 2048, 4096, 8192, + 16384 }, + .nr_sizes = 9, + .nr_windows = 1, + .imap_addr_offset = 0x4, + .imap_window_offset = 0x8, + }, + { + /* IARR3/IMAP3 */ + .type = IPROC_PCIE_IB_MAP_MEM, + .size_unit = SZ_1G, + .region_sizes = { 1, 2, 4, 8, 16, 32 }, + .nr_sizes = 6, + .nr_windows = 8, + .imap_addr_offset = 0x4, + .imap_window_offset = 0x8, + }, + { + /* IARR4/IMAP4 */ + .type = IPROC_PCIE_IB_MAP_MEM, + .size_unit = SZ_1G, + .region_sizes = { 32, 64, 128, 256, 512 }, + .nr_sizes = 5, + .nr_windows = 8, + .imap_addr_offset = 0x4, + .imap_window_offset = 0x8, + }, +}; + +/* + * iProc PCIe host registers + */ +enum iproc_pcie_reg { + /* clock/reset signal control */ + IPROC_PCIE_CLK_CTRL = 0, + + /* + * To allow MSI to be steered to an external MSI controller (e.g., ARM + * GICv3 ITS) + */ + IPROC_PCIE_MSI_GIC_MODE, + + /* + * IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the + * window where the MSI posted writes are written, for the writes to be + * interpreted as MSI writes. + */ + IPROC_PCIE_MSI_BASE_ADDR, + IPROC_PCIE_MSI_WINDOW_SIZE, + + /* + * To hold the address of the register where the MSI writes are + * programed. When ARM GICv3 ITS is used, this should be programmed + * with the address of the GITS_TRANSLATER register. + */ + IPROC_PCIE_MSI_ADDR_LO, + IPROC_PCIE_MSI_ADDR_HI, + + /* enable MSI */ + IPROC_PCIE_MSI_EN_CFG, + + /* allow access to root complex configuration space */ + IPROC_PCIE_CFG_IND_ADDR, + IPROC_PCIE_CFG_IND_DATA, + + /* allow access to device configuration space */ + IPROC_PCIE_CFG_ADDR, + IPROC_PCIE_CFG_DATA, + + /* enable INTx */ + IPROC_PCIE_INTX_EN, + + /* outbound address mapping */ + IPROC_PCIE_OARR0, + IPROC_PCIE_OMAP0, + IPROC_PCIE_OARR1, + IPROC_PCIE_OMAP1, + IPROC_PCIE_OARR2, + IPROC_PCIE_OMAP2, + IPROC_PCIE_OARR3, + IPROC_PCIE_OMAP3, + + /* inbound address mapping */ + IPROC_PCIE_IARR0, + IPROC_PCIE_IMAP0, + IPROC_PCIE_IARR1, + IPROC_PCIE_IMAP1, + IPROC_PCIE_IARR2, + IPROC_PCIE_IMAP2, + IPROC_PCIE_IARR3, + IPROC_PCIE_IMAP3, + IPROC_PCIE_IARR4, + IPROC_PCIE_IMAP4, + + /* link status */ + IPROC_PCIE_LINK_STATUS, + + /* enable APB error for unsupported requests */ + IPROC_PCIE_APB_ERR_EN, + + /* total number of core registers */ + IPROC_PCIE_MAX_NUM_REG, +}; + +/* iProc PCIe PAXB BCMA registers */ +static const u16 iproc_pcie_reg_paxb_bcma[] = { + [IPROC_PCIE_CLK_CTRL] = 0x000, + [IPROC_PCIE_CFG_IND_ADDR] = 0x120, + [IPROC_PCIE_CFG_IND_DATA] = 0x124, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, + [IPROC_PCIE_INTX_EN] = 0x330, + [IPROC_PCIE_LINK_STATUS] = 0xf0c, +}; + +/* iProc PCIe PAXB registers */ +static const u16 iproc_pcie_reg_paxb[] = { + [IPROC_PCIE_CLK_CTRL] = 0x000, + [IPROC_PCIE_CFG_IND_ADDR] = 0x120, + [IPROC_PCIE_CFG_IND_DATA] = 0x124, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, + [IPROC_PCIE_INTX_EN] = 0x330, + [IPROC_PCIE_OARR0] = 0xd20, + [IPROC_PCIE_OMAP0] = 0xd40, + [IPROC_PCIE_OARR1] = 0xd28, + [IPROC_PCIE_OMAP1] = 0xd48, + [IPROC_PCIE_LINK_STATUS] = 0xf0c, + [IPROC_PCIE_APB_ERR_EN] = 0xf40, +}; + +/* iProc PCIe PAXB v2 registers */ +static const u16 iproc_pcie_reg_paxb_v2[] = { + [IPROC_PCIE_CLK_CTRL] = 0x000, + [IPROC_PCIE_CFG_IND_ADDR] = 0x120, + [IPROC_PCIE_CFG_IND_DATA] = 0x124, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, + [IPROC_PCIE_INTX_EN] = 0x330, + [IPROC_PCIE_OARR0] = 0xd20, + [IPROC_PCIE_OMAP0] = 0xd40, + [IPROC_PCIE_OARR1] = 0xd28, + [IPROC_PCIE_OMAP1] = 0xd48, + [IPROC_PCIE_OARR2] = 0xd60, + [IPROC_PCIE_OMAP2] = 0xd68, + [IPROC_PCIE_OARR3] = 0xdf0, + [IPROC_PCIE_OMAP3] = 0xdf8, + [IPROC_PCIE_IARR0] = 0xd00, + [IPROC_PCIE_IMAP0] = 0xc00, + [IPROC_PCIE_IARR2] = 0xd10, + [IPROC_PCIE_IMAP2] = 0xcc0, + [IPROC_PCIE_IARR3] = 0xe00, + [IPROC_PCIE_IMAP3] = 0xe08, + [IPROC_PCIE_IARR4] = 0xe68, + [IPROC_PCIE_IMAP4] = 0xe70, + [IPROC_PCIE_LINK_STATUS] = 0xf0c, + [IPROC_PCIE_APB_ERR_EN] = 0xf40, +}; + +/* iProc PCIe PAXC v1 registers */ +static const u16 iproc_pcie_reg_paxc[] = { + [IPROC_PCIE_CLK_CTRL] = 0x000, + [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, + [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, +}; + +/* iProc PCIe PAXC v2 registers */ +static const u16 iproc_pcie_reg_paxc_v2[] = { + [IPROC_PCIE_MSI_GIC_MODE] = 0x050, + [IPROC_PCIE_MSI_BASE_ADDR] = 0x074, + [IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078, + [IPROC_PCIE_MSI_ADDR_LO] = 0x07c, + [IPROC_PCIE_MSI_ADDR_HI] = 0x080, + [IPROC_PCIE_MSI_EN_CFG] = 0x09c, + [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, + [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, +}; + +static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) +{ + struct iproc_pcie *pcie = bus->sysdata; + return pcie; +} + +static inline bool iproc_pcie_reg_is_invalid(u16 reg_offset) +{ + return !!(reg_offset == IPROC_PCIE_REG_INVALID); +} + +static inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie, + enum iproc_pcie_reg reg) +{ + return pcie->reg_offsets[reg]; +} + +static inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie, + enum iproc_pcie_reg reg) +{ + u16 offset = iproc_pcie_reg_offset(pcie, reg); + + if (iproc_pcie_reg_is_invalid(offset)) + return 0; + + return readl(pcie->base + offset); +} + +static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie, + enum iproc_pcie_reg reg, u32 val) +{ + u16 offset = iproc_pcie_reg_offset(pcie, reg); + + if (iproc_pcie_reg_is_invalid(offset)) + return; + + writel(val, pcie->base + offset); +} + +/** + * APB error forwarding can be disabled during access of configuration + * registers of the endpoint device, to prevent unsupported requests + * (typically seen during enumeration with multi-function devices) from + * triggering a system exception. + */ +static inline void iproc_pcie_apb_err_disable(struct pci_bus *bus, + bool disable) +{ + struct iproc_pcie *pcie = iproc_data(bus); + u32 val; + + if (bus->number && pcie->has_apb_err_disable) { + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_APB_ERR_EN); + if (disable) + val &= ~APB_ERR_EN; + else + val |= APB_ERR_EN; + iproc_pcie_write_reg(pcie, IPROC_PCIE_APB_ERR_EN, val); + } +} + +static void __iomem *iproc_pcie_map_ep_cfg_reg(struct iproc_pcie *pcie, + unsigned int busno, + unsigned int slot, + unsigned int fn, + int where) +{ + u16 offset; + u32 val; + + /* EP device access */ + val = (busno << CFG_ADDR_BUS_NUM_SHIFT) | + (slot << CFG_ADDR_DEV_NUM_SHIFT) | + (fn << CFG_ADDR_FUNC_NUM_SHIFT) | + (where & CFG_ADDR_REG_NUM_MASK) | + (1 & CFG_ADDR_CFG_TYPE_MASK); + + iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val); + offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA); + + if (iproc_pcie_reg_is_invalid(offset)) + return NULL; + + return (pcie->base + offset); +} + +static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p) +{ + int timeout = CFG_RETRY_STATUS_TIMEOUT_US; + unsigned int data; + + /* + * As per PCIe spec r3.1, sec 2.3.2, CRS Software Visibility only + * affects config reads of the Vendor ID. For config writes or any + * other config reads, the Root may automatically reissue the + * configuration request again as a new request. + * + * For config reads, this hardware returns CFG_RETRY_STATUS data + * when it receives a CRS completion, regardless of the address of + * the read or the CRS Software Visibility Enable bit. As a + * partial workaround for this, we retry in software any read that + * returns CFG_RETRY_STATUS. + * + * Note that a non-Vendor ID config register may have a value of + * CFG_RETRY_STATUS. If we read that, we can't distinguish it from + * a CRS completion, so we will incorrectly retry the read and + * eventually return the wrong data (0xffffffff). + */ + data = readl(cfg_data_p); + while (data == CFG_RETRY_STATUS && timeout--) { + udelay(1); + data = readl(cfg_data_p); + } + + if (data == CFG_RETRY_STATUS) + data = 0xffffffff; + + return data; +} + +static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct iproc_pcie *pcie = iproc_data(bus); + unsigned int slot = PCI_SLOT(devfn); + unsigned int fn = PCI_FUNC(devfn); + unsigned int busno = bus->number; + void __iomem *cfg_data_p; + unsigned int data; + int ret; + + /* root complex access */ + if (busno == 0) { + ret = pci_generic_config_read32(bus, devfn, where, size, val); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + /* Don't advertise CRS SV support */ + if ((where & ~0x3) == IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL) + *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); + return PCIBIOS_SUCCESSFUL; + } + + cfg_data_p = iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where); + + if (!cfg_data_p) + return PCIBIOS_DEVICE_NOT_FOUND; + + data = iproc_pcie_cfg_retry(cfg_data_p); + + *val = data; + if (size <= 2) + *val = (data >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); + + return PCIBIOS_SUCCESSFUL; +} + +/** + * Note access to the configuration registers are protected at the higher layer + * by 'pci_lock' in drivers/pci/access.c + */ +static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie, + int busno, unsigned int devfn, + int where) +{ + unsigned slot = PCI_SLOT(devfn); + unsigned fn = PCI_FUNC(devfn); + u16 offset; + + /* root complex access */ + if (busno == 0) { + if (slot > 0 || fn > 0) + return NULL; + + iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR, + where & CFG_IND_ADDR_MASK); + offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA); + if (iproc_pcie_reg_is_invalid(offset)) + return NULL; + else + return (pcie->base + offset); + } + + /* + * PAXC is connected to an internally emulated EP within the SoC. It + * allows only one device. + */ + if (pcie->ep_is_internal) + if (slot > 0) + return NULL; + + return iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where); +} + +static void __iomem *iproc_pcie_bus_map_cfg_bus(struct pci_bus *bus, + unsigned int devfn, + int where) +{ + return iproc_pcie_map_cfg_bus(iproc_data(bus), bus->number, devfn, + where); +} + +static int iproc_pci_raw_config_read32(struct iproc_pcie *pcie, + unsigned int devfn, int where, + int size, u32 *val) +{ + void __iomem *addr; + + addr = iproc_pcie_map_cfg_bus(pcie, 0, devfn, where & ~0x3); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + *val = readl(addr); + + if (size <= 2) + *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); + + return PCIBIOS_SUCCESSFUL; +} + +static int iproc_pci_raw_config_write32(struct iproc_pcie *pcie, + unsigned int devfn, int where, + int size, u32 val) +{ + void __iomem *addr; + u32 mask, tmp; + + addr = iproc_pcie_map_cfg_bus(pcie, 0, devfn, where & ~0x3); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 4) { + writel(val, addr); + return PCIBIOS_SUCCESSFUL; + } + + mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); + tmp = readl(addr) & mask; + tmp |= val << ((where & 0x3) * 8); + writel(tmp, addr); + + return PCIBIOS_SUCCESSFUL; +} + +static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + int ret; + struct iproc_pcie *pcie = iproc_data(bus); + + iproc_pcie_apb_err_disable(bus, true); + if (pcie->type == IPROC_PCIE_PAXB_V2) + ret = iproc_pcie_config_read(bus, devfn, where, size, val); + else + ret = pci_generic_config_read32(bus, devfn, where, size, val); + iproc_pcie_apb_err_disable(bus, false); + + return ret; +} + +static int iproc_pcie_config_write32(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + int ret; + + iproc_pcie_apb_err_disable(bus, true); + ret = pci_generic_config_write32(bus, devfn, where, size, val); + iproc_pcie_apb_err_disable(bus, false); + + return ret; +} + +static struct pci_ops iproc_pcie_ops = { + .map_bus = iproc_pcie_bus_map_cfg_bus, + .read = iproc_pcie_config_read32, + .write = iproc_pcie_config_write32, +}; + +static void iproc_pcie_perst_ctrl(struct iproc_pcie *pcie, bool assert) +{ + u32 val; + + /* + * PAXC and the internal emulated endpoint device downstream should not + * be reset. If firmware has been loaded on the endpoint device at an + * earlier boot stage, reset here causes issues. + */ + if (pcie->ep_is_internal) + return; + + if (assert) { + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); + val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST & + ~RC_PCIE_RST_OUTPUT; + iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); + udelay(250); + } else { + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); + val |= RC_PCIE_RST_OUTPUT; + iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); + msleep(100); + } +} + +int iproc_pcie_shutdown(struct iproc_pcie *pcie) +{ + iproc_pcie_perst_ctrl(pcie, true); + msleep(500); + + return 0; +} +EXPORT_SYMBOL_GPL(iproc_pcie_shutdown); + +static int iproc_pcie_check_link(struct iproc_pcie *pcie) +{ + struct device *dev = pcie->dev; + u32 hdr_type, link_ctrl, link_status, class, val; + bool link_is_active = false; + + /* + * PAXC connects to emulated endpoint devices directly and does not + * have a Serdes. Therefore skip the link detection logic here. + */ + if (pcie->ep_is_internal) + return 0; + + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS); + if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) { + dev_err(dev, "PHY or data link is INACTIVE!\n"); + return -ENODEV; + } + + /* make sure we are not in EP mode */ + iproc_pci_raw_config_read32(pcie, 0, PCI_HEADER_TYPE, 1, &hdr_type); + if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) { + dev_err(dev, "in EP mode, hdr=%#02x\n", hdr_type); + return -EFAULT; + } + + /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */ +#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c +#define PCI_CLASS_BRIDGE_MASK 0xffff00 +#define PCI_CLASS_BRIDGE_SHIFT 8 + iproc_pci_raw_config_read32(pcie, 0, PCI_BRIDGE_CTRL_REG_OFFSET, + 4, &class); + class &= ~PCI_CLASS_BRIDGE_MASK; + class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT); + iproc_pci_raw_config_write32(pcie, 0, PCI_BRIDGE_CTRL_REG_OFFSET, + 4, class); + + /* check link status to see if link is active */ + iproc_pci_raw_config_read32(pcie, 0, IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA, + 2, &link_status); + if (link_status & PCI_EXP_LNKSTA_NLW) + link_is_active = true; + + if (!link_is_active) { + /* try GEN 1 link speed */ +#define PCI_TARGET_LINK_SPEED_MASK 0xf +#define PCI_TARGET_LINK_SPEED_GEN2 0x2 +#define PCI_TARGET_LINK_SPEED_GEN1 0x1 + iproc_pci_raw_config_read32(pcie, 0, + IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2, + 4, &link_ctrl); + if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) == + PCI_TARGET_LINK_SPEED_GEN2) { + link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK; + link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1; + iproc_pci_raw_config_write32(pcie, 0, + IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2, + 4, link_ctrl); + msleep(100); + + iproc_pci_raw_config_read32(pcie, 0, + IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA, + 2, &link_status); + if (link_status & PCI_EXP_LNKSTA_NLW) + link_is_active = true; + } + } + + dev_info(dev, "link: %s\n", link_is_active ? "UP" : "DOWN"); + + return link_is_active ? 0 : -ENODEV; +} + +static void iproc_pcie_enable(struct iproc_pcie *pcie) +{ + iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK); +} + +static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie, + int window_idx) +{ + u32 val; + + val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx)); + + return !!(val & OARR_VALID); +} + +static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx, + int size_idx, u64 axi_addr, u64 pci_addr) +{ + struct device *dev = pcie->dev; + u16 oarr_offset, omap_offset; + + /* + * Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based + * on window index. + */ + oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0, + window_idx)); + omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0, + window_idx)); + if (iproc_pcie_reg_is_invalid(oarr_offset) || + iproc_pcie_reg_is_invalid(omap_offset)) + return -EINVAL; + + /* + * Program the OARR registers. The upper 32-bit OARR register is + * always right after the lower 32-bit OARR register. + */ + writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) | + OARR_VALID, pcie->base + oarr_offset); + writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4); + + /* now program the OMAP registers */ + writel(lower_32_bits(pci_addr), pcie->base + omap_offset); + writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4); + + dev_info(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n", + window_idx, oarr_offset, &axi_addr, &pci_addr); + dev_info(dev, "oarr lo 0x%x oarr hi 0x%x\n", + readl(pcie->base + oarr_offset), + readl(pcie->base + oarr_offset + 4)); + dev_info(dev, "omap lo 0x%x omap hi 0x%x\n", + readl(pcie->base + omap_offset), + readl(pcie->base + omap_offset + 4)); + + return 0; +} + +/** + * Some iProc SoCs require the SW to configure the outbound address mapping + * + * Outbound address translation: + * + * iproc_pcie_address = axi_address - axi_offset + * OARR = iproc_pcie_address + * OMAP = pci_addr + * + * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address + */ +static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, + u64 pci_addr, resource_size_t size) +{ + struct iproc_pcie_ob *ob = &pcie->ob; + struct device *dev = pcie->dev; + int ret = -EINVAL, window_idx, size_idx; + + if (axi_addr < ob->axi_offset) { + dev_err(dev, "axi address %pap less than offset %pap\n", + &axi_addr, &ob->axi_offset); + return -EINVAL; + } + + /* + * Translate the AXI address to the internal address used by the iProc + * PCIe core before programming the OARR + */ + axi_addr -= ob->axi_offset; + + /* iterate through all OARR/OMAP mapping windows */ + for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) { + const struct iproc_pcie_ob_map *ob_map = + &pcie->ob_map[window_idx]; + + /* + * If current outbound window is already in use, move on to the + * next one. + */ + if (iproc_pcie_ob_is_valid(pcie, window_idx)) + continue; + + /* + * Iterate through all supported window sizes within the + * OARR/OMAP pair to find a match. Go through the window sizes + * in a descending order. + */ + for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0; + size_idx--) { + resource_size_t window_size = + ob_map->window_sizes[size_idx] * SZ_1M; + + if (size < window_size) + continue; + + if (!IS_ALIGNED(axi_addr, window_size) || + !IS_ALIGNED(pci_addr, window_size)) { + dev_err(dev, + "axi %pap or pci %pap not aligned\n", + &axi_addr, &pci_addr); + return -EINVAL; + } + + /* + * Match found! Program both OARR and OMAP and mark + * them as a valid entry. + */ + ret = iproc_pcie_ob_write(pcie, window_idx, size_idx, + axi_addr, pci_addr); + if (ret) + goto err_ob; + + size -= window_size; + if (size == 0) + return 0; + + /* + * If we are here, we are done with the current window, + * but not yet finished all mappings. Need to move on + * to the next window. + */ + axi_addr += window_size; + pci_addr += window_size; + break; + } + } + +err_ob: + dev_err(dev, "unable to configure outbound mapping\n"); + dev_err(dev, + "axi %pap, axi offset %pap, pci %pap, res size %pap\n", + &axi_addr, &ob->axi_offset, &pci_addr, &size); + + return ret; +} + +static int iproc_pcie_map_ranges(struct iproc_pcie *pcie, + struct list_head *resources) +{ + struct device *dev = pcie->dev; + struct resource_entry *window; + int ret; + + resource_list_for_each_entry(window, resources) { + struct resource *res = window->res; + u64 res_type = resource_type(res); + + switch (res_type) { + case IORESOURCE_IO: + case IORESOURCE_BUS: + break; + case IORESOURCE_MEM: + ret = iproc_pcie_setup_ob(pcie, res->start, + res->start - window->offset, + resource_size(res)); + if (ret) + return ret; + break; + default: + dev_err(dev, "invalid resource %pR\n", res); + return -EINVAL; + } + } + + return 0; +} + +static inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie, + int region_idx) +{ + const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; + u32 val; + + val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx)); + + return !!(val & (BIT(ib_map->nr_sizes) - 1)); +} + +static inline bool iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map, + enum iproc_pcie_ib_map_type type) +{ + return !!(ib_map->type == type); +} + +static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx, + int size_idx, int nr_windows, u64 axi_addr, + u64 pci_addr, resource_size_t size) +{ + struct device *dev = pcie->dev; + const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; + u16 iarr_offset, imap_offset; + u32 val; + int window_idx; + + iarr_offset = iproc_pcie_reg_offset(pcie, + MAP_REG(IPROC_PCIE_IARR0, region_idx)); + imap_offset = iproc_pcie_reg_offset(pcie, + MAP_REG(IPROC_PCIE_IMAP0, region_idx)); + if (iproc_pcie_reg_is_invalid(iarr_offset) || + iproc_pcie_reg_is_invalid(imap_offset)) + return -EINVAL; + + dev_info(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n", + region_idx, iarr_offset, &axi_addr, &pci_addr); + + /* + * Program the IARR registers. The upper 32-bit IARR register is + * always right after the lower 32-bit IARR register. + */ + writel(lower_32_bits(pci_addr) | BIT(size_idx), + pcie->base + iarr_offset); + writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4); + + dev_info(dev, "iarr lo 0x%x iarr hi 0x%x\n", + readl(pcie->base + iarr_offset), + readl(pcie->base + iarr_offset + 4)); + + /* + * Now program the IMAP registers. Each IARR region may have one or + * more IMAP windows. + */ + size >>= ilog2(nr_windows); + for (window_idx = 0; window_idx < nr_windows; window_idx++) { + val = readl(pcie->base + imap_offset); + val |= lower_32_bits(axi_addr) | IMAP_VALID; + writel(val, pcie->base + imap_offset); + writel(upper_32_bits(axi_addr), + pcie->base + imap_offset + ib_map->imap_addr_offset); + + dev_info(dev, "imap window [%d] lo 0x%x hi 0x%x\n", + window_idx, readl(pcie->base + imap_offset), + readl(pcie->base + imap_offset + + ib_map->imap_addr_offset)); + + imap_offset += ib_map->imap_window_offset; + axi_addr += size; + } + + return 0; +} + +static int iproc_pcie_setup_ib(struct iproc_pcie *pcie, + struct of_pci_range *range, + enum iproc_pcie_ib_map_type type) +{ + struct device *dev = pcie->dev; + struct iproc_pcie_ib *ib = &pcie->ib; + int ret; + unsigned int region_idx, size_idx; + u64 axi_addr = range->cpu_addr, pci_addr = range->pci_addr; + resource_size_t size = range->size; + + /* iterate through all IARR mapping regions */ + for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) { + const struct iproc_pcie_ib_map *ib_map = + &pcie->ib_map[region_idx]; + + /* + * If current inbound region is already in use or not a + * compatible type, move on to the next. + */ + if (iproc_pcie_ib_is_in_use(pcie, region_idx) || + !iproc_pcie_ib_check_type(ib_map, type)) + continue; + + /* iterate through all supported region sizes to find a match */ + for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) { + resource_size_t region_size = + ib_map->region_sizes[size_idx] * ib_map->size_unit; + + if (size != region_size) + continue; + + if (!IS_ALIGNED(axi_addr, region_size) || + !IS_ALIGNED(pci_addr, region_size)) { + dev_err(dev, + "axi %pap or pci %pap not aligned\n", + &axi_addr, &pci_addr); + return -EINVAL; + } + + /* Match found! Program IARR and all IMAP windows. */ + ret = iproc_pcie_ib_write(pcie, region_idx, size_idx, + ib_map->nr_windows, axi_addr, + pci_addr, size); + if (ret) + goto err_ib; + else + return 0; + + } + } + ret = -EINVAL; + +err_ib: + dev_err(dev, "unable to configure inbound mapping\n"); + dev_err(dev, "axi %pap, pci %pap, res size %pap\n", + &axi_addr, &pci_addr, &size); + + return ret; +} + +static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) +{ + struct of_pci_range range; + struct of_pci_range_parser parser; + int ret; + + /* Get the dma-ranges from DT */ + ret = of_pci_dma_range_parser_init(&parser, pcie->dev->of_node); + if (ret) + return ret; + + for_each_of_pci_range(&parser, &range) { + /* Each range entry corresponds to an inbound mapping region */ + ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_MEM); + if (ret) + return ret; + } + + return 0; +} + +static int iproce_pcie_get_msi(struct iproc_pcie *pcie, + struct device_node *msi_node, + u64 *msi_addr) +{ + struct device *dev = pcie->dev; + int ret; + struct resource res; + + /* + * Check if 'msi-map' points to ARM GICv3 ITS, which is the only + * supported external MSI controller that requires steering. + */ + if (!of_device_is_compatible(msi_node, "arm,gic-v3-its")) { + dev_err(dev, "unable to find compatible MSI controller\n"); + return -ENODEV; + } + + /* derive GITS_TRANSLATER address from GICv3 */ + ret = of_address_to_resource(msi_node, 0, &res); + if (ret < 0) { + dev_err(dev, "unable to obtain MSI controller resources\n"); + return ret; + } + + *msi_addr = res.start + GITS_TRANSLATER; + return 0; +} + +static int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr) +{ + int ret; + struct of_pci_range range; + + memset(&range, 0, sizeof(range)); + range.size = SZ_32K; + range.pci_addr = range.cpu_addr = msi_addr & ~(range.size - 1); + + ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_IO); + return ret; +} + +static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr) +{ + u32 val; + + /* + * Program bits [43:13] of address of GITS_TRANSLATER register into + * bits [30:0] of the MSI base address register. In fact, in all iProc + * based SoCs, all I/O register bases are well below the 32-bit + * boundary, so we can safely assume bits [43:32] are always zeros. + */ + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_BASE_ADDR, + (u32)(msi_addr >> 13)); + + /* use a default 8K window size */ + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_WINDOW_SIZE, 0); + + /* steering MSI to GICv3 ITS */ + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_GIC_MODE); + val |= GIC_V3_CFG; + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_GIC_MODE, val); + + /* + * Program bits [43:2] of address of GITS_TRANSLATER register into the + * iProc MSI address registers. + */ + msi_addr >>= 2; + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_HI, + upper_32_bits(msi_addr)); + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_LO, + lower_32_bits(msi_addr)); + + /* enable MSI */ + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG); + val |= MSI_ENABLE_CFG; + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val); +} + +static int iproc_pcie_msi_steer(struct iproc_pcie *pcie, + struct device_node *msi_node) +{ + struct device *dev = pcie->dev; + int ret; + u64 msi_addr; + + ret = iproce_pcie_get_msi(pcie, msi_node, &msi_addr); + if (ret < 0) { + dev_err(dev, "msi steering failed\n"); + return ret; + } + + switch (pcie->type) { + case IPROC_PCIE_PAXB_V2: + ret = iproc_pcie_paxb_v2_msi_steer(pcie, msi_addr); + if (ret) + return ret; + break; + case IPROC_PCIE_PAXC_V2: + iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int iproc_pcie_msi_enable(struct iproc_pcie *pcie) +{ + struct device_node *msi_node; + int ret; + + /* + * Either the "msi-parent" or the "msi-map" phandle needs to exist + * for us to obtain the MSI node. + */ + + msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0); + if (!msi_node) { + const __be32 *msi_map = NULL; + int len; + u32 phandle; + + msi_map = of_get_property(pcie->dev->of_node, "msi-map", &len); + if (!msi_map) + return -ENODEV; + + phandle = be32_to_cpup(msi_map + 1); + msi_node = of_find_node_by_phandle(phandle); + if (!msi_node) + return -ENODEV; + } + + /* + * Certain revisions of the iProc PCIe controller require additional + * configurations to steer the MSI writes towards an external MSI + * controller. + */ + if (pcie->need_msi_steer) { + ret = iproc_pcie_msi_steer(pcie, msi_node); + if (ret) + return ret; + } + + /* + * If another MSI controller is being used, the call below should fail + * but that is okay + */ + return iproc_msi_init(pcie, msi_node); +} + +static void iproc_pcie_msi_disable(struct iproc_pcie *pcie) +{ + iproc_msi_exit(pcie); +} + +static int iproc_pcie_rev_init(struct iproc_pcie *pcie) +{ + struct device *dev = pcie->dev; + unsigned int reg_idx; + const u16 *regs; + + switch (pcie->type) { + case IPROC_PCIE_PAXB_BCMA: + regs = iproc_pcie_reg_paxb_bcma; + break; + case IPROC_PCIE_PAXB: + regs = iproc_pcie_reg_paxb; + pcie->has_apb_err_disable = true; + if (pcie->need_ob_cfg) { + pcie->ob_map = paxb_ob_map; + pcie->ob.nr_windows = ARRAY_SIZE(paxb_ob_map); + } + break; + case IPROC_PCIE_PAXB_V2: + regs = iproc_pcie_reg_paxb_v2; + pcie->has_apb_err_disable = true; + if (pcie->need_ob_cfg) { + pcie->ob_map = paxb_v2_ob_map; + pcie->ob.nr_windows = ARRAY_SIZE(paxb_v2_ob_map); + } + pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map); + pcie->ib_map = paxb_v2_ib_map; + pcie->need_msi_steer = true; + dev_warn(dev, "reads of config registers that contain %#x return incorrect data\n", + CFG_RETRY_STATUS); + break; + case IPROC_PCIE_PAXC: + regs = iproc_pcie_reg_paxc; + pcie->ep_is_internal = true; + break; + case IPROC_PCIE_PAXC_V2: + regs = iproc_pcie_reg_paxc_v2; + pcie->ep_is_internal = true; + pcie->need_msi_steer = true; + break; + default: + dev_err(dev, "incompatible iProc PCIe interface\n"); + return -EINVAL; + } + + pcie->reg_offsets = devm_kcalloc(dev, IPROC_PCIE_MAX_NUM_REG, + sizeof(*pcie->reg_offsets), + GFP_KERNEL); + if (!pcie->reg_offsets) + return -ENOMEM; + + /* go through the register table and populate all valid registers */ + pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ? + IPROC_PCIE_REG_INVALID : regs[0]; + for (reg_idx = 1; reg_idx < IPROC_PCIE_MAX_NUM_REG; reg_idx++) + pcie->reg_offsets[reg_idx] = regs[reg_idx] ? + regs[reg_idx] : IPROC_PCIE_REG_INVALID; + + return 0; +} + +int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) +{ + struct device *dev; + int ret; + struct pci_bus *child; + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + + dev = pcie->dev; + + ret = iproc_pcie_rev_init(pcie); + if (ret) { + dev_err(dev, "unable to initialize controller parameters\n"); + return ret; + } + + ret = devm_request_pci_bus_resources(dev, res); + if (ret) + return ret; + + ret = phy_init(pcie->phy); + if (ret) { + dev_err(dev, "unable to initialize PCIe PHY\n"); + return ret; + } + + ret = phy_power_on(pcie->phy); + if (ret) { + dev_err(dev, "unable to power on PCIe PHY\n"); + goto err_exit_phy; + } + + iproc_pcie_perst_ctrl(pcie, true); + iproc_pcie_perst_ctrl(pcie, false); + + if (pcie->need_ob_cfg) { + ret = iproc_pcie_map_ranges(pcie, res); + if (ret) { + dev_err(dev, "map failed\n"); + goto err_power_off_phy; + } + } + + if (pcie->need_ib_cfg) { + ret = iproc_pcie_map_dma_ranges(pcie); + if (ret && ret != -ENOENT) + goto err_power_off_phy; + } + + ret = iproc_pcie_check_link(pcie); + if (ret) { + dev_err(dev, "no PCIe EP device detected\n"); + goto err_power_off_phy; + } + + iproc_pcie_enable(pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + if (iproc_pcie_msi_enable(pcie)) + dev_info(dev, "not using iProc MSI\n"); + + list_splice_init(res, &host->windows); + host->busnr = 0; + host->dev.parent = dev; + host->ops = &iproc_pcie_ops; + host->sysdata = pcie; + host->map_irq = pcie->map_irq; + host->swizzle_irq = pci_common_swizzle; + + ret = pci_scan_root_bus_bridge(host); + if (ret < 0) { + dev_err(dev, "failed to scan host: %d\n", ret); + goto err_power_off_phy; + } + + pci_assign_unassigned_bus_resources(host->bus); + + pcie->root_bus = host->bus; + + list_for_each_entry(child, &host->bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(host->bus); + + return 0; + +err_power_off_phy: + phy_power_off(pcie->phy); +err_exit_phy: + phy_exit(pcie->phy); + return ret; +} +EXPORT_SYMBOL(iproc_pcie_setup); + +int iproc_pcie_remove(struct iproc_pcie *pcie) +{ + pci_stop_root_bus(pcie->root_bus); + pci_remove_root_bus(pcie->root_bus); + + iproc_pcie_msi_disable(pcie); + + phy_power_off(pcie->phy); + phy_exit(pcie->phy); + + return 0; +} +EXPORT_SYMBOL(iproc_pcie_remove); + +MODULE_AUTHOR("Ray Jui "); +MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pcie-iproc.h b/drivers/pci/controller/pcie-iproc.h new file mode 100644 index 000000000000..814b600b383a --- /dev/null +++ b/drivers/pci/controller/pcie-iproc.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2014-2015 Broadcom Corporation + */ + +#ifndef _PCIE_IPROC_H +#define _PCIE_IPROC_H + +/** + * iProc PCIe interface type + * + * PAXB is the wrapper used in root complex that can be connected to an + * external endpoint device. + * + * PAXC is the wrapper used in root complex dedicated for internal emulated + * endpoint devices. + */ +enum iproc_pcie_type { + IPROC_PCIE_PAXB_BCMA = 0, + IPROC_PCIE_PAXB, + IPROC_PCIE_PAXB_V2, + IPROC_PCIE_PAXC, + IPROC_PCIE_PAXC_V2, +}; + +/** + * iProc PCIe outbound mapping + * @axi_offset: offset from the AXI address to the internal address used by + * the iProc PCIe core + * @nr_windows: total number of supported outbound mapping windows + */ +struct iproc_pcie_ob { + resource_size_t axi_offset; + unsigned int nr_windows; +}; + +/** + * iProc PCIe inbound mapping + * @nr_regions: total number of supported inbound mapping regions + */ +struct iproc_pcie_ib { + unsigned int nr_regions; +}; + +struct iproc_pcie_ob_map; +struct iproc_pcie_ib_map; +struct iproc_msi; + +/** + * iProc PCIe device + * + * @dev: pointer to device data structure + * @type: iProc PCIe interface type + * @reg_offsets: register offsets + * @base: PCIe host controller I/O register base + * @base_addr: PCIe host controller register base physical address + * @root_bus: pointer to root bus + * @phy: optional PHY device that controls the Serdes + * @map_irq: function callback to map interrupts + * @ep_is_internal: indicates an internal emulated endpoint device is connected + * @has_apb_err_disable: indicates the controller can be configured to prevent + * unsupported request from being forwarded as an APB bus error + * + * @need_ob_cfg: indicates SW needs to configure the outbound mapping window + * @ob: outbound mapping related parameters + * @ob_map: outbound mapping related parameters specific to the controller + * + * @need_ib_cfg: indicates SW needs to configure the inbound mapping window + * @ib: inbound mapping related parameters + * @ib_map: outbound mapping region related parameters + * + * @need_msi_steer: indicates additional configuration of the iProc PCIe + * controller is required to steer MSI writes to external interrupt controller + * @msi: MSI data + */ +struct iproc_pcie { + struct device *dev; + enum iproc_pcie_type type; + u16 *reg_offsets; + void __iomem *base; + phys_addr_t base_addr; + struct resource mem; + struct pci_bus *root_bus; + struct phy *phy; + int (*map_irq)(const struct pci_dev *, u8, u8); + bool ep_is_internal; + bool has_apb_err_disable; + + bool need_ob_cfg; + struct iproc_pcie_ob ob; + const struct iproc_pcie_ob_map *ob_map; + + bool need_ib_cfg; + struct iproc_pcie_ib ib; + const struct iproc_pcie_ib_map *ib_map; + + bool need_msi_steer; + struct iproc_msi *msi; +}; + +int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res); +int iproc_pcie_remove(struct iproc_pcie *pcie); +int iproc_pcie_shutdown(struct iproc_pcie *pcie); + +#ifdef CONFIG_PCIE_IPROC_MSI +int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node); +void iproc_msi_exit(struct iproc_pcie *pcie); +#else +static inline int iproc_msi_init(struct iproc_pcie *pcie, + struct device_node *node) +{ + return -ENODEV; +} +static inline void iproc_msi_exit(struct iproc_pcie *pcie) +{ +} +#endif + +#endif /* _PCIE_IPROC_H */ diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c new file mode 100644 index 000000000000..0baabe30858f --- /dev/null +++ b/drivers/pci/controller/pcie-mediatek.c @@ -0,0 +1,1218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek PCIe host controller driver. + * + * Copyright (c) 2017 MediaTek Inc. + * Author: Ryder Lee + * Honghui Zhang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* PCIe shared registers */ +#define PCIE_SYS_CFG 0x00 +#define PCIE_INT_ENABLE 0x0c +#define PCIE_CFG_ADDR 0x20 +#define PCIE_CFG_DATA 0x24 + +/* PCIe per port registers */ +#define PCIE_BAR0_SETUP 0x10 +#define PCIE_CLASS 0x34 +#define PCIE_LINK_STATUS 0x50 + +#define PCIE_PORT_INT_EN(x) BIT(20 + (x)) +#define PCIE_PORT_PERST(x) BIT(1 + (x)) +#define PCIE_PORT_LINKUP BIT(0) +#define PCIE_BAR_MAP_MAX GENMASK(31, 16) + +#define PCIE_BAR_ENABLE BIT(0) +#define PCIE_REVISION_ID BIT(0) +#define PCIE_CLASS_CODE (0x60400 << 8) +#define PCIE_CONF_REG(regn) (((regn) & GENMASK(7, 2)) | \ + ((((regn) >> 8) & GENMASK(3, 0)) << 24)) +#define PCIE_CONF_FUN(fun) (((fun) << 8) & GENMASK(10, 8)) +#define PCIE_CONF_DEV(dev) (((dev) << 11) & GENMASK(15, 11)) +#define PCIE_CONF_BUS(bus) (((bus) << 16) & GENMASK(23, 16)) +#define PCIE_CONF_ADDR(regn, fun, dev, bus) \ + (PCIE_CONF_REG(regn) | PCIE_CONF_FUN(fun) | \ + PCIE_CONF_DEV(dev) | PCIE_CONF_BUS(bus)) + +/* MediaTek specific configuration registers */ +#define PCIE_FTS_NUM 0x70c +#define PCIE_FTS_NUM_MASK GENMASK(15, 8) +#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8) + +#define PCIE_FC_CREDIT 0x73c +#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) +#define PCIE_FC_CREDIT_VAL(x) ((x) << 16) + +/* PCIe V2 share registers */ +#define PCIE_SYS_CFG_V2 0x0 +#define PCIE_CSR_LTSSM_EN(x) BIT(0 + (x) * 8) +#define PCIE_CSR_ASPM_L1_EN(x) BIT(1 + (x) * 8) + +/* PCIe V2 per-port registers */ +#define PCIE_MSI_VECTOR 0x0c0 + +#define PCIE_CONF_VEND_ID 0x100 +#define PCIE_CONF_CLASS_ID 0x106 + +#define PCIE_INT_MASK 0x420 +#define INTX_MASK GENMASK(19, 16) +#define INTX_SHIFT 16 +#define PCIE_INT_STATUS 0x424 +#define MSI_STATUS BIT(23) +#define PCIE_IMSI_STATUS 0x42c +#define PCIE_IMSI_ADDR 0x430 +#define MSI_MASK BIT(23) +#define MTK_MSI_IRQS_NUM 32 + +#define PCIE_AHB_TRANS_BASE0_L 0x438 +#define PCIE_AHB_TRANS_BASE0_H 0x43c +#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0)) +#define PCIE_AXI_WINDOW0 0x448 +#define WIN_ENABLE BIT(7) + +/* PCIe V2 configuration transaction header */ +#define PCIE_CFG_HEADER0 0x460 +#define PCIE_CFG_HEADER1 0x464 +#define PCIE_CFG_HEADER2 0x468 +#define PCIE_CFG_WDATA 0x470 +#define PCIE_APP_TLP_REQ 0x488 +#define PCIE_CFG_RDATA 0x48c +#define APP_CFG_REQ BIT(0) +#define APP_CPL_STATUS GENMASK(7, 5) + +#define CFG_WRRD_TYPE_0 4 +#define CFG_WR_FMT 2 +#define CFG_RD_FMT 0 + +#define CFG_DW0_LENGTH(length) ((length) & GENMASK(9, 0)) +#define CFG_DW0_TYPE(type) (((type) << 24) & GENMASK(28, 24)) +#define CFG_DW0_FMT(fmt) (((fmt) << 29) & GENMASK(31, 29)) +#define CFG_DW2_REGN(regn) ((regn) & GENMASK(11, 2)) +#define CFG_DW2_FUN(fun) (((fun) << 16) & GENMASK(18, 16)) +#define CFG_DW2_DEV(dev) (((dev) << 19) & GENMASK(23, 19)) +#define CFG_DW2_BUS(bus) (((bus) << 24) & GENMASK(31, 24)) +#define CFG_HEADER_DW0(type, fmt) \ + (CFG_DW0_LENGTH(1) | CFG_DW0_TYPE(type) | CFG_DW0_FMT(fmt)) +#define CFG_HEADER_DW1(where, size) \ + (GENMASK(((size) - 1), 0) << ((where) & 0x3)) +#define CFG_HEADER_DW2(regn, fun, dev, bus) \ + (CFG_DW2_REGN(regn) | CFG_DW2_FUN(fun) | \ + CFG_DW2_DEV(dev) | CFG_DW2_BUS(bus)) + +#define PCIE_RST_CTRL 0x510 +#define PCIE_PHY_RSTB BIT(0) +#define PCIE_PIPE_SRSTB BIT(1) +#define PCIE_MAC_SRSTB BIT(2) +#define PCIE_CRSTB BIT(3) +#define PCIE_PERSTB BIT(8) +#define PCIE_LINKDOWN_RST_EN GENMASK(15, 13) +#define PCIE_LINK_STATUS_V2 0x804 +#define PCIE_PORT_LINKUP_V2 BIT(10) + +struct mtk_pcie_port; + +/** + * struct mtk_pcie_soc - differentiate between host generations + * @need_fix_class_id: whether this host's class ID needed to be fixed or not + * @ops: pointer to configuration access functions + * @startup: pointer to controller setting functions + * @setup_irq: pointer to initialize IRQ functions + */ +struct mtk_pcie_soc { + bool need_fix_class_id; + struct pci_ops *ops; + int (*startup)(struct mtk_pcie_port *port); + int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node); +}; + +/** + * struct mtk_pcie_port - PCIe port information + * @base: IO mapped register base + * @list: port list + * @pcie: pointer to PCIe host info + * @reset: pointer to port reset control + * @sys_ck: pointer to transaction/data link layer clock + * @ahb_ck: pointer to AHB slave interface operating clock for CSR access + * and RC initiated MMIO access + * @axi_ck: pointer to application layer MMIO channel operating clock + * @aux_ck: pointer to pe2_mac_bridge and pe2_mac_core operating clock + * when pcie_mac_ck/pcie_pipe_ck is turned off + * @obff_ck: pointer to OBFF functional block operating clock + * @pipe_ck: pointer to LTSSM and PHY/MAC layer operating clock + * @phy: pointer to PHY control block + * @lane: lane count + * @slot: port slot + * @irq_domain: legacy INTx IRQ domain + * @inner_domain: inner IRQ domain + * @msi_domain: MSI IRQ domain + * @lock: protect the msi_irq_in_use bitmap + * @msi_irq_in_use: bit map for assigned MSI IRQ + */ +struct mtk_pcie_port { + void __iomem *base; + struct list_head list; + struct mtk_pcie *pcie; + struct reset_control *reset; + struct clk *sys_ck; + struct clk *ahb_ck; + struct clk *axi_ck; + struct clk *aux_ck; + struct clk *obff_ck; + struct clk *pipe_ck; + struct phy *phy; + u32 lane; + u32 slot; + struct irq_domain *irq_domain; + struct irq_domain *inner_domain; + struct irq_domain *msi_domain; + struct mutex lock; + DECLARE_BITMAP(msi_irq_in_use, MTK_MSI_IRQS_NUM); +}; + +/** + * struct mtk_pcie - PCIe host information + * @dev: pointer to PCIe device + * @base: IO mapped register base + * @free_ck: free-run reference clock + * @io: IO resource + * @pio: PIO resource + * @mem: non-prefetchable memory resource + * @busn: bus range + * @offset: IO / Memory offset + * @ports: pointer to PCIe port information + * @soc: pointer to SoC-dependent operations + */ +struct mtk_pcie { + struct device *dev; + void __iomem *base; + struct clk *free_ck; + + struct resource io; + struct resource pio; + struct resource mem; + struct resource busn; + struct { + resource_size_t mem; + resource_size_t io; + } offset; + struct list_head ports; + const struct mtk_pcie_soc *soc; +}; + +static void mtk_pcie_subsys_powerdown(struct mtk_pcie *pcie) +{ + struct device *dev = pcie->dev; + + clk_disable_unprepare(pcie->free_ck); + + if (dev->pm_domain) { + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + } +} + +static void mtk_pcie_port_free(struct mtk_pcie_port *port) +{ + struct mtk_pcie *pcie = port->pcie; + struct device *dev = pcie->dev; + + devm_iounmap(dev, port->base); + list_del(&port->list); + devm_kfree(dev, port); +} + +static void mtk_pcie_put_resources(struct mtk_pcie *pcie) +{ + struct mtk_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + phy_power_off(port->phy); + phy_exit(port->phy); + clk_disable_unprepare(port->pipe_ck); + clk_disable_unprepare(port->obff_ck); + clk_disable_unprepare(port->axi_ck); + clk_disable_unprepare(port->aux_ck); + clk_disable_unprepare(port->ahb_ck); + clk_disable_unprepare(port->sys_ck); + mtk_pcie_port_free(port); + } + + mtk_pcie_subsys_powerdown(pcie); +} + +static int mtk_pcie_check_cfg_cpld(struct mtk_pcie_port *port) +{ + u32 val; + int err; + + err = readl_poll_timeout_atomic(port->base + PCIE_APP_TLP_REQ, val, + !(val & APP_CFG_REQ), 10, + 100 * USEC_PER_MSEC); + if (err) + return PCIBIOS_SET_FAILED; + + if (readl(port->base + PCIE_APP_TLP_REQ) & APP_CPL_STATUS) + return PCIBIOS_SET_FAILED; + + return PCIBIOS_SUCCESSFUL; +} + +static int mtk_pcie_hw_rd_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn, + int where, int size, u32 *val) +{ + u32 tmp; + + /* Write PCIe configuration transaction header for Cfgrd */ + writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_RD_FMT), + port->base + PCIE_CFG_HEADER0); + writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1); + writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus), + port->base + PCIE_CFG_HEADER2); + + /* Trigger h/w to transmit Cfgrd TLP */ + tmp = readl(port->base + PCIE_APP_TLP_REQ); + tmp |= APP_CFG_REQ; + writel(tmp, port->base + PCIE_APP_TLP_REQ); + + /* Check completion status */ + if (mtk_pcie_check_cfg_cpld(port)) + return PCIBIOS_SET_FAILED; + + /* Read cpld payload of Cfgrd */ + *val = readl(port->base + PCIE_CFG_RDATA); + + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 3))) & 0xffff; + + return PCIBIOS_SUCCESSFUL; +} + +static int mtk_pcie_hw_wr_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn, + int where, int size, u32 val) +{ + /* Write PCIe configuration transaction header for Cfgwr */ + writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_WR_FMT), + port->base + PCIE_CFG_HEADER0); + writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1); + writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus), + port->base + PCIE_CFG_HEADER2); + + /* Write Cfgwr data */ + val = val << 8 * (where & 3); + writel(val, port->base + PCIE_CFG_WDATA); + + /* Trigger h/w to transmit Cfgwr TLP */ + val = readl(port->base + PCIE_APP_TLP_REQ); + val |= APP_CFG_REQ; + writel(val, port->base + PCIE_APP_TLP_REQ); + + /* Check completion status */ + return mtk_pcie_check_cfg_cpld(port); +} + +static struct mtk_pcie_port *mtk_pcie_find_port(struct pci_bus *bus, + unsigned int devfn) +{ + struct mtk_pcie *pcie = bus->sysdata; + struct mtk_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) + if (port->slot == PCI_SLOT(devfn)) + return port; + + return NULL; +} + +static int mtk_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct mtk_pcie_port *port; + u32 bn = bus->number; + int ret; + + port = mtk_pcie_find_port(bus, devfn); + if (!port) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + ret = mtk_pcie_hw_rd_cfg(port, bn, devfn, where, size, val); + if (ret) + *val = ~0; + + return ret; +} + +static int mtk_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct mtk_pcie_port *port; + u32 bn = bus->number; + + port = mtk_pcie_find_port(bus, devfn); + if (!port) + return PCIBIOS_DEVICE_NOT_FOUND; + + return mtk_pcie_hw_wr_cfg(port, bn, devfn, where, size, val); +} + +static struct pci_ops mtk_pcie_ops_v2 = { + .read = mtk_pcie_config_read, + .write = mtk_pcie_config_write, +}; + +static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port) +{ + struct mtk_pcie *pcie = port->pcie; + struct resource *mem = &pcie->mem; + const struct mtk_pcie_soc *soc = port->pcie->soc; + u32 val; + size_t size; + int err; + + /* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */ + if (pcie->base) { + val = readl(pcie->base + PCIE_SYS_CFG_V2); + val |= PCIE_CSR_LTSSM_EN(port->slot) | + PCIE_CSR_ASPM_L1_EN(port->slot); + writel(val, pcie->base + PCIE_SYS_CFG_V2); + } + + /* Assert all reset signals */ + writel(0, port->base + PCIE_RST_CTRL); + + /* + * Enable PCIe link down reset, if link status changed from link up to + * link down, this will reset MAC control registers and configuration + * space. + */ + writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL); + + /* De-assert PHY, PE, PIPE, MAC and configuration reset */ + val = readl(port->base + PCIE_RST_CTRL); + val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB | + PCIE_MAC_SRSTB | PCIE_CRSTB; + writel(val, port->base + PCIE_RST_CTRL); + + /* Set up vendor ID and class code */ + if (soc->need_fix_class_id) { + val = PCI_VENDOR_ID_MEDIATEK; + writew(val, port->base + PCIE_CONF_VEND_ID); + + val = PCI_CLASS_BRIDGE_HOST; + writew(val, port->base + PCIE_CONF_CLASS_ID); + } + + /* 100ms timeout value should be enough for Gen1/2 training */ + err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_V2, val, + !!(val & PCIE_PORT_LINKUP_V2), 20, + 100 * USEC_PER_MSEC); + if (err) + return -ETIMEDOUT; + + /* Set INTx mask */ + val = readl(port->base + PCIE_INT_MASK); + val &= ~INTX_MASK; + writel(val, port->base + PCIE_INT_MASK); + + /* Set AHB to PCIe translation windows */ + size = mem->end - mem->start; + val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size)); + writel(val, port->base + PCIE_AHB_TRANS_BASE0_L); + + val = upper_32_bits(mem->start); + writel(val, port->base + PCIE_AHB_TRANS_BASE0_H); + + /* Set PCIe to AXI translation memory space.*/ + val = fls(0xffffffff) | WIN_ENABLE; + writel(val, port->base + PCIE_AXI_WINDOW0); + + return 0; +} + +static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); + phys_addr_t addr; + + /* MT2712/MT7622 only support 32-bit MSI addresses */ + addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + msg->address_hi = 0; + msg->address_lo = lower_32_bits(addr); + + msg->data = data->hwirq; + + dev_dbg(port->pcie->dev, "msi#%d address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); +} + +static int mtk_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static void mtk_msi_ack_irq(struct irq_data *data) +{ + struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); + u32 hwirq = data->hwirq; + + writel(1 << hwirq, port->base + PCIE_IMSI_STATUS); +} + +static struct irq_chip mtk_msi_bottom_irq_chip = { + .name = "MTK MSI", + .irq_compose_msi_msg = mtk_compose_msi_msg, + .irq_set_affinity = mtk_msi_set_affinity, + .irq_ack = mtk_msi_ack_irq, +}; + +static int mtk_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct mtk_pcie_port *port = domain->host_data; + unsigned long bit; + + WARN_ON(nr_irqs != 1); + mutex_lock(&port->lock); + + bit = find_first_zero_bit(port->msi_irq_in_use, MTK_MSI_IRQS_NUM); + if (bit >= MTK_MSI_IRQS_NUM) { + mutex_unlock(&port->lock); + return -ENOSPC; + } + + __set_bit(bit, port->msi_irq_in_use); + + mutex_unlock(&port->lock); + + irq_domain_set_info(domain, virq, bit, &mtk_msi_bottom_irq_chip, + domain->host_data, handle_edge_irq, + NULL, NULL); + + return 0; +} + +static void mtk_pcie_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct mtk_pcie_port *port = irq_data_get_irq_chip_data(d); + + mutex_lock(&port->lock); + + if (!test_bit(d->hwirq, port->msi_irq_in_use)) + dev_err(port->pcie->dev, "trying to free unused MSI#%lu\n", + d->hwirq); + else + __clear_bit(d->hwirq, port->msi_irq_in_use); + + mutex_unlock(&port->lock); + + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +} + +static const struct irq_domain_ops msi_domain_ops = { + .alloc = mtk_pcie_irq_domain_alloc, + .free = mtk_pcie_irq_domain_free, +}; + +static struct irq_chip mtk_msi_irq_chip = { + .name = "MTK PCIe MSI", + .irq_ack = irq_chip_ack_parent, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info mtk_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), + .chip = &mtk_msi_irq_chip, +}; + +static int mtk_pcie_allocate_msi_domains(struct mtk_pcie_port *port) +{ + struct fwnode_handle *fwnode = of_node_to_fwnode(port->pcie->dev->of_node); + + mutex_init(&port->lock); + + port->inner_domain = irq_domain_create_linear(fwnode, MTK_MSI_IRQS_NUM, + &msi_domain_ops, port); + if (!port->inner_domain) { + dev_err(port->pcie->dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + port->msi_domain = pci_msi_create_irq_domain(fwnode, &mtk_msi_domain_info, + port->inner_domain); + if (!port->msi_domain) { + dev_err(port->pcie->dev, "failed to create MSI domain\n"); + irq_domain_remove(port->inner_domain); + return -ENOMEM; + } + + return 0; +} + +static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) +{ + u32 val; + phys_addr_t msg_addr; + + msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + val = lower_32_bits(msg_addr); + writel(val, port->base + PCIE_IMSI_ADDR); + + val = readl(port->base + PCIE_INT_MASK); + val &= ~MSI_MASK; + writel(val, port->base + PCIE_INT_MASK); +} + +static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { + .map = mtk_pcie_intx_map, +}; + +static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port, + struct device_node *node) +{ + struct device *dev = port->pcie->dev; + struct device_node *pcie_intc_node; + int ret; + + /* Setup INTx */ + pcie_intc_node = of_get_next_child(node, NULL); + if (!pcie_intc_node) { + dev_err(dev, "no PCIe Intc node found\n"); + return -ENODEV; + } + + port->irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &intx_domain_ops, port); + if (!port->irq_domain) { + dev_err(dev, "failed to get INTx IRQ domain\n"); + return -ENODEV; + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + ret = mtk_pcie_allocate_msi_domains(port); + if (ret) + return ret; + + mtk_pcie_enable_msi(port); + } + + return 0; +} + +static void mtk_pcie_intr_handler(struct irq_desc *desc) +{ + struct mtk_pcie_port *port = irq_desc_get_handler_data(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + unsigned long status; + u32 virq; + u32 bit = INTX_SHIFT; + + chained_irq_enter(irqchip, desc); + + status = readl(port->base + PCIE_INT_STATUS); + if (status & INTX_MASK) { + for_each_set_bit_from(bit, &status, PCI_NUM_INTX + INTX_SHIFT) { + /* Clear the INTx */ + writel(1 << bit, port->base + PCIE_INT_STATUS); + virq = irq_find_mapping(port->irq_domain, + bit - INTX_SHIFT); + generic_handle_irq(virq); + } + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + if (status & MSI_STATUS){ + unsigned long imsi_status; + + while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) { + for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) { + virq = irq_find_mapping(port->inner_domain, bit); + generic_handle_irq(virq); + } + } + /* Clear MSI interrupt status */ + writel(MSI_STATUS, port->base + PCIE_INT_STATUS); + } + } + + chained_irq_exit(irqchip, desc); + + return; +} + +static int mtk_pcie_setup_irq(struct mtk_pcie_port *port, + struct device_node *node) +{ + struct mtk_pcie *pcie = port->pcie; + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); + int err, irq; + + err = mtk_pcie_init_irq_domain(port, node); + if (err) { + dev_err(dev, "failed to init PCIe IRQ domain\n"); + return err; + } + + irq = platform_get_irq(pdev, port->slot); + irq_set_chained_handler_and_data(irq, mtk_pcie_intr_handler, port); + + return 0; +} + +static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct mtk_pcie *pcie = bus->sysdata; + + writel(PCIE_CONF_ADDR(where, PCI_FUNC(devfn), PCI_SLOT(devfn), + bus->number), pcie->base + PCIE_CFG_ADDR); + + return pcie->base + PCIE_CFG_DATA + (where & 3); +} + +static struct pci_ops mtk_pcie_ops = { + .map_bus = mtk_pcie_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static int mtk_pcie_startup_port(struct mtk_pcie_port *port) +{ + struct mtk_pcie *pcie = port->pcie; + u32 func = PCI_FUNC(port->slot << 3); + u32 slot = PCI_SLOT(port->slot << 3); + u32 val; + int err; + + /* assert port PERST_N */ + val = readl(pcie->base + PCIE_SYS_CFG); + val |= PCIE_PORT_PERST(port->slot); + writel(val, pcie->base + PCIE_SYS_CFG); + + /* de-assert port PERST_N */ + val = readl(pcie->base + PCIE_SYS_CFG); + val &= ~PCIE_PORT_PERST(port->slot); + writel(val, pcie->base + PCIE_SYS_CFG); + + /* 100ms timeout value should be enough for Gen1/2 training */ + err = readl_poll_timeout(port->base + PCIE_LINK_STATUS, val, + !!(val & PCIE_PORT_LINKUP), 20, + 100 * USEC_PER_MSEC); + if (err) + return -ETIMEDOUT; + + /* enable interrupt */ + val = readl(pcie->base + PCIE_INT_ENABLE); + val |= PCIE_PORT_INT_EN(port->slot); + writel(val, pcie->base + PCIE_INT_ENABLE); + + /* map to all DDR region. We need to set it before cfg operation. */ + writel(PCIE_BAR_MAP_MAX | PCIE_BAR_ENABLE, + port->base + PCIE_BAR0_SETUP); + + /* configure class code and revision ID */ + writel(PCIE_CLASS_CODE | PCIE_REVISION_ID, port->base + PCIE_CLASS); + + /* configure FC credit */ + writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, func, slot, 0), + pcie->base + PCIE_CFG_ADDR); + val = readl(pcie->base + PCIE_CFG_DATA); + val &= ~PCIE_FC_CREDIT_MASK; + val |= PCIE_FC_CREDIT_VAL(0x806c); + writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, func, slot, 0), + pcie->base + PCIE_CFG_ADDR); + writel(val, pcie->base + PCIE_CFG_DATA); + + /* configure RC FTS number to 250 when it leaves L0s */ + writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, func, slot, 0), + pcie->base + PCIE_CFG_ADDR); + val = readl(pcie->base + PCIE_CFG_DATA); + val &= ~PCIE_FTS_NUM_MASK; + val |= PCIE_FTS_NUM_L0(0x50); + writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, func, slot, 0), + pcie->base + PCIE_CFG_ADDR); + writel(val, pcie->base + PCIE_CFG_DATA); + + return 0; +} + +static void mtk_pcie_enable_port(struct mtk_pcie_port *port) +{ + struct mtk_pcie *pcie = port->pcie; + struct device *dev = pcie->dev; + int err; + + err = clk_prepare_enable(port->sys_ck); + if (err) { + dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot); + goto err_sys_clk; + } + + err = clk_prepare_enable(port->ahb_ck); + if (err) { + dev_err(dev, "failed to enable ahb_ck%d\n", port->slot); + goto err_ahb_clk; + } + + err = clk_prepare_enable(port->aux_ck); + if (err) { + dev_err(dev, "failed to enable aux_ck%d\n", port->slot); + goto err_aux_clk; + } + + err = clk_prepare_enable(port->axi_ck); + if (err) { + dev_err(dev, "failed to enable axi_ck%d\n", port->slot); + goto err_axi_clk; + } + + err = clk_prepare_enable(port->obff_ck); + if (err) { + dev_err(dev, "failed to enable obff_ck%d\n", port->slot); + goto err_obff_clk; + } + + err = clk_prepare_enable(port->pipe_ck); + if (err) { + dev_err(dev, "failed to enable pipe_ck%d\n", port->slot); + goto err_pipe_clk; + } + + reset_control_assert(port->reset); + reset_control_deassert(port->reset); + + err = phy_init(port->phy); + if (err) { + dev_err(dev, "failed to initialize port%d phy\n", port->slot); + goto err_phy_init; + } + + err = phy_power_on(port->phy); + if (err) { + dev_err(dev, "failed to power on port%d phy\n", port->slot); + goto err_phy_on; + } + + if (!pcie->soc->startup(port)) + return; + + dev_info(dev, "Port%d link down\n", port->slot); + + phy_power_off(port->phy); +err_phy_on: + phy_exit(port->phy); +err_phy_init: + clk_disable_unprepare(port->pipe_ck); +err_pipe_clk: + clk_disable_unprepare(port->obff_ck); +err_obff_clk: + clk_disable_unprepare(port->axi_ck); +err_axi_clk: + clk_disable_unprepare(port->aux_ck); +err_aux_clk: + clk_disable_unprepare(port->ahb_ck); +err_ahb_clk: + clk_disable_unprepare(port->sys_ck); +err_sys_clk: + mtk_pcie_port_free(port); +} + +static int mtk_pcie_parse_port(struct mtk_pcie *pcie, + struct device_node *node, + int slot) +{ + struct mtk_pcie_port *port; + struct resource *regs; + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); + char name[10]; + int err; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + err = of_property_read_u32(node, "num-lanes", &port->lane); + if (err) { + dev_err(dev, "missing num-lanes property\n"); + return err; + } + + snprintf(name, sizeof(name), "port%d", slot); + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + port->base = devm_ioremap_resource(dev, regs); + if (IS_ERR(port->base)) { + dev_err(dev, "failed to map port%d base\n", slot); + return PTR_ERR(port->base); + } + + snprintf(name, sizeof(name), "sys_ck%d", slot); + port->sys_ck = devm_clk_get(dev, name); + if (IS_ERR(port->sys_ck)) { + dev_err(dev, "failed to get sys_ck%d clock\n", slot); + return PTR_ERR(port->sys_ck); + } + + /* sys_ck might be divided into the following parts in some chips */ + snprintf(name, sizeof(name), "ahb_ck%d", slot); + port->ahb_ck = devm_clk_get(dev, name); + if (IS_ERR(port->ahb_ck)) { + if (PTR_ERR(port->ahb_ck) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + port->ahb_ck = NULL; + } + + snprintf(name, sizeof(name), "axi_ck%d", slot); + port->axi_ck = devm_clk_get(dev, name); + if (IS_ERR(port->axi_ck)) { + if (PTR_ERR(port->axi_ck) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + port->axi_ck = NULL; + } + + snprintf(name, sizeof(name), "aux_ck%d", slot); + port->aux_ck = devm_clk_get(dev, name); + if (IS_ERR(port->aux_ck)) { + if (PTR_ERR(port->aux_ck) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + port->aux_ck = NULL; + } + + snprintf(name, sizeof(name), "obff_ck%d", slot); + port->obff_ck = devm_clk_get(dev, name); + if (IS_ERR(port->obff_ck)) { + if (PTR_ERR(port->obff_ck) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + port->obff_ck = NULL; + } + + snprintf(name, sizeof(name), "pipe_ck%d", slot); + port->pipe_ck = devm_clk_get(dev, name); + if (IS_ERR(port->pipe_ck)) { + if (PTR_ERR(port->pipe_ck) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + port->pipe_ck = NULL; + } + + snprintf(name, sizeof(name), "pcie-rst%d", slot); + port->reset = devm_reset_control_get_optional_exclusive(dev, name); + if (PTR_ERR(port->reset) == -EPROBE_DEFER) + return PTR_ERR(port->reset); + + /* some platforms may use default PHY setting */ + snprintf(name, sizeof(name), "pcie-phy%d", slot); + port->phy = devm_phy_optional_get(dev, name); + if (IS_ERR(port->phy)) + return PTR_ERR(port->phy); + + port->slot = slot; + port->pcie = pcie; + + if (pcie->soc->setup_irq) { + err = pcie->soc->setup_irq(port, node); + if (err) + return err; + } + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &pcie->ports); + + return 0; +} + +static int mtk_pcie_subsys_powerup(struct mtk_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *regs; + int err; + + /* get shared registers, which are optional */ + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "subsys"); + if (regs) { + pcie->base = devm_ioremap_resource(dev, regs); + if (IS_ERR(pcie->base)) { + dev_err(dev, "failed to map shared register\n"); + return PTR_ERR(pcie->base); + } + } + + pcie->free_ck = devm_clk_get(dev, "free_ck"); + if (IS_ERR(pcie->free_ck)) { + if (PTR_ERR(pcie->free_ck) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + pcie->free_ck = NULL; + } + + if (dev->pm_domain) { + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + } + + /* enable top level clock */ + err = clk_prepare_enable(pcie->free_ck); + if (err) { + dev_err(dev, "failed to enable free_ck\n"); + goto err_free_ck; + } + + return 0; + +err_free_ck: + if (dev->pm_domain) { + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + } + + return err; +} + +static int mtk_pcie_setup(struct mtk_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node, *child; + struct of_pci_range_parser parser; + struct of_pci_range range; + struct resource res; + struct mtk_pcie_port *port, *tmp; + int err; + + if (of_pci_range_parser_init(&parser, node)) { + dev_err(dev, "missing \"ranges\" property\n"); + return -EINVAL; + } + + for_each_of_pci_range(&parser, &range) { + err = of_pci_range_to_resource(&range, node, &res); + if (err < 0) + return err; + + switch (res.flags & IORESOURCE_TYPE_BITS) { + case IORESOURCE_IO: + pcie->offset.io = res.start - range.pci_addr; + + memcpy(&pcie->pio, &res, sizeof(res)); + pcie->pio.name = node->full_name; + + pcie->io.start = range.cpu_addr; + pcie->io.end = range.cpu_addr + range.size - 1; + pcie->io.flags = IORESOURCE_MEM; + pcie->io.name = "I/O"; + + memcpy(&res, &pcie->io, sizeof(res)); + break; + + case IORESOURCE_MEM: + pcie->offset.mem = res.start - range.pci_addr; + + memcpy(&pcie->mem, &res, sizeof(res)); + pcie->mem.name = "non-prefetchable"; + break; + } + } + + err = of_pci_parse_bus_range(node, &pcie->busn); + if (err < 0) { + dev_err(dev, "failed to parse bus ranges property: %d\n", err); + pcie->busn.name = node->name; + pcie->busn.start = 0; + pcie->busn.end = 0xff; + pcie->busn.flags = IORESOURCE_BUS; + } + + for_each_available_child_of_node(node, child) { + int slot; + + err = of_pci_get_devfn(child); + if (err < 0) { + dev_err(dev, "failed to parse devfn: %d\n", err); + return err; + } + + slot = PCI_SLOT(err); + + err = mtk_pcie_parse_port(pcie, child, slot); + if (err) + return err; + } + + err = mtk_pcie_subsys_powerup(pcie); + if (err) + return err; + + /* enable each port, and then check link status */ + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + mtk_pcie_enable_port(port); + + /* power down PCIe subsys if slots are all empty (link down) */ + if (list_empty(&pcie->ports)) + mtk_pcie_subsys_powerdown(pcie); + + return 0; +} + +static int mtk_pcie_request_resources(struct mtk_pcie *pcie) +{ + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct list_head *windows = &host->windows; + struct device *dev = pcie->dev; + int err; + + pci_add_resource_offset(windows, &pcie->pio, pcie->offset.io); + pci_add_resource_offset(windows, &pcie->mem, pcie->offset.mem); + pci_add_resource(windows, &pcie->busn); + + err = devm_request_pci_bus_resources(dev, windows); + if (err < 0) + return err; + + pci_remap_iospace(&pcie->pio, pcie->io.start); + + return 0; +} + +static int mtk_pcie_register_host(struct pci_host_bridge *host) +{ + struct mtk_pcie *pcie = pci_host_bridge_priv(host); + struct pci_bus *child; + int err; + + host->busnr = pcie->busn.start; + host->dev.parent = pcie->dev; + host->ops = pcie->soc->ops; + host->map_irq = of_irq_parse_and_map_pci; + host->swizzle_irq = pci_common_swizzle; + host->sysdata = pcie; + + err = pci_scan_root_bus_bridge(host); + if (err < 0) + return err; + + pci_bus_size_bridges(host->bus); + pci_bus_assign_resources(host->bus); + + list_for_each_entry(child, &host->bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(host->bus); + + return 0; +} + +static int mtk_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_pcie *pcie; + struct pci_host_bridge *host; + int err; + + host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!host) + return -ENOMEM; + + pcie = pci_host_bridge_priv(host); + + pcie->dev = dev; + pcie->soc = of_device_get_match_data(dev); + platform_set_drvdata(pdev, pcie); + INIT_LIST_HEAD(&pcie->ports); + + err = mtk_pcie_setup(pcie); + if (err) + return err; + + err = mtk_pcie_request_resources(pcie); + if (err) + goto put_resources; + + err = mtk_pcie_register_host(host); + if (err) + goto put_resources; + + return 0; + +put_resources: + if (!list_empty(&pcie->ports)) + mtk_pcie_put_resources(pcie); + + return err; +} + +static const struct mtk_pcie_soc mtk_pcie_soc_v1 = { + .ops = &mtk_pcie_ops, + .startup = mtk_pcie_startup_port, +}; + +static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = { + .ops = &mtk_pcie_ops_v2, + .startup = mtk_pcie_startup_port_v2, + .setup_irq = mtk_pcie_setup_irq, +}; + +static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = { + .need_fix_class_id = true, + .ops = &mtk_pcie_ops_v2, + .startup = mtk_pcie_startup_port_v2, + .setup_irq = mtk_pcie_setup_irq, +}; + +static const struct of_device_id mtk_pcie_ids[] = { + { .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 }, + { .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 }, + { .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_mt2712 }, + { .compatible = "mediatek,mt7622-pcie", .data = &mtk_pcie_soc_mt7622 }, + {}, +}; + +static struct platform_driver mtk_pcie_driver = { + .probe = mtk_pcie_probe, + .driver = { + .name = "mtk-pcie", + .of_match_table = mtk_pcie_ids, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(mtk_pcie_driver); diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c new file mode 100644 index 000000000000..4d6c20e47bed --- /dev/null +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -0,0 +1,866 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Mobiveil PCIe Host controller + * + * Copyright (c) 2018 Mobiveil Inc. + * Author: Subrahmanya Lingappa + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* register offsets and bit positions */ + +/* + * translation tables are grouped into windows, each window registers are + * grouped into blocks of 4 or 16 registers each + */ +#define PAB_REG_BLOCK_SIZE 16 +#define PAB_EXT_REG_BLOCK_SIZE 4 + +#define PAB_REG_ADDR(offset, win) (offset + (win * PAB_REG_BLOCK_SIZE)) +#define PAB_EXT_REG_ADDR(offset, win) (offset + (win * PAB_EXT_REG_BLOCK_SIZE)) + +#define LTSSM_STATUS 0x0404 +#define LTSSM_STATUS_L0_MASK 0x3f +#define LTSSM_STATUS_L0 0x2d + +#define PAB_CTRL 0x0808 +#define AMBA_PIO_ENABLE_SHIFT 0 +#define PEX_PIO_ENABLE_SHIFT 1 +#define PAGE_SEL_SHIFT 13 +#define PAGE_SEL_MASK 0x3f +#define PAGE_LO_MASK 0x3ff +#define PAGE_SEL_EN 0xc00 +#define PAGE_SEL_OFFSET_SHIFT 10 + +#define PAB_AXI_PIO_CTRL 0x0840 +#define APIO_EN_MASK 0xf + +#define PAB_PEX_PIO_CTRL 0x08c0 +#define PIO_ENABLE_SHIFT 0 + +#define PAB_INTP_AMBA_MISC_ENB 0x0b0c +#define PAB_INTP_AMBA_MISC_STAT 0x0b1c +#define PAB_INTP_INTX_MASK 0x01e0 +#define PAB_INTP_MSI_MASK 0x8 + +#define PAB_AXI_AMAP_CTRL(win) PAB_REG_ADDR(0x0ba0, win) +#define WIN_ENABLE_SHIFT 0 +#define WIN_TYPE_SHIFT 1 + +#define PAB_EXT_AXI_AMAP_SIZE(win) PAB_EXT_REG_ADDR(0xbaf0, win) + +#define PAB_AXI_AMAP_AXI_WIN(win) PAB_REG_ADDR(0x0ba4, win) +#define AXI_WINDOW_ALIGN_MASK 3 + +#define PAB_AXI_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x0ba8, win) +#define PAB_BUS_SHIFT 24 +#define PAB_DEVICE_SHIFT 19 +#define PAB_FUNCTION_SHIFT 16 + +#define PAB_AXI_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x0bac, win) +#define PAB_INTP_AXI_PIO_CLASS 0x474 + +#define PAB_PEX_AMAP_CTRL(win) PAB_REG_ADDR(0x4ba0, win) +#define AMAP_CTRL_EN_SHIFT 0 +#define AMAP_CTRL_TYPE_SHIFT 1 + +#define PAB_EXT_PEX_AMAP_SIZEN(win) PAB_EXT_REG_ADDR(0xbef0, win) +#define PAB_PEX_AMAP_AXI_WIN(win) PAB_REG_ADDR(0x4ba4, win) +#define PAB_PEX_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x4ba8, win) +#define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win) + +/* starting offset of INTX bits in status register */ +#define PAB_INTX_START 5 + +/* supported number of MSI interrupts */ +#define PCI_NUM_MSI 16 + +/* MSI registers */ +#define MSI_BASE_LO_OFFSET 0x04 +#define MSI_BASE_HI_OFFSET 0x08 +#define MSI_SIZE_OFFSET 0x0c +#define MSI_ENABLE_OFFSET 0x14 +#define MSI_STATUS_OFFSET 0x18 +#define MSI_DATA_OFFSET 0x20 +#define MSI_ADDR_L_OFFSET 0x24 +#define MSI_ADDR_H_OFFSET 0x28 + +/* outbound and inbound window definitions */ +#define WIN_NUM_0 0 +#define WIN_NUM_1 1 +#define CFG_WINDOW_TYPE 0 +#define IO_WINDOW_TYPE 1 +#define MEM_WINDOW_TYPE 2 +#define IB_WIN_SIZE (256 * 1024 * 1024 * 1024) +#define MAX_PIO_WINDOWS 8 + +/* Parameters for the waiting for link up routine */ +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_MIN 90000 +#define LINK_WAIT_MAX 100000 + +struct mobiveil_msi { /* MSI information */ + struct mutex lock; /* protect bitmap variable */ + struct irq_domain *msi_domain; + struct irq_domain *dev_domain; + phys_addr_t msi_pages_phys; + int num_of_vectors; + DECLARE_BITMAP(msi_irq_in_use, PCI_NUM_MSI); +}; + +struct mobiveil_pcie { + struct platform_device *pdev; + struct list_head resources; + void __iomem *config_axi_slave_base; /* endpoint config base */ + void __iomem *csr_axi_slave_base; /* root port config base */ + void __iomem *apb_csr_base; /* MSI register base */ + void __iomem *pcie_reg_base; /* Physical PCIe Controller Base */ + struct irq_domain *intx_domain; + raw_spinlock_t intx_mask_lock; + int irq; + int apio_wins; + int ppio_wins; + int ob_wins_configured; /* configured outbound windows */ + int ib_wins_configured; /* configured inbound windows */ + struct resource *ob_io_res; + char root_bus_nr; + struct mobiveil_msi msi; +}; + +static inline void csr_writel(struct mobiveil_pcie *pcie, const u32 value, + const u32 reg) +{ + writel_relaxed(value, pcie->csr_axi_slave_base + reg); +} + +static inline u32 csr_readl(struct mobiveil_pcie *pcie, const u32 reg) +{ + return readl_relaxed(pcie->csr_axi_slave_base + reg); +} + +static bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie) +{ + return (csr_readl(pcie, LTSSM_STATUS) & + LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0; +} + +static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) +{ + struct mobiveil_pcie *pcie = bus->sysdata; + + /* Only one device down on each root port */ + if ((bus->number == pcie->root_bus_nr) && (devfn > 0)) + return false; + + /* + * Do not read more than one device on the bus directly + * attached to RC + */ + if ((bus->primary == pcie->root_bus_nr) && (devfn > 0)) + return false; + + return true; +} + +/* + * mobiveil_pcie_map_bus - routine to get the configuration base of either + * root port or endpoint + */ +static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct mobiveil_pcie *pcie = bus->sysdata; + + if (!mobiveil_pcie_valid_device(bus, devfn)) + return NULL; + + if (bus->number == pcie->root_bus_nr) { + /* RC config access */ + return pcie->csr_axi_slave_base + where; + } + + /* + * EP config access (in Config/APIO space) + * Program PEX Address base (31..16 bits) with appropriate value + * (BDF) in PAB_AXI_AMAP_PEX_WIN_L0 Register. + * Relies on pci_lock serialization + */ + csr_writel(pcie, bus->number << PAB_BUS_SHIFT | + PCI_SLOT(devfn) << PAB_DEVICE_SHIFT | + PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT, + PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0)); + return pcie->config_axi_slave_base + where; +} + +static struct pci_ops mobiveil_pcie_ops = { + .map_bus = mobiveil_pcie_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static void mobiveil_pcie_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct mobiveil_pcie *pcie = irq_desc_get_handler_data(desc); + struct device *dev = &pcie->pdev->dev; + struct mobiveil_msi *msi = &pcie->msi; + u32 msi_data, msi_addr_lo, msi_addr_hi; + u32 intr_status, msi_status; + unsigned long shifted_status; + u32 bit, virq, val, mask; + + /* + * The core provides a single interrupt for both INTx/MSI messages. + * So we'll read both INTx and MSI status + */ + + chained_irq_enter(chip, desc); + + /* read INTx status */ + val = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT); + mask = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); + intr_status = val & mask; + + /* Handle INTx */ + if (intr_status & PAB_INTP_INTX_MASK) { + shifted_status = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT) >> + PAB_INTX_START; + do { + for_each_set_bit(bit, &shifted_status, PCI_NUM_INTX) { + virq = irq_find_mapping(pcie->intx_domain, + bit + 1); + if (virq) + generic_handle_irq(virq); + else + dev_err_ratelimited(dev, + "unexpected IRQ, INT%d\n", bit); + + /* clear interrupt */ + csr_writel(pcie, + shifted_status << PAB_INTX_START, + PAB_INTP_AMBA_MISC_STAT); + } + } while ((shifted_status >> PAB_INTX_START) != 0); + } + + /* read extra MSI status register */ + msi_status = readl_relaxed(pcie->apb_csr_base + MSI_STATUS_OFFSET); + + /* handle MSI interrupts */ + while (msi_status & 1) { + msi_data = readl_relaxed(pcie->apb_csr_base + + MSI_DATA_OFFSET); + + /* + * MSI_STATUS_OFFSET register gets updated to zero + * once we pop not only the MSI data but also address + * from MSI hardware FIFO. So keeping these following + * two dummy reads. + */ + msi_addr_lo = readl_relaxed(pcie->apb_csr_base + + MSI_ADDR_L_OFFSET); + msi_addr_hi = readl_relaxed(pcie->apb_csr_base + + MSI_ADDR_H_OFFSET); + dev_dbg(dev, "MSI registers, data: %08x, addr: %08x:%08x\n", + msi_data, msi_addr_hi, msi_addr_lo); + + virq = irq_find_mapping(msi->dev_domain, msi_data); + if (virq) + generic_handle_irq(virq); + + msi_status = readl_relaxed(pcie->apb_csr_base + + MSI_STATUS_OFFSET); + } + + /* Clear the interrupt status */ + csr_writel(pcie, intr_status, PAB_INTP_AMBA_MISC_STAT); + chained_irq_exit(chip, desc); +} + +static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct platform_device *pdev = pcie->pdev; + struct device_node *node = dev->of_node; + struct resource *res; + const char *type; + + type = of_get_property(node, "device_type", NULL); + if (!type || strcmp(type, "pci")) { + dev_err(dev, "invalid \"device_type\" %s\n", type); + return -EINVAL; + } + + /* map config resource */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "config_axi_slave"); + pcie->config_axi_slave_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(pcie->config_axi_slave_base)) + return PTR_ERR(pcie->config_axi_slave_base); + pcie->ob_io_res = res; + + /* map csr resource */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_axi_slave"); + pcie->csr_axi_slave_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(pcie->csr_axi_slave_base)) + return PTR_ERR(pcie->csr_axi_slave_base); + pcie->pcie_reg_base = res->start; + + /* map MSI config resource */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb_csr"); + pcie->apb_csr_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(pcie->apb_csr_base)) + return PTR_ERR(pcie->apb_csr_base); + + /* read the number of windows requested */ + if (of_property_read_u32(node, "apio-wins", &pcie->apio_wins)) + pcie->apio_wins = MAX_PIO_WINDOWS; + + if (of_property_read_u32(node, "ppio-wins", &pcie->ppio_wins)) + pcie->ppio_wins = MAX_PIO_WINDOWS; + + pcie->irq = platform_get_irq(pdev, 0); + if (pcie->irq <= 0) { + dev_err(dev, "failed to map IRQ: %d\n", pcie->irq); + return -ENODEV; + } + + irq_set_chained_handler_and_data(pcie->irq, mobiveil_pcie_isr, pcie); + + return 0; +} + +/* + * select_paged_register - routine to access paged register of root complex + * + * registers of RC are paged, for this scheme to work + * extracted higher 6 bits of the offset will be written to pg_sel + * field of PAB_CTRL register and rest of the lower 10 bits enabled with + * PAGE_SEL_EN are used as offset of the register. + */ +static void select_paged_register(struct mobiveil_pcie *pcie, u32 offset) +{ + int pab_ctrl_dw, pg_sel; + + /* clear pg_sel field */ + pab_ctrl_dw = csr_readl(pcie, PAB_CTRL); + pab_ctrl_dw = (pab_ctrl_dw & ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT)); + + /* set pg_sel field */ + pg_sel = (offset >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK; + pab_ctrl_dw |= ((pg_sel << PAGE_SEL_SHIFT)); + csr_writel(pcie, pab_ctrl_dw, PAB_CTRL); +} + +static void write_paged_register(struct mobiveil_pcie *pcie, + u32 val, u32 offset) +{ + u32 off = (offset & PAGE_LO_MASK) | PAGE_SEL_EN; + + select_paged_register(pcie, offset); + csr_writel(pcie, val, off); +} + +static u32 read_paged_register(struct mobiveil_pcie *pcie, u32 offset) +{ + u32 off = (offset & PAGE_LO_MASK) | PAGE_SEL_EN; + + select_paged_register(pcie, offset); + return csr_readl(pcie, off); +} + +static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, + int pci_addr, u32 type, u64 size) +{ + int pio_ctrl_val; + int amap_ctrl_dw; + u64 size64 = ~(size - 1); + + if ((pcie->ib_wins_configured + 1) > pcie->ppio_wins) { + dev_err(&pcie->pdev->dev, + "ERROR: max inbound windows reached !\n"); + return; + } + + pio_ctrl_val = csr_readl(pcie, PAB_PEX_PIO_CTRL); + csr_writel(pcie, + pio_ctrl_val | (1 << PIO_ENABLE_SHIFT), PAB_PEX_PIO_CTRL); + amap_ctrl_dw = read_paged_register(pcie, PAB_PEX_AMAP_CTRL(win_num)); + amap_ctrl_dw = (amap_ctrl_dw | (type << AMAP_CTRL_TYPE_SHIFT)); + amap_ctrl_dw = (amap_ctrl_dw | (1 << AMAP_CTRL_EN_SHIFT)); + + write_paged_register(pcie, amap_ctrl_dw | lower_32_bits(size64), + PAB_PEX_AMAP_CTRL(win_num)); + + write_paged_register(pcie, upper_32_bits(size64), + PAB_EXT_PEX_AMAP_SIZEN(win_num)); + + write_paged_register(pcie, pci_addr, PAB_PEX_AMAP_AXI_WIN(win_num)); + write_paged_register(pcie, pci_addr, PAB_PEX_AMAP_PEX_WIN_L(win_num)); + write_paged_register(pcie, 0, PAB_PEX_AMAP_PEX_WIN_H(win_num)); +} + +/* + * routine to program the outbound windows + */ +static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, + u64 cpu_addr, u64 pci_addr, u32 config_io_bit, u64 size) +{ + + u32 value, type; + u64 size64 = ~(size - 1); + + if ((pcie->ob_wins_configured + 1) > pcie->apio_wins) { + dev_err(&pcie->pdev->dev, + "ERROR: max outbound windows reached !\n"); + return; + } + + /* + * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit + * to 4 KB in PAB_AXI_AMAP_CTRL register + */ + type = config_io_bit; + value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); + csr_writel(pcie, 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | + lower_32_bits(size64), PAB_AXI_AMAP_CTRL(win_num)); + + write_paged_register(pcie, upper_32_bits(size64), + PAB_EXT_AXI_AMAP_SIZE(win_num)); + + /* + * program AXI window base with appropriate value in + * PAB_AXI_AMAP_AXI_WIN0 register + */ + value = csr_readl(pcie, PAB_AXI_AMAP_AXI_WIN(win_num)); + csr_writel(pcie, cpu_addr & (~AXI_WINDOW_ALIGN_MASK), + PAB_AXI_AMAP_AXI_WIN(win_num)); + + value = csr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_H(win_num)); + + csr_writel(pcie, lower_32_bits(pci_addr), + PAB_AXI_AMAP_PEX_WIN_L(win_num)); + csr_writel(pcie, upper_32_bits(pci_addr), + PAB_AXI_AMAP_PEX_WIN_H(win_num)); + + pcie->ob_wins_configured++; +} + +static int mobiveil_bringup_link(struct mobiveil_pcie *pcie) +{ + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (mobiveil_pcie_link_up(pcie)) + return 0; + + usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); + } + dev_err(&pcie->pdev->dev, "link never came up\n"); + return -ETIMEDOUT; +} + +static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie) +{ + phys_addr_t msg_addr = pcie->pcie_reg_base; + struct mobiveil_msi *msi = &pcie->msi; + + pcie->msi.num_of_vectors = PCI_NUM_MSI; + msi->msi_pages_phys = (phys_addr_t)msg_addr; + + writel_relaxed(lower_32_bits(msg_addr), + pcie->apb_csr_base + MSI_BASE_LO_OFFSET); + writel_relaxed(upper_32_bits(msg_addr), + pcie->apb_csr_base + MSI_BASE_HI_OFFSET); + writel_relaxed(4096, pcie->apb_csr_base + MSI_SIZE_OFFSET); + writel_relaxed(1, pcie->apb_csr_base + MSI_ENABLE_OFFSET); +} + +static int mobiveil_host_init(struct mobiveil_pcie *pcie) +{ + u32 value, pab_ctrl, type = 0; + int err; + struct resource_entry *win, *tmp; + + err = mobiveil_bringup_link(pcie); + if (err) { + dev_info(&pcie->pdev->dev, "link bring-up failed\n"); + return err; + } + + /* + * program Bus Master Enable Bit in Command Register in PAB Config + * Space + */ + value = csr_readl(pcie, PCI_COMMAND); + csr_writel(pcie, value | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER, PCI_COMMAND); + + /* + * program PIO Enable Bit to 1 (and PEX PIO Enable to 1) in PAB_CTRL + * register + */ + pab_ctrl = csr_readl(pcie, PAB_CTRL); + csr_writel(pcie, pab_ctrl | (1 << AMBA_PIO_ENABLE_SHIFT) | + (1 << PEX_PIO_ENABLE_SHIFT), PAB_CTRL); + + csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK), + PAB_INTP_AMBA_MISC_ENB); + + /* + * program PIO Enable Bit to 1 and Config Window Enable Bit to 1 in + * PAB_AXI_PIO_CTRL Register + */ + value = csr_readl(pcie, PAB_AXI_PIO_CTRL); + csr_writel(pcie, value | APIO_EN_MASK, PAB_AXI_PIO_CTRL); + + /* + * we'll program one outbound window for config reads and + * another default inbound window for all the upstream traffic + * rest of the outbound windows will be configured according to + * the "ranges" field defined in device tree + */ + + /* config outbound translation window */ + program_ob_windows(pcie, pcie->ob_wins_configured, + pcie->ob_io_res->start, 0, CFG_WINDOW_TYPE, + resource_size(pcie->ob_io_res)); + + /* memory inbound translation window */ + program_ib_windows(pcie, WIN_NUM_1, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE); + + /* Get the I/O and memory ranges from DT */ + resource_list_for_each_entry_safe(win, tmp, &pcie->resources) { + type = 0; + if (resource_type(win->res) == IORESOURCE_MEM) + type = MEM_WINDOW_TYPE; + if (resource_type(win->res) == IORESOURCE_IO) + type = IO_WINDOW_TYPE; + if (type) { + /* configure outbound translation window */ + program_ob_windows(pcie, pcie->ob_wins_configured, + win->res->start, 0, type, + resource_size(win->res)); + } + } + + /* setup MSI hardware registers */ + mobiveil_pcie_enable_msi(pcie); + + return err; +} + +static void mobiveil_mask_intx_irq(struct irq_data *data) +{ + struct irq_desc *desc = irq_to_desc(data->irq); + struct mobiveil_pcie *pcie; + unsigned long flags; + u32 mask, shifted_val; + + pcie = irq_desc_get_chip_data(desc); + mask = 1 << ((data->hwirq + PAB_INTX_START) - 1); + raw_spin_lock_irqsave(&pcie->intx_mask_lock, flags); + shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); + csr_writel(pcie, (shifted_val & (~mask)), PAB_INTP_AMBA_MISC_ENB); + raw_spin_unlock_irqrestore(&pcie->intx_mask_lock, flags); +} + +static void mobiveil_unmask_intx_irq(struct irq_data *data) +{ + struct irq_desc *desc = irq_to_desc(data->irq); + struct mobiveil_pcie *pcie; + unsigned long flags; + u32 shifted_val, mask; + + pcie = irq_desc_get_chip_data(desc); + mask = 1 << ((data->hwirq + PAB_INTX_START) - 1); + raw_spin_lock_irqsave(&pcie->intx_mask_lock, flags); + shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); + csr_writel(pcie, (shifted_val | mask), PAB_INTP_AMBA_MISC_ENB); + raw_spin_unlock_irqrestore(&pcie->intx_mask_lock, flags); +} + +static struct irq_chip intx_irq_chip = { + .name = "mobiveil_pcie:intx", + .irq_enable = mobiveil_unmask_intx_irq, + .irq_disable = mobiveil_mask_intx_irq, + .irq_mask = mobiveil_mask_intx_irq, + .irq_unmask = mobiveil_unmask_intx_irq, +}; + +/* routine to setup the INTx related data */ +static int mobiveil_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &intx_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + return 0; +} + +/* INTx domain operations structure */ +static const struct irq_domain_ops intx_domain_ops = { + .map = mobiveil_pcie_intx_map, +}; + +static struct irq_chip mobiveil_msi_irq_chip = { + .name = "Mobiveil PCIe MSI", + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info mobiveil_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), + .chip = &mobiveil_msi_irq_chip, +}; + +static void mobiveil_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(data); + phys_addr_t addr = pcie->pcie_reg_base + (data->hwirq * sizeof(int)); + + msg->address_lo = lower_32_bits(addr); + msg->address_hi = upper_32_bits(addr); + msg->data = data->hwirq; + + dev_dbg(&pcie->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); +} + +static int mobiveil_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static struct irq_chip mobiveil_msi_bottom_irq_chip = { + .name = "Mobiveil MSI", + .irq_compose_msi_msg = mobiveil_compose_msi_msg, + .irq_set_affinity = mobiveil_msi_set_affinity, +}; + +static int mobiveil_irq_msi_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, void *args) +{ + struct mobiveil_pcie *pcie = domain->host_data; + struct mobiveil_msi *msi = &pcie->msi; + unsigned long bit; + + WARN_ON(nr_irqs != 1); + mutex_lock(&msi->lock); + + bit = find_first_zero_bit(msi->msi_irq_in_use, msi->num_of_vectors); + if (bit >= msi->num_of_vectors) { + mutex_unlock(&msi->lock); + return -ENOSPC; + } + + set_bit(bit, msi->msi_irq_in_use); + + mutex_unlock(&msi->lock); + + irq_domain_set_info(domain, virq, bit, &mobiveil_msi_bottom_irq_chip, + domain->host_data, handle_level_irq, + NULL, NULL); + return 0; +} + +static void mobiveil_irq_msi_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(d); + struct mobiveil_msi *msi = &pcie->msi; + + mutex_lock(&msi->lock); + + if (!test_bit(d->hwirq, msi->msi_irq_in_use)) { + dev_err(&pcie->pdev->dev, "trying to free unused MSI#%lu\n", + d->hwirq); + } else { + __clear_bit(d->hwirq, msi->msi_irq_in_use); + } + + mutex_unlock(&msi->lock); +} +static const struct irq_domain_ops msi_domain_ops = { + .alloc = mobiveil_irq_msi_domain_alloc, + .free = mobiveil_irq_msi_domain_free, +}; + +static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); + struct mobiveil_msi *msi = &pcie->msi; + + mutex_init(&pcie->msi.lock); + msi->dev_domain = irq_domain_add_linear(NULL, msi->num_of_vectors, + &msi_domain_ops, pcie); + if (!msi->dev_domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + msi->msi_domain = pci_msi_create_irq_domain(fwnode, + &mobiveil_msi_domain_info, msi->dev_domain); + if (!msi->msi_domain) { + dev_err(dev, "failed to create MSI domain\n"); + irq_domain_remove(msi->dev_domain); + return -ENOMEM; + } + return 0; +} + +static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + int ret; + + /* setup INTx */ + pcie->intx_domain = irq_domain_add_linear(node, + PCI_NUM_INTX, &intx_domain_ops, pcie); + + if (!pcie->intx_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return -ENODEV; + } + + raw_spin_lock_init(&pcie->intx_mask_lock); + + /* setup MSI */ + ret = mobiveil_allocate_msi_domains(pcie); + if (ret) + return ret; + + return 0; +} + +static int mobiveil_pcie_probe(struct platform_device *pdev) +{ + struct mobiveil_pcie *pcie; + struct pci_bus *bus; + struct pci_bus *child; + struct pci_host_bridge *bridge; + struct device *dev = &pdev->dev; + resource_size_t iobase; + int ret; + + /* allocate the PCIe port */ + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!bridge) + return -ENODEV; + + pcie = pci_host_bridge_priv(bridge); + if (!pcie) + return -ENOMEM; + + pcie->pdev = pdev; + + ret = mobiveil_pcie_parse_dt(pcie); + if (ret) { + dev_err(dev, "Parsing DT failed, ret: %x\n", ret); + return ret; + } + + INIT_LIST_HEAD(&pcie->resources); + + /* parse the host bridge base addresses from the device tree file */ + ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, + &pcie->resources, &iobase); + if (ret) { + dev_err(dev, "Getting bridge resources failed\n"); + return -ENOMEM; + } + + /* + * configure all inbound and outbound windows and prepare the RC for + * config access + */ + ret = mobiveil_host_init(pcie); + if (ret) { + dev_err(dev, "Failed to initialize host\n"); + goto error; + } + + /* fixup for PCIe class register */ + csr_writel(pcie, 0x060402ab, PAB_INTP_AXI_PIO_CLASS); + + /* initialize the IRQ domains */ + ret = mobiveil_pcie_init_irq_domain(pcie); + if (ret) { + dev_err(dev, "Failed creating IRQ Domain\n"); + goto error; + } + + ret = devm_request_pci_bus_resources(dev, &pcie->resources); + if (ret) + goto error; + + /* Initialize bridge */ + list_splice_init(&pcie->resources, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = pcie; + bridge->busnr = pcie->root_bus_nr; + bridge->ops = &mobiveil_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + /* setup the kernel resources for the newly added PCIe root bus */ + ret = pci_scan_root_bus_bridge(bridge); + if (ret) + goto error; + + bus = bridge->bus; + + pci_assign_unassigned_bus_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + pci_bus_add_devices(bus); + + return 0; +error: + pci_free_resource_list(&pcie->resources); + return ret; +} + +static const struct of_device_id mobiveil_pcie_of_match[] = { + {.compatible = "mbvl,gpex40-pcie",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, mobiveil_pcie_of_match); + +static struct platform_driver mobiveil_pcie_driver = { + .probe = mobiveil_pcie_probe, + .driver = { + .name = "mobiveil-pcie", + .of_match_table = mobiveil_pcie_of_match, + .suppress_bind_attrs = true, + }, +}; + +builtin_platform_driver(mobiveil_pcie_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mobiveil PCIe host controller driver"); +MODULE_AUTHOR("Subrahmanya Lingappa "); diff --git a/drivers/pci/controller/pcie-rcar.c b/drivers/pci/controller/pcie-rcar.c new file mode 100644 index 000000000000..874d75c9ee4a --- /dev/null +++ b/drivers/pci/controller/pcie-rcar.c @@ -0,0 +1,1222 @@ +// 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 + +#include "../pci.h" + +#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 CFINIT 1 +#define PCIETSTR 0x02004 +#define DATA_LINK_ACTIVE 1 +#define PCIEERRFR 0x02020 +#define UNSUPPORTED_REQUEST BIT(4) +#define PCIEMSIFR 0x02044 +#define PCIEMSIALR 0x02048 +#define MSIFE 1 +#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 SPEED_CHANGE BIT(24) +#define SCRAMBLE_DISABLE BIT(27) +#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; +}; + +static void rcar_pci_write_reg(struct rcar_pcie *pcie, unsigned long val, + unsigned long reg) +{ + writel(val, pcie->base + reg); +} + +static unsigned long rcar_pci_read_reg(struct rcar_pcie *pcie, + unsigned long 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) +{ + int shift = 8 * (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) +{ + int shift = 8 * (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) +{ + 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 >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 2))) & 0xffff; + + dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08lx\n", + bus->number, devfn, where, size, (unsigned long)*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; + int shift, ret; + u32 data; + + 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%08lx\n", + bus->number, devfn, where, size, (unsigned long)val); + + if (size == 1) { + shift = 8 * (where & 3); + data &= ~(0xff << shift); + data |= ((val & 0xff) << shift); + } else if (size == 2) { + shift = 8 * (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 *res) +{ + /* Setup PCIe address space mappings for each resource */ + resource_size_t size; + resource_size_t res_start; + 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); + else + res_start = res->start; + + 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, res); + 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 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, unsigned int addr, + unsigned int lane, unsigned int data) +{ + unsigned long 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); + + /* 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; + + return phy_power_on(pcie->phy); +} + +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 irq; + + /* clear the interrupt */ + rcar_pci_write_reg(pcie, 1 << index, PCIEMSIFR); + + irq = irq_find_mapping(msi->domain, index); + if (irq) { + if (test_bit(index, msi->used)) + generic_handle_irq(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 int rcar_pcie_enable_msi(struct rcar_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct rcar_msi *msi = &pcie->msi; + unsigned long base; + 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_err(dev, "failed to request IRQ: %d\n", err); + goto err; + } + + /* setup MSI data target */ + msi->pages = __get_free_pages(GFP_KERNEL, 0); + base = virt_to_phys((void *)msi->pages); + + rcar_pci_write_reg(pcie, base | MSIFE, PCIEMSIALR); + rcar_pci_write_reg(pcie, 0, PCIEMSIAUR); + + /* enable all MSI interrupts */ + rcar_pci_write_reg(pcie, 0xffffffff, PCIEMSIIER); + + return 0; + +err: + rcar_pcie_unmap_msi(pcie); + return err; +} + +static void rcar_pcie_teardown_msi(struct rcar_pcie *pcie) +{ + struct rcar_msi *msi = &pcie->msi; + + /* Disable all MSI interrupts */ + rcar_pci_write_reg(pcie, 0, PCIEMSIIER); + + /* Disable address decoding of the MSI interrupt, MSIFE */ + rcar_pci_write_reg(pcie, 0, PCIEMSIALR); + + free_pages(msi->pages, 0); + + rcar_pcie_unmap_msi(pcie); +} + +static int rcar_pcie_get_resources(struct rcar_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct resource res; + int err, i; + + pcie->phy = devm_phy_optional_get(dev, "pcie"); + if (IS_ERR(pcie->phy)) + return PTR_ERR(pcie->phy); + + err = of_address_to_resource(dev->of_node, 0, &res); + if (err) + return err; + + pcie->base = devm_ioremap_resource(dev, &res); + if (IS_ERR(pcie->base)) + return PTR_ERR(pcie->base); + + pcie->bus_clk = devm_clk_get(dev, "pcie_bus"); + if (IS_ERR(pcie->bus_clk)) { + dev_err(dev, "cannot get pcie bus clock\n"); + return PTR_ERR(pcie->bus_clk); + } + + i = irq_of_parse_and_map(dev->of_node, 0); + if (!i) { + dev_err(dev, "cannot get platform resources for msi interrupt\n"); + err = -ENOENT; + goto err_irq1; + } + pcie->msi.irq1 = i; + + i = irq_of_parse_and_map(dev->of_node, 1); + if (!i) { + dev_err(dev, "cannot get platform resources for msi interrupt\n"); + err = -ENOENT; + goto err_irq2; + } + pcie->msi.irq2 = i; + + return 0; + +err_irq2: + irq_dispose_mapping(pcie->msi.irq1); +err_irq1: + return err; +} + +static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie, + struct of_pci_range *range, + int *index) +{ + u64 restype = range->flags; + u64 cpu_addr = range->cpu_addr; + u64 cpu_end = range->cpu_addr + range->size; + u64 pci_addr = range->pci_addr; + u32 flags = LAM_64BIT | LAR_ENABLE; + u64 mask; + u64 size; + int idx = *index; + + if (restype & IORESOURCE_PREFETCH) + flags |= LAM_PREFETCH; + + /* + * If the size of the range is larger than the alignment of the start + * address, we have to use multiple entries to perform the mapping. + */ + if (cpu_addr > 0) { + unsigned long nr_zeros = __ffs64(cpu_addr); + u64 alignment = 1ULL << nr_zeros; + + size = min(range->size, alignment); + } else { + size = range->size; + } + /* Hardware supports max 4GiB inbound region */ + size = min(size, 1ULL << 32); + + mask = roundup_pow_of_two(size) - 1; + mask &= ~0xf; + + while (cpu_addr < cpu_end) { + /* + * Set up 64-bit inbound regions as the range parser doesn't + * distinguish between 32 and 64-bit types. + */ + rcar_pci_write_reg(pcie, lower_32_bits(pci_addr), + PCIEPRAR(idx)); + rcar_pci_write_reg(pcie, lower_32_bits(cpu_addr), PCIELAR(idx)); + rcar_pci_write_reg(pcie, lower_32_bits(mask) | flags, + PCIELAMR(idx)); + + rcar_pci_write_reg(pcie, upper_32_bits(pci_addr), + PCIEPRAR(idx + 1)); + rcar_pci_write_reg(pcie, upper_32_bits(cpu_addr), + PCIELAR(idx + 1)); + rcar_pci_write_reg(pcie, 0, PCIELAMR(idx + 1)); + + pci_addr += size; + cpu_addr += size; + idx += 2; + + if (idx > MAX_NR_INBOUND_MAPS) { + dev_err(pcie->dev, "Failed to map inbound regions!\n"); + return -EINVAL; + } + } + *index = idx; + + return 0; +} + +static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie, + struct device_node *np) +{ + struct of_pci_range range; + struct of_pci_range_parser parser; + int index = 0; + int err; + + if (of_pci_dma_range_parser_init(&parser, np)) + return -EINVAL; + + /* Get the dma-ranges from DT */ + for_each_of_pci_range(&parser, &range) { + u64 end = range.cpu_addr + range.size - 1; + + dev_dbg(pcie->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", + range.flags, range.cpu_addr, end, range.pci_addr); + + err = rcar_pcie_inbound_ranges(pcie, &range, &index); + if (err) + return err; + } + + return 0; +} + +static const struct of_device_id rcar_pcie_of_match[] = { + { .compatible = "renesas,pcie-r8a7779", + .data = rcar_pcie_phy_init_h1 }, + { .compatible = "renesas,pcie-r8a7790", + .data = rcar_pcie_phy_init_gen2 }, + { .compatible = "renesas,pcie-r8a7791", + .data = rcar_pcie_phy_init_gen2 }, + { .compatible = "renesas,pcie-rcar-gen2", + .data = rcar_pcie_phy_init_gen2 }, + { .compatible = "renesas,pcie-r8a7795", + .data = rcar_pcie_phy_init_gen3 }, + { .compatible = "renesas,pcie-rcar-gen3", + .data = rcar_pcie_phy_init_gen3 }, + {}, +}; + +static int rcar_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_pcie *pcie; + unsigned int data; + int err; + int (*phy_init_fn)(struct rcar_pcie *); + struct pci_host_bridge *bridge; + + bridge = pci_alloc_host_bridge(sizeof(*pcie)); + if (!bridge) + return -ENOMEM; + + pcie = pci_host_bridge_priv(bridge); + + pcie->dev = dev; + + err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, NULL); + if (err) + goto err_free_bridge; + + pm_runtime_enable(pcie->dev); + err = pm_runtime_get_sync(pcie->dev); + if (err < 0) { + dev_err(pcie->dev, "pm_runtime_get_sync failed\n"); + goto err_pm_disable; + } + + err = rcar_pcie_get_resources(pcie); + if (err < 0) { + dev_err(dev, "failed to request resources: %d\n", err); + goto err_pm_put; + } + + err = clk_prepare_enable(pcie->bus_clk); + if (err) { + dev_err(dev, "failed to enable bus clock: %d\n", err); + goto err_unmap_msi_irqs; + } + + err = rcar_pcie_parse_map_dma_ranges(pcie, dev->of_node); + if (err) + goto err_clk_disable; + + phy_init_fn = of_device_get_match_data(dev); + err = phy_init_fn(pcie); + if (err) { + dev_err(dev, "failed to init PCIe PHY\n"); + goto err_clk_disable; + } + + /* Failure to get a link might just be that no cards are inserted */ + if (rcar_pcie_hw_init(pcie)) { + dev_info(dev, "PCIe link down\n"); + err = -ENODEV; + goto err_clk_disable; + } + + data = rcar_pci_read_reg(pcie, MACSR); + dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + err = rcar_pcie_enable_msi(pcie); + if (err < 0) { + dev_err(dev, + "failed to enable MSI support: %d\n", + err); + goto err_clk_disable; + } + } + + err = rcar_pcie_enable(pcie); + if (err) + goto err_msi_teardown; + + return 0; + +err_msi_teardown: + if (IS_ENABLED(CONFIG_PCI_MSI)) + rcar_pcie_teardown_msi(pcie); + +err_clk_disable: + clk_disable_unprepare(pcie->bus_clk); + +err_unmap_msi_irqs: + irq_dispose_mapping(pcie->msi.irq2); + irq_dispose_mapping(pcie->msi.irq1); + +err_pm_put: + pm_runtime_put(dev); + +err_pm_disable: + pm_runtime_disable(dev); + pci_free_resource_list(&pcie->resources); + +err_free_bridge: + pci_free_host_bridge(bridge); + + return err; +} + +static struct platform_driver rcar_pcie_driver = { + .driver = { + .name = "rcar-pcie", + .of_match_table = rcar_pcie_of_match, + .suppress_bind_attrs = true, + }, + .probe = rcar_pcie_probe, +}; +builtin_platform_driver(rcar_pcie_driver); diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c new file mode 100644 index 000000000000..fc267a49a932 --- /dev/null +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -0,0 +1,642 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Rockchip AXI PCIe endpoint controller driver + * + * Copyright (c) 2018 Rockchip, Inc. + * + * Author: Shawn Lin + * Simon Xue + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-rockchip.h" + +/** + * struct rockchip_pcie_ep - private data for PCIe endpoint controller driver + * @rockchip: Rockchip PCIe controller + * @max_regions: maximum number of regions supported by hardware + * @ob_region_map: bitmask of mapped outbound regions + * @ob_addr: base addresses in the AXI bus where the outbound regions start + * @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ + * dedicated outbound regions is mapped. + * @irq_cpu_addr: base address in the CPU space where a write access triggers + * the sending of a memory write (MSI) / normal message (legacy + * IRQ) TLP through the PCIe bus. + * @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ + * dedicated outbound region. + * @irq_pci_fn: the latest PCI function that has updated the mapping of + * the MSI/legacy IRQ dedicated outbound region. + * @irq_pending: bitmask of asserted legacy IRQs. + */ +struct rockchip_pcie_ep { + struct rockchip_pcie rockchip; + struct pci_epc *epc; + u32 max_regions; + unsigned long ob_region_map; + phys_addr_t *ob_addr; + phys_addr_t irq_phys_addr; + void __iomem *irq_cpu_addr; + u64 irq_pci_addr; + u8 irq_pci_fn; + u8 irq_pending; +}; + +static void rockchip_pcie_clear_ep_ob_atu(struct rockchip_pcie *rockchip, + u32 region) +{ + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(region)); + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(region)); + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_DESC0(region)); + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_DESC1(region)); + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(region)); + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(region)); +} + +static void rockchip_pcie_prog_ep_ob_atu(struct rockchip_pcie *rockchip, u8 fn, + u32 r, u32 type, u64 cpu_addr, + u64 pci_addr, size_t size) +{ + u64 sz = 1ULL << fls64(size - 1); + int num_pass_bits = ilog2(sz); + u32 addr0, addr1, desc0, desc1; + bool is_nor_msg = (type == AXI_WRAPPER_NOR_MSG); + + /* The minimal region size is 1MB */ + if (num_pass_bits < 8) + num_pass_bits = 8; + + cpu_addr -= rockchip->mem_res->start; + addr0 = ((is_nor_msg ? 0x10 : (num_pass_bits - 1)) & + PCIE_CORE_OB_REGION_ADDR0_NUM_BITS) | + (lower_32_bits(cpu_addr) & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); + addr1 = upper_32_bits(is_nor_msg ? cpu_addr : pci_addr); + desc0 = ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN(fn) | type; + desc1 = 0; + + if (is_nor_msg) { + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r)); + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r)); + rockchip_pcie_write(rockchip, desc0, + ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r)); + rockchip_pcie_write(rockchip, desc1, + ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r)); + } else { + /* PCI bus address region */ + rockchip_pcie_write(rockchip, addr0, + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r)); + rockchip_pcie_write(rockchip, addr1, + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r)); + rockchip_pcie_write(rockchip, desc0, + ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r)); + rockchip_pcie_write(rockchip, desc1, + ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r)); + + addr0 = + ((num_pass_bits - 1) & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS) | + (lower_32_bits(cpu_addr) & + PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); + addr1 = upper_32_bits(cpu_addr); + } + + /* CPU bus address region */ + rockchip_pcie_write(rockchip, addr0, + ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(r)); + rockchip_pcie_write(rockchip, addr1, + ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(r)); +} + +static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, + struct pci_epf_header *hdr) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + + /* All functions share the same vendor ID with function 0 */ + if (fn == 0) { + u32 vid_regs = (hdr->vendorid & GENMASK(15, 0)) | + (hdr->subsys_vendor_id & GENMASK(31, 16)) << 16; + + rockchip_pcie_write(rockchip, vid_regs, + PCIE_CORE_CONFIG_VENDOR); + } + + rockchip_pcie_write(rockchip, hdr->deviceid << 16, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + PCI_VENDOR_ID); + + rockchip_pcie_write(rockchip, + hdr->revid | + hdr->progif_code << 8 | + hdr->subclass_code << 16 | + hdr->baseclass_code << 24, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + PCI_REVISION_ID); + rockchip_pcie_write(rockchip, hdr->cache_line_size, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + PCI_CACHE_LINE_SIZE); + rockchip_pcie_write(rockchip, hdr->subsys_id << 16, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + PCI_SUBSYSTEM_VENDOR_ID); + rockchip_pcie_write(rockchip, hdr->interrupt_pin << 8, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + PCI_INTERRUPT_LINE); + + return 0; +} + +static int rockchip_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, + struct pci_epf_bar *epf_bar) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + dma_addr_t bar_phys = epf_bar->phys_addr; + enum pci_barno bar = epf_bar->barno; + int flags = epf_bar->flags; + u32 addr0, addr1, reg, cfg, b, aperture, ctrl; + u64 sz; + + /* BAR size is 2^(aperture + 7) */ + sz = max_t(size_t, epf_bar->size, MIN_EP_APERTURE); + + /* + * roundup_pow_of_two() returns an unsigned long, which is not suited + * for 64bit values. + */ + sz = 1ULL << fls64(sz - 1); + aperture = ilog2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */ + + if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { + ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_IO_32BITS; + } else { + bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH); + bool is_64bits = sz > SZ_2G; + + if (is_64bits && (bar & 1)) + return -EINVAL; + + if (is_64bits && is_prefetch) + ctrl = + ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; + else if (is_prefetch) + ctrl = + ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_32BITS; + else if (is_64bits) + ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_64BITS; + else + ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_32BITS; + } + + if (bar < BAR_4) { + reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0(fn); + b = bar; + } else { + reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG1(fn); + b = bar - BAR_4; + } + + addr0 = lower_32_bits(bar_phys); + addr1 = upper_32_bits(bar_phys); + + cfg = rockchip_pcie_read(rockchip, reg); + cfg &= ~(ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | + ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); + cfg |= (ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) | + ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl)); + + rockchip_pcie_write(rockchip, cfg, reg); + rockchip_pcie_write(rockchip, addr0, + ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar)); + rockchip_pcie_write(rockchip, addr1, + ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar)); + + return 0; +} + +static void rockchip_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, + struct pci_epf_bar *epf_bar) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + u32 reg, cfg, b, ctrl; + enum pci_barno bar = epf_bar->barno; + + if (bar < BAR_4) { + reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0(fn); + b = bar; + } else { + reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG1(fn); + b = bar - BAR_4; + } + + ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_DISABLED; + cfg = rockchip_pcie_read(rockchip, reg); + cfg &= ~(ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | + ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); + cfg |= ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl); + + rockchip_pcie_write(rockchip, cfg, reg); + rockchip_pcie_write(rockchip, 0x0, + ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar)); + rockchip_pcie_write(rockchip, 0x0, + ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar)); +} + +static int rockchip_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, + phys_addr_t addr, u64 pci_addr, + size_t size) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *pcie = &ep->rockchip; + u32 r; + + r = find_first_zero_bit(&ep->ob_region_map, + sizeof(ep->ob_region_map) * BITS_PER_LONG); + /* + * Region 0 is reserved for configuration space and shouldn't + * be used elsewhere per TRM, so leave it out. + */ + if (r >= ep->max_regions - 1) { + dev_err(&epc->dev, "no free outbound region\n"); + return -EINVAL; + } + + rockchip_pcie_prog_ep_ob_atu(pcie, fn, r, AXI_WRAPPER_MEM_WRITE, addr, + pci_addr, size); + + set_bit(r, &ep->ob_region_map); + ep->ob_addr[r] = addr; + + return 0; +} + +static void rockchip_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, + phys_addr_t addr) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + u32 r; + + for (r = 0; r < ep->max_regions - 1; r++) + if (ep->ob_addr[r] == addr) + break; + + /* + * Region 0 is reserved for configuration space and shouldn't + * be used elsewhere per TRM, so leave it out. + */ + if (r == ep->max_regions - 1) + return; + + rockchip_pcie_clear_ep_ob_atu(rockchip, r); + + ep->ob_addr[r] = 0; + clear_bit(r, &ep->ob_region_map); +} + +static int rockchip_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, + u8 multi_msg_cap) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + u16 flags; + + flags = rockchip_pcie_read(rockchip, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_MSI_CTRL_REG); + flags &= ~ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_MASK; + flags |= + ((multi_msg_cap << 1) << ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET) | + PCI_MSI_FLAGS_64BIT; + flags &= ~ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP; + rockchip_pcie_write(rockchip, flags, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_MSI_CTRL_REG); + return 0; +} + +static int rockchip_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + u16 flags; + + flags = rockchip_pcie_read(rockchip, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_MSI_CTRL_REG); + if (!(flags & ROCKCHIP_PCIE_EP_MSI_CTRL_ME)) + return -EINVAL; + + return ((flags & ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK) >> + ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET); +} + +static void rockchip_pcie_ep_assert_intx(struct rockchip_pcie_ep *ep, u8 fn, + u8 intx, bool is_asserted) +{ + struct rockchip_pcie *rockchip = &ep->rockchip; + u32 r = ep->max_regions - 1; + u32 offset; + u16 status; + u8 msg_code; + + if (unlikely(ep->irq_pci_addr != ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR || + ep->irq_pci_fn != fn)) { + rockchip_pcie_prog_ep_ob_atu(rockchip, fn, r, + AXI_WRAPPER_NOR_MSG, + ep->irq_phys_addr, 0, 0); + ep->irq_pci_addr = ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR; + ep->irq_pci_fn = fn; + } + + intx &= 3; + if (is_asserted) { + ep->irq_pending |= BIT(intx); + msg_code = ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTA + intx; + } else { + ep->irq_pending &= ~BIT(intx); + msg_code = ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTA + intx; + } + + status = rockchip_pcie_read(rockchip, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_CMD_STATUS); + status &= ROCKCHIP_PCIE_EP_CMD_STATUS_IS; + + if ((status != 0) ^ (ep->irq_pending != 0)) { + status ^= ROCKCHIP_PCIE_EP_CMD_STATUS_IS; + rockchip_pcie_write(rockchip, status, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_CMD_STATUS); + } + + offset = + ROCKCHIP_PCIE_MSG_ROUTING(ROCKCHIP_PCIE_MSG_ROUTING_LOCAL_INTX) | + ROCKCHIP_PCIE_MSG_CODE(msg_code) | ROCKCHIP_PCIE_MSG_NO_DATA; + writel(0, ep->irq_cpu_addr + offset); +} + +static int rockchip_pcie_ep_send_legacy_irq(struct rockchip_pcie_ep *ep, u8 fn, + u8 intx) +{ + u16 cmd; + + cmd = rockchip_pcie_read(&ep->rockchip, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_CMD_STATUS); + + if (cmd & PCI_COMMAND_INTX_DISABLE) + return -EINVAL; + + /* + * Should add some delay between toggling INTx per TRM vaguely saying + * it depends on some cycles of the AHB bus clock to function it. So + * add sufficient 1ms here. + */ + rockchip_pcie_ep_assert_intx(ep, fn, intx, true); + mdelay(1); + rockchip_pcie_ep_assert_intx(ep, fn, intx, false); + return 0; +} + +static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, + u8 interrupt_num) +{ + struct rockchip_pcie *rockchip = &ep->rockchip; + u16 flags, mme, data, data_mask; + u8 msi_count; + u64 pci_addr, pci_addr_mask = 0xff; + + /* Check MSI enable bit */ + flags = rockchip_pcie_read(&ep->rockchip, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_MSI_CTRL_REG); + if (!(flags & ROCKCHIP_PCIE_EP_MSI_CTRL_ME)) + return -EINVAL; + + /* Get MSI numbers from MME */ + mme = ((flags & ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK) >> + ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET); + msi_count = 1 << mme; + if (!interrupt_num || interrupt_num > msi_count) + return -EINVAL; + + /* Set MSI private data */ + data_mask = msi_count - 1; + data = rockchip_pcie_read(rockchip, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_MSI_CTRL_REG + + PCI_MSI_DATA_64); + data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask); + + /* Get MSI PCI address */ + pci_addr = rockchip_pcie_read(rockchip, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_MSI_CTRL_REG + + PCI_MSI_ADDRESS_HI); + pci_addr <<= 32; + pci_addr |= rockchip_pcie_read(rockchip, + ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + + ROCKCHIP_PCIE_EP_MSI_CTRL_REG + + PCI_MSI_ADDRESS_LO); + pci_addr &= GENMASK_ULL(63, 2); + + /* Set the outbound region if needed. */ + if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) || + ep->irq_pci_fn != fn)) { + rockchip_pcie_prog_ep_ob_atu(rockchip, fn, ep->max_regions - 1, + AXI_WRAPPER_MEM_WRITE, + ep->irq_phys_addr, + pci_addr & ~pci_addr_mask, + pci_addr_mask + 1); + ep->irq_pci_addr = (pci_addr & ~pci_addr_mask); + ep->irq_pci_fn = fn; + } + + writew(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask)); + return 0; +} + +static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, + enum pci_epc_irq_type type, + u8 interrupt_num) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + + switch (type) { + case PCI_EPC_IRQ_LEGACY: + return rockchip_pcie_ep_send_legacy_irq(ep, fn, 0); + case PCI_EPC_IRQ_MSI: + return rockchip_pcie_ep_send_msi_irq(ep, fn, interrupt_num); + default: + return -EINVAL; + } +} + +static int rockchip_pcie_ep_start(struct pci_epc *epc) +{ + struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); + struct rockchip_pcie *rockchip = &ep->rockchip; + struct pci_epf *epf; + u32 cfg; + + cfg = BIT(0); + list_for_each_entry(epf, &epc->pci_epf, list) + cfg |= BIT(epf->func_no); + + rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG); + + list_for_each_entry(epf, &epc->pci_epf, list) + pci_epf_linkup(epf); + + return 0; +} + +static const struct pci_epc_ops rockchip_pcie_epc_ops = { + .write_header = rockchip_pcie_ep_write_header, + .set_bar = rockchip_pcie_ep_set_bar, + .clear_bar = rockchip_pcie_ep_clear_bar, + .map_addr = rockchip_pcie_ep_map_addr, + .unmap_addr = rockchip_pcie_ep_unmap_addr, + .set_msi = rockchip_pcie_ep_set_msi, + .get_msi = rockchip_pcie_ep_get_msi, + .raise_irq = rockchip_pcie_ep_raise_irq, + .start = rockchip_pcie_ep_start, +}; + +static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip, + struct rockchip_pcie_ep *ep) +{ + struct device *dev = rockchip->dev; + int err; + + err = rockchip_pcie_parse_dt(rockchip); + if (err) + return err; + + err = rockchip_pcie_get_phys(rockchip); + if (err) + return err; + + err = of_property_read_u32(dev->of_node, + "rockchip,max-outbound-regions", + &ep->max_regions); + if (err < 0 || ep->max_regions > MAX_REGION_LIMIT) + ep->max_regions = MAX_REGION_LIMIT; + + err = of_property_read_u8(dev->of_node, "max-functions", + &ep->epc->max_functions); + if (err < 0) + ep->epc->max_functions = 1; + + return 0; +} + +static const struct of_device_id rockchip_pcie_ep_of_match[] = { + { .compatible = "rockchip,rk3399-pcie-ep"}, + {}, +}; + +static int rockchip_pcie_ep_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_pcie_ep *ep; + struct rockchip_pcie *rockchip; + struct pci_epc *epc; + size_t max_regions; + int err; + + ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + rockchip = &ep->rockchip; + rockchip->is_rc = false; + rockchip->dev = dev; + + epc = devm_pci_epc_create(dev, &rockchip_pcie_epc_ops); + if (IS_ERR(epc)) { + dev_err(dev, "failed to create epc device\n"); + return PTR_ERR(epc); + } + + ep->epc = epc; + epc_set_drvdata(epc, ep); + + err = rockchip_pcie_parse_ep_dt(rockchip, ep); + if (err) + return err; + + err = rockchip_pcie_enable_clocks(rockchip); + if (err) + return err; + + err = rockchip_pcie_init_port(rockchip); + if (err) + goto err_disable_clocks; + + /* Establish the link automatically */ + rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_ENABLE, + PCIE_CLIENT_CONFIG); + + max_regions = ep->max_regions; + ep->ob_addr = devm_kzalloc(dev, max_regions * sizeof(*ep->ob_addr), + GFP_KERNEL); + + if (!ep->ob_addr) { + err = -ENOMEM; + goto err_uninit_port; + } + + /* Only enable function 0 by default */ + rockchip_pcie_write(rockchip, BIT(0), PCIE_CORE_PHY_FUNC_CFG); + + err = pci_epc_mem_init(epc, rockchip->mem_res->start, + resource_size(rockchip->mem_res)); + if (err < 0) { + dev_err(dev, "failed to initialize the memory space\n"); + goto err_uninit_port; + } + + ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr, + SZ_128K); + if (!ep->irq_cpu_addr) { + dev_err(dev, "failed to reserve memory space for MSI\n"); + err = -ENOMEM; + goto err_epc_mem_exit; + } + + ep->irq_pci_addr = ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR; + + return 0; +err_epc_mem_exit: + pci_epc_mem_exit(epc); +err_uninit_port: + rockchip_pcie_deinit_phys(rockchip); +err_disable_clocks: + rockchip_pcie_disable_clocks(rockchip); + return err; +} + +static struct platform_driver rockchip_pcie_ep_driver = { + .driver = { + .name = "rockchip-pcie-ep", + .of_match_table = rockchip_pcie_ep_of_match, + }, + .probe = rockchip_pcie_ep_probe, +}; + +builtin_platform_driver(rockchip_pcie_ep_driver); diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c new file mode 100644 index 000000000000..1372d270764f --- /dev/null +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Rockchip AXI PCIe host controller driver + * + * Copyright (c) 2016 Rockchip, Inc. + * + * Author: Shawn Lin + * Wenrui Li + * + * Bits taken from Synopsys DesignWare Host controller driver and + * ARM PCI Host generic driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" +#include "pcie-rockchip.h" + +static void rockchip_pcie_enable_bw_int(struct rockchip_pcie *rockchip) +{ + u32 status; + + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); + status |= (PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE); + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); +} + +static void rockchip_pcie_clr_bw_int(struct rockchip_pcie *rockchip) +{ + u32 status; + + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); + status |= (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS) << 16; + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); +} + +static void rockchip_pcie_update_txcredit_mui(struct rockchip_pcie *rockchip) +{ + u32 val; + + /* Update Tx credit maximum update interval */ + val = rockchip_pcie_read(rockchip, PCIE_CORE_TXCREDIT_CFG1); + val &= ~PCIE_CORE_TXCREDIT_CFG1_MUI_MASK; + val |= PCIE_CORE_TXCREDIT_CFG1_MUI_ENCODE(24000); /* ns */ + rockchip_pcie_write(rockchip, val, PCIE_CORE_TXCREDIT_CFG1); +} + +static int rockchip_pcie_valid_device(struct rockchip_pcie *rockchip, + struct pci_bus *bus, int dev) +{ + /* access only one slot on each root port */ + if (bus->number == rockchip->root_bus_nr && dev > 0) + return 0; + + /* + * do not read more than one device on the bus directly attached + * to RC's downstream side. + */ + if (bus->primary == rockchip->root_bus_nr && dev > 0) + return 0; + + return 1; +} + +static u8 rockchip_pcie_lane_map(struct rockchip_pcie *rockchip) +{ + u32 val; + u8 map; + + if (rockchip->legacy_phy) + return GENMASK(MAX_LANE_NUM - 1, 0); + + val = rockchip_pcie_read(rockchip, PCIE_CORE_LANE_MAP); + map = val & PCIE_CORE_LANE_MAP_MASK; + + /* The link may be using a reverse-indexed mapping. */ + if (val & PCIE_CORE_LANE_MAP_REVERSE) + map = bitrev8(map) >> 4; + + return map; +} + +static int rockchip_pcie_rd_own_conf(struct rockchip_pcie *rockchip, + int where, int size, u32 *val) +{ + void __iomem *addr; + + addr = rockchip->apb_base + PCIE_RC_CONFIG_NORMAL_BASE + 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 rockchip_pcie_wr_own_conf(struct rockchip_pcie *rockchip, + int where, int size, u32 val) +{ + u32 mask, tmp, offset; + void __iomem *addr; + + offset = where & ~0x3; + addr = rockchip->apb_base + PCIE_RC_CONFIG_NORMAL_BASE + offset; + + if (size == 4) { + writel(val, addr); + return PCIBIOS_SUCCESSFUL; + } + + mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); + + /* + * N.B. This read/modify/write isn't safe in general because it can + * corrupt RW1C bits in adjacent registers. But the hardware + * doesn't support smaller writes. + */ + tmp = readl(addr) & mask; + tmp |= val << ((where & 0x3) * 8); + writel(tmp, addr); + + return PCIBIOS_SUCCESSFUL; +} + +static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip, + struct pci_bus *bus, u32 devfn, + int where, int size, u32 *val) +{ + u32 busdev; + + busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), where); + + if (!IS_ALIGNED(busdev, size)) { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (bus->parent->number == rockchip->root_bus_nr) + rockchip_pcie_cfg_configuration_accesses(rockchip, + AXI_WRAPPER_TYPE0_CFG); + else + rockchip_pcie_cfg_configuration_accesses(rockchip, + AXI_WRAPPER_TYPE1_CFG); + + if (size == 4) { + *val = readl(rockchip->reg_base + busdev); + } else if (size == 2) { + *val = readw(rockchip->reg_base + busdev); + } else if (size == 1) { + *val = readb(rockchip->reg_base + busdev); + } else { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + return PCIBIOS_SUCCESSFUL; +} + +static int rockchip_pcie_wr_other_conf(struct rockchip_pcie *rockchip, + struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + u32 busdev; + + busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), where); + if (!IS_ALIGNED(busdev, size)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (bus->parent->number == rockchip->root_bus_nr) + rockchip_pcie_cfg_configuration_accesses(rockchip, + AXI_WRAPPER_TYPE0_CFG); + else + rockchip_pcie_cfg_configuration_accesses(rockchip, + AXI_WRAPPER_TYPE1_CFG); + + if (size == 4) + writel(val, rockchip->reg_base + busdev); + else if (size == 2) + writew(val, rockchip->reg_base + busdev); + else if (size == 1) + writeb(val, rockchip->reg_base + busdev); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + struct rockchip_pcie *rockchip = bus->sysdata; + + if (!rockchip_pcie_valid_device(rockchip, bus, PCI_SLOT(devfn))) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (bus->number == rockchip->root_bus_nr) + return rockchip_pcie_rd_own_conf(rockchip, where, size, val); + + return rockchip_pcie_rd_other_conf(rockchip, bus, devfn, where, size, + val); +} + +static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct rockchip_pcie *rockchip = bus->sysdata; + + if (!rockchip_pcie_valid_device(rockchip, bus, PCI_SLOT(devfn))) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (bus->number == rockchip->root_bus_nr) + return rockchip_pcie_wr_own_conf(rockchip, where, size, val); + + return rockchip_pcie_wr_other_conf(rockchip, bus, devfn, where, size, + val); +} + +static struct pci_ops rockchip_pcie_ops = { + .read = rockchip_pcie_rd_conf, + .write = rockchip_pcie_wr_conf, +}; + +static void rockchip_pcie_set_power_limit(struct rockchip_pcie *rockchip) +{ + int curr; + u32 status, scale, power; + + if (IS_ERR(rockchip->vpcie3v3)) + return; + + /* + * Set RC's captured slot power limit and scale if + * vpcie3v3 available. The default values are both zero + * which means the software should set these two according + * to the actual power supply. + */ + curr = regulator_get_current_limit(rockchip->vpcie3v3); + if (curr <= 0) + return; + + scale = 3; /* 0.001x */ + curr = curr / 1000; /* convert to mA */ + power = (curr * 3300) / 1000; /* milliwatt */ + while (power > PCIE_RC_CONFIG_DCR_CSPL_LIMIT) { + if (!scale) { + dev_warn(rockchip->dev, "invalid power supply\n"); + return; + } + scale--; + power = power / 10; + } + + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_DCR); + status |= (power << PCIE_RC_CONFIG_DCR_CSPL_SHIFT) | + (scale << PCIE_RC_CONFIG_DCR_CPLS_SHIFT); + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_DCR); +} + +/** + * rockchip_pcie_host_init_port - Initialize hardware + * @rockchip: PCIe port information + */ +static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + int err, i = MAX_LANE_NUM; + u32 status; + + gpiod_set_value_cansleep(rockchip->ep_gpio, 0); + + err = rockchip_pcie_init_port(rockchip); + if (err) + return err; + + /* Fix the transmitted FTS count desired to exit from L0s. */ + status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL_PLC1); + status = (status & ~PCIE_CORE_CTRL_PLC1_FTS_MASK) | + (PCIE_CORE_CTRL_PLC1_FTS_CNT << PCIE_CORE_CTRL_PLC1_FTS_SHIFT); + rockchip_pcie_write(rockchip, status, PCIE_CORE_CTRL_PLC1); + + rockchip_pcie_set_power_limit(rockchip); + + /* Set RC's clock architecture as common clock */ + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); + status |= PCI_EXP_LNKSTA_SLC << 16; + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); + + /* Set RC's RCB to 128 */ + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); + status |= PCI_EXP_LNKCTL_RCB; + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); + + /* Enable Gen1 training */ + rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_ENABLE, + PCIE_CLIENT_CONFIG); + + gpiod_set_value_cansleep(rockchip->ep_gpio, 1); + + /* 500ms timeout value should be enough for Gen1/2 training */ + err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1, + status, PCIE_LINK_UP(status), 20, + 500 * USEC_PER_MSEC); + if (err) { + dev_err(dev, "PCIe link training gen1 timeout!\n"); + goto err_power_off_phy; + } + + if (rockchip->link_gen == 2) { + /* + * Enable retrain for gen2. This should be configured only after + * gen1 finished. + */ + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); + status |= PCI_EXP_LNKCTL_RL; + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); + + err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL, + status, PCIE_LINK_IS_GEN2(status), 20, + 500 * USEC_PER_MSEC); + if (err) + dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n"); + } + + /* Check the final link width from negotiated lane counter from MGMT */ + status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL); + status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >> + PCIE_CORE_PL_CONF_LANE_SHIFT); + dev_dbg(dev, "current link width is x%d\n", status); + + /* Power off unused lane(s) */ + rockchip->lanes_map = rockchip_pcie_lane_map(rockchip); + for (i = 0; i < MAX_LANE_NUM; i++) { + if (!(rockchip->lanes_map & BIT(i))) { + dev_dbg(dev, "idling lane %d\n", i); + phy_power_off(rockchip->phys[i]); + } + } + + rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID, + PCIE_CORE_CONFIG_VENDOR); + rockchip_pcie_write(rockchip, + PCI_CLASS_BRIDGE_PCI << PCIE_RC_CONFIG_SCC_SHIFT, + PCIE_RC_CONFIG_RID_CCR); + + /* Clear THP cap's next cap pointer to remove L1 substate cap */ + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_THP_CAP); + status &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK; + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_THP_CAP); + + /* Clear L0s from RC's link cap */ + if (of_property_read_bool(dev->of_node, "aspm-no-l0s")) { + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LINK_CAP); + status &= ~PCIE_RC_CONFIG_LINK_CAP_L0S; + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LINK_CAP); + } + + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_DCSR); + status &= ~PCIE_RC_CONFIG_DCSR_MPS_MASK; + status |= PCIE_RC_CONFIG_DCSR_MPS_256; + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_DCSR); + + return 0; +err_power_off_phy: + while (i--) + phy_power_off(rockchip->phys[i]); + i = MAX_LANE_NUM; + while (i--) + phy_exit(rockchip->phys[i]); + return err; +} + +static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg) +{ + struct rockchip_pcie *rockchip = arg; + struct device *dev = rockchip->dev; + u32 reg; + u32 sub_reg; + + reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS); + if (reg & PCIE_CLIENT_INT_LOCAL) { + dev_dbg(dev, "local interrupt received\n"); + sub_reg = rockchip_pcie_read(rockchip, PCIE_CORE_INT_STATUS); + if (sub_reg & PCIE_CORE_INT_PRFPE) + dev_dbg(dev, "parity error detected while reading from the PNP receive FIFO RAM\n"); + + if (sub_reg & PCIE_CORE_INT_CRFPE) + dev_dbg(dev, "parity error detected while reading from the Completion Receive FIFO RAM\n"); + + if (sub_reg & PCIE_CORE_INT_RRPE) + dev_dbg(dev, "parity error detected while reading from replay buffer RAM\n"); + + if (sub_reg & PCIE_CORE_INT_PRFO) + dev_dbg(dev, "overflow occurred in the PNP receive FIFO\n"); + + if (sub_reg & PCIE_CORE_INT_CRFO) + dev_dbg(dev, "overflow occurred in the completion receive FIFO\n"); + + if (sub_reg & PCIE_CORE_INT_RT) + dev_dbg(dev, "replay timer timed out\n"); + + if (sub_reg & PCIE_CORE_INT_RTR) + dev_dbg(dev, "replay timer rolled over after 4 transmissions of the same TLP\n"); + + if (sub_reg & PCIE_CORE_INT_PE) + dev_dbg(dev, "phy error detected on receive side\n"); + + if (sub_reg & PCIE_CORE_INT_MTR) + dev_dbg(dev, "malformed TLP received from the link\n"); + + if (sub_reg & PCIE_CORE_INT_UCR) + dev_dbg(dev, "malformed TLP received from the link\n"); + + if (sub_reg & PCIE_CORE_INT_FCE) + dev_dbg(dev, "an error was observed in the flow control advertisements from the other side\n"); + + if (sub_reg & PCIE_CORE_INT_CT) + dev_dbg(dev, "a request timed out waiting for completion\n"); + + if (sub_reg & PCIE_CORE_INT_UTC) + dev_dbg(dev, "unmapped TC error\n"); + + if (sub_reg & PCIE_CORE_INT_MMVC) + dev_dbg(dev, "MSI mask register changes\n"); + + rockchip_pcie_write(rockchip, sub_reg, PCIE_CORE_INT_STATUS); + } else if (reg & PCIE_CLIENT_INT_PHY) { + dev_dbg(dev, "phy link changes\n"); + rockchip_pcie_update_txcredit_mui(rockchip); + rockchip_pcie_clr_bw_int(rockchip); + } + + rockchip_pcie_write(rockchip, reg & PCIE_CLIENT_INT_LOCAL, + PCIE_CLIENT_INT_STATUS); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg) +{ + struct rockchip_pcie *rockchip = arg; + struct device *dev = rockchip->dev; + u32 reg; + + reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS); + if (reg & PCIE_CLIENT_INT_LEGACY_DONE) + dev_dbg(dev, "legacy done interrupt received\n"); + + if (reg & PCIE_CLIENT_INT_MSG) + dev_dbg(dev, "message done interrupt received\n"); + + if (reg & PCIE_CLIENT_INT_HOT_RST) + dev_dbg(dev, "hot reset interrupt received\n"); + + if (reg & PCIE_CLIENT_INT_DPA) + dev_dbg(dev, "dpa interrupt received\n"); + + if (reg & PCIE_CLIENT_INT_FATAL_ERR) + dev_dbg(dev, "fatal error interrupt received\n"); + + if (reg & PCIE_CLIENT_INT_NFATAL_ERR) + dev_dbg(dev, "no fatal error interrupt received\n"); + + if (reg & PCIE_CLIENT_INT_CORR_ERR) + dev_dbg(dev, "correctable error interrupt received\n"); + + if (reg & PCIE_CLIENT_INT_PHY) + dev_dbg(dev, "phy interrupt received\n"); + + rockchip_pcie_write(rockchip, reg & (PCIE_CLIENT_INT_LEGACY_DONE | + PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_HOT_RST | + PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_FATAL_ERR | + PCIE_CLIENT_INT_NFATAL_ERR | + PCIE_CLIENT_INT_CORR_ERR | + PCIE_CLIENT_INT_PHY), + PCIE_CLIENT_INT_STATUS); + + return IRQ_HANDLED; +} + +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc); + struct device *dev = rockchip->dev; + u32 reg; + u32 hwirq; + u32 virq; + + chained_irq_enter(chip, desc); + + reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS); + reg = (reg & PCIE_CLIENT_INTR_MASK) >> PCIE_CLIENT_INTR_SHIFT; + + while (reg) { + hwirq = ffs(reg) - 1; + reg &= ~BIT(hwirq); + + virq = irq_find_mapping(rockchip->irq_domain, hwirq); + if (virq) + generic_handle_irq(virq); + else + dev_err(dev, "unexpected IRQ, INT%d\n", hwirq); + } + + chained_irq_exit(chip, desc); +} + +static int rockchip_pcie_setup_irq(struct rockchip_pcie *rockchip) +{ + int irq, err; + struct device *dev = rockchip->dev; + struct platform_device *pdev = to_platform_device(dev); + + irq = platform_get_irq_byname(pdev, "sys"); + if (irq < 0) { + dev_err(dev, "missing sys IRQ resource\n"); + return irq; + } + + err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler, + IRQF_SHARED, "pcie-sys", rockchip); + if (err) { + dev_err(dev, "failed to request PCIe subsystem IRQ\n"); + return err; + } + + irq = platform_get_irq_byname(pdev, "legacy"); + if (irq < 0) { + dev_err(dev, "missing legacy IRQ resource\n"); + return irq; + } + + irq_set_chained_handler_and_data(irq, + rockchip_pcie_legacy_int_handler, + rockchip); + + irq = platform_get_irq_byname(pdev, "client"); + if (irq < 0) { + dev_err(dev, "missing client IRQ resource\n"); + return irq; + } + + err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler, + IRQF_SHARED, "pcie-client", rockchip); + if (err) { + dev_err(dev, "failed to request PCIe client IRQ\n"); + return err; + } + + return 0; +} + +/** + * rockchip_pcie_parse_host_dt - Parse Device Tree + * @rockchip: PCIe port information + * + * Return: '0' on success and error value on failure + */ +static int rockchip_pcie_parse_host_dt(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + int err; + + err = rockchip_pcie_parse_dt(rockchip); + if (err) + return err; + + err = rockchip_pcie_setup_irq(rockchip); + if (err) + return err; + + rockchip->vpcie12v = devm_regulator_get_optional(dev, "vpcie12v"); + if (IS_ERR(rockchip->vpcie12v)) { + if (PTR_ERR(rockchip->vpcie12v) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "no vpcie12v regulator found\n"); + } + + rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3"); + if (IS_ERR(rockchip->vpcie3v3)) { + if (PTR_ERR(rockchip->vpcie3v3) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "no vpcie3v3 regulator found\n"); + } + + rockchip->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8"); + if (IS_ERR(rockchip->vpcie1v8)) { + if (PTR_ERR(rockchip->vpcie1v8) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "no vpcie1v8 regulator found\n"); + } + + rockchip->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9"); + if (IS_ERR(rockchip->vpcie0v9)) { + if (PTR_ERR(rockchip->vpcie0v9) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "no vpcie0v9 regulator found\n"); + } + + return 0; +} + +static int rockchip_pcie_set_vpcie(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + int err; + + if (!IS_ERR(rockchip->vpcie12v)) { + err = regulator_enable(rockchip->vpcie12v); + if (err) { + dev_err(dev, "fail to enable vpcie12v regulator\n"); + goto err_out; + } + } + + if (!IS_ERR(rockchip->vpcie3v3)) { + err = regulator_enable(rockchip->vpcie3v3); + if (err) { + dev_err(dev, "fail to enable vpcie3v3 regulator\n"); + goto err_disable_12v; + } + } + + if (!IS_ERR(rockchip->vpcie1v8)) { + err = regulator_enable(rockchip->vpcie1v8); + if (err) { + dev_err(dev, "fail to enable vpcie1v8 regulator\n"); + goto err_disable_3v3; + } + } + + if (!IS_ERR(rockchip->vpcie0v9)) { + err = regulator_enable(rockchip->vpcie0v9); + if (err) { + dev_err(dev, "fail to enable vpcie0v9 regulator\n"); + goto err_disable_1v8; + } + } + + return 0; + +err_disable_1v8: + if (!IS_ERR(rockchip->vpcie1v8)) + regulator_disable(rockchip->vpcie1v8); +err_disable_3v3: + if (!IS_ERR(rockchip->vpcie3v3)) + regulator_disable(rockchip->vpcie3v3); +err_disable_12v: + if (!IS_ERR(rockchip->vpcie12v)) + regulator_disable(rockchip->vpcie12v); +err_out: + return err; +} + +static void rockchip_pcie_enable_interrupts(struct rockchip_pcie *rockchip) +{ + rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) & + (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK); + rockchip_pcie_write(rockchip, (u32)(~PCIE_CORE_INT), + PCIE_CORE_INT_MASK); + + rockchip_pcie_enable_bw_int(rockchip); +} + +static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { + .map = rockchip_pcie_intx_map, +}; + +static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + struct device_node *intc = of_get_next_child(dev->of_node, NULL); + + if (!intc) { + dev_err(dev, "missing child interrupt-controller node\n"); + return -EINVAL; + } + + rockchip->irq_domain = irq_domain_add_linear(intc, PCI_NUM_INTX, + &intx_domain_ops, rockchip); + if (!rockchip->irq_domain) { + dev_err(dev, "failed to get a INTx IRQ domain\n"); + return -EINVAL; + } + + return 0; +} + +static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie *rockchip, + int region_no, int type, u8 num_pass_bits, + u32 lower_addr, u32 upper_addr) +{ + u32 ob_addr_0; + u32 ob_addr_1; + u32 ob_desc_0; + u32 aw_offset; + + if (region_no >= MAX_AXI_WRAPPER_REGION_NUM) + return -EINVAL; + if (num_pass_bits + 1 < 8) + return -EINVAL; + if (num_pass_bits > 63) + return -EINVAL; + if (region_no == 0) { + if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits)) + return -EINVAL; + } + if (region_no != 0) { + if (AXI_REGION_SIZE < (2ULL << num_pass_bits)) + return -EINVAL; + } + + aw_offset = (region_no << OB_REG_SIZE_SHIFT); + + ob_addr_0 = num_pass_bits & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS; + ob_addr_0 |= lower_addr & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR; + ob_addr_1 = upper_addr; + ob_desc_0 = (1 << 23 | type); + + rockchip_pcie_write(rockchip, ob_addr_0, + PCIE_CORE_OB_REGION_ADDR0 + aw_offset); + rockchip_pcie_write(rockchip, ob_addr_1, + PCIE_CORE_OB_REGION_ADDR1 + aw_offset); + rockchip_pcie_write(rockchip, ob_desc_0, + PCIE_CORE_OB_REGION_DESC0 + aw_offset); + rockchip_pcie_write(rockchip, 0, + PCIE_CORE_OB_REGION_DESC1 + aw_offset); + + return 0; +} + +static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie *rockchip, + int region_no, u8 num_pass_bits, + u32 lower_addr, u32 upper_addr) +{ + u32 ib_addr_0; + u32 ib_addr_1; + u32 aw_offset; + + if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM) + return -EINVAL; + if (num_pass_bits + 1 < MIN_AXI_ADDR_BITS_PASSED) + return -EINVAL; + if (num_pass_bits > 63) + return -EINVAL; + + aw_offset = (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT); + + ib_addr_0 = num_pass_bits & PCIE_CORE_IB_REGION_ADDR0_NUM_BITS; + ib_addr_0 |= (lower_addr << 8) & PCIE_CORE_IB_REGION_ADDR0_LO_ADDR; + ib_addr_1 = upper_addr; + + rockchip_pcie_write(rockchip, ib_addr_0, PCIE_RP_IB_ADDR0 + aw_offset); + rockchip_pcie_write(rockchip, ib_addr_1, PCIE_RP_IB_ADDR1 + aw_offset); + + return 0; +} + +static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + int offset; + int err; + int reg_no; + + rockchip_pcie_cfg_configuration_accesses(rockchip, + AXI_WRAPPER_TYPE0_CFG); + + for (reg_no = 0; reg_no < (rockchip->mem_size >> 20); reg_no++) { + err = rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1, + AXI_WRAPPER_MEM_WRITE, + 20 - 1, + rockchip->mem_bus_addr + + (reg_no << 20), + 0); + if (err) { + dev_err(dev, "program RC mem outbound ATU failed\n"); + return err; + } + } + + err = rockchip_pcie_prog_ib_atu(rockchip, 2, 32 - 1, 0x0, 0); + if (err) { + dev_err(dev, "program RC mem inbound ATU failed\n"); + return err; + } + + offset = rockchip->mem_size >> 20; + for (reg_no = 0; reg_no < (rockchip->io_size >> 20); reg_no++) { + err = rockchip_pcie_prog_ob_atu(rockchip, + reg_no + 1 + offset, + AXI_WRAPPER_IO_WRITE, + 20 - 1, + rockchip->io_bus_addr + + (reg_no << 20), + 0); + if (err) { + dev_err(dev, "program RC io outbound ATU failed\n"); + return err; + } + } + + /* assign message regions */ + rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset, + AXI_WRAPPER_NOR_MSG, + 20 - 1, 0, 0); + + rockchip->msg_bus_addr = rockchip->mem_bus_addr + + ((reg_no + offset) << 20); + return err; +} + +static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip) +{ + u32 value; + int err; + + /* send PME_TURN_OFF message */ + writel(0x0, rockchip->msg_region + PCIE_RC_SEND_PME_OFF); + + /* read LTSSM and wait for falling into L2 link state */ + err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0, + value, PCIE_LINK_IS_L2(value), 20, + jiffies_to_usecs(5 * HZ)); + if (err) { + dev_err(rockchip->dev, "PCIe link enter L2 timeout!\n"); + return err; + } + + return 0; +} + +static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) +{ + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + int ret; + + /* disable core and cli int since we don't need to ack PME_ACK */ + rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) | + PCIE_CLIENT_INT_CLI, PCIE_CLIENT_INT_MASK); + rockchip_pcie_write(rockchip, (u32)PCIE_CORE_INT, PCIE_CORE_INT_MASK); + + ret = rockchip_pcie_wait_l2(rockchip); + if (ret) { + rockchip_pcie_enable_interrupts(rockchip); + return ret; + } + + rockchip_pcie_deinit_phys(rockchip); + + rockchip_pcie_disable_clocks(rockchip); + + if (!IS_ERR(rockchip->vpcie0v9)) + regulator_disable(rockchip->vpcie0v9); + + return ret; +} + +static int __maybe_unused rockchip_pcie_resume_noirq(struct device *dev) +{ + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + int err; + + if (!IS_ERR(rockchip->vpcie0v9)) { + err = regulator_enable(rockchip->vpcie0v9); + if (err) { + dev_err(dev, "fail to enable vpcie0v9 regulator\n"); + return err; + } + } + + err = rockchip_pcie_enable_clocks(rockchip); + if (err) + goto err_disable_0v9; + + err = rockchip_pcie_host_init_port(rockchip); + if (err) + goto err_pcie_resume; + + err = rockchip_pcie_cfg_atu(rockchip); + if (err) + goto err_err_deinit_port; + + /* Need this to enter L1 again */ + rockchip_pcie_update_txcredit_mui(rockchip); + rockchip_pcie_enable_interrupts(rockchip); + + return 0; + +err_err_deinit_port: + rockchip_pcie_deinit_phys(rockchip); +err_pcie_resume: + rockchip_pcie_disable_clocks(rockchip); +err_disable_0v9: + if (!IS_ERR(rockchip->vpcie0v9)) + regulator_disable(rockchip->vpcie0v9); + return err; +} + +static int rockchip_pcie_probe(struct platform_device *pdev) +{ + struct rockchip_pcie *rockchip; + struct device *dev = &pdev->dev; + struct pci_bus *bus, *child; + struct pci_host_bridge *bridge; + struct resource_entry *win; + resource_size_t io_base; + struct resource *mem; + struct resource *io; + int err; + + LIST_HEAD(res); + + if (!dev->of_node) + return -ENODEV; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rockchip)); + if (!bridge) + return -ENOMEM; + + rockchip = pci_host_bridge_priv(bridge); + + platform_set_drvdata(pdev, rockchip); + rockchip->dev = dev; + rockchip->is_rc = true; + + err = rockchip_pcie_parse_host_dt(rockchip); + if (err) + return err; + + err = rockchip_pcie_enable_clocks(rockchip); + if (err) + return err; + + err = rockchip_pcie_set_vpcie(rockchip); + if (err) { + dev_err(dev, "failed to set vpcie regulator\n"); + goto err_set_vpcie; + } + + err = rockchip_pcie_host_init_port(rockchip); + if (err) + goto err_vpcie; + + rockchip_pcie_enable_interrupts(rockchip); + + err = rockchip_pcie_init_irq_domain(rockchip); + if (err < 0) + goto err_deinit_port; + + err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, + &res, &io_base); + if (err) + goto err_remove_irq_domain; + + err = devm_request_pci_bus_resources(dev, &res); + if (err) + goto err_free_res; + + /* Get the I/O and memory ranges from DT */ + resource_list_for_each_entry(win, &res) { + switch (resource_type(win->res)) { + case IORESOURCE_IO: + io = win->res; + io->name = "I/O"; + rockchip->io_size = resource_size(io); + rockchip->io_bus_addr = io->start - win->offset; + err = pci_remap_iospace(io, io_base); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, io); + continue; + } + rockchip->io = io; + break; + case IORESOURCE_MEM: + mem = win->res; + mem->name = "MEM"; + rockchip->mem_size = resource_size(mem); + rockchip->mem_bus_addr = mem->start - win->offset; + break; + case IORESOURCE_BUS: + rockchip->root_bus_nr = win->res->start; + break; + default: + continue; + } + } + + err = rockchip_pcie_cfg_atu(rockchip); + if (err) + goto err_unmap_iospace; + + rockchip->msg_region = devm_ioremap(dev, rockchip->msg_bus_addr, SZ_1M); + if (!rockchip->msg_region) { + err = -ENOMEM; + goto err_unmap_iospace; + } + + list_splice_init(&res, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = rockchip; + bridge->busnr = 0; + bridge->ops = &rockchip_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + err = pci_scan_root_bus_bridge(bridge); + if (err < 0) + goto err_unmap_iospace; + + bus = bridge->bus; + + rockchip->root_bus = 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; + +err_unmap_iospace: + pci_unmap_iospace(rockchip->io); +err_free_res: + pci_free_resource_list(&res); +err_remove_irq_domain: + irq_domain_remove(rockchip->irq_domain); +err_deinit_port: + rockchip_pcie_deinit_phys(rockchip); +err_vpcie: + if (!IS_ERR(rockchip->vpcie12v)) + regulator_disable(rockchip->vpcie12v); + if (!IS_ERR(rockchip->vpcie3v3)) + regulator_disable(rockchip->vpcie3v3); + if (!IS_ERR(rockchip->vpcie1v8)) + regulator_disable(rockchip->vpcie1v8); + if (!IS_ERR(rockchip->vpcie0v9)) + regulator_disable(rockchip->vpcie0v9); +err_set_vpcie: + rockchip_pcie_disable_clocks(rockchip); + return err; +} + +static int rockchip_pcie_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + + pci_stop_root_bus(rockchip->root_bus); + pci_remove_root_bus(rockchip->root_bus); + pci_unmap_iospace(rockchip->io); + irq_domain_remove(rockchip->irq_domain); + + rockchip_pcie_deinit_phys(rockchip); + + rockchip_pcie_disable_clocks(rockchip); + + if (!IS_ERR(rockchip->vpcie12v)) + regulator_disable(rockchip->vpcie12v); + if (!IS_ERR(rockchip->vpcie3v3)) + regulator_disable(rockchip->vpcie3v3); + if (!IS_ERR(rockchip->vpcie1v8)) + regulator_disable(rockchip->vpcie1v8); + if (!IS_ERR(rockchip->vpcie0v9)) + regulator_disable(rockchip->vpcie0v9); + + return 0; +} + +static const struct dev_pm_ops rockchip_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq, + rockchip_pcie_resume_noirq) +}; + +static const struct of_device_id rockchip_pcie_of_match[] = { + { .compatible = "rockchip,rk3399-pcie", }, + {} +}; +MODULE_DEVICE_TABLE(of, rockchip_pcie_of_match); + +static struct platform_driver rockchip_pcie_driver = { + .driver = { + .name = "rockchip-pcie", + .of_match_table = rockchip_pcie_of_match, + .pm = &rockchip_pcie_pm_ops, + }, + .probe = rockchip_pcie_probe, + .remove = rockchip_pcie_remove, +}; +module_platform_driver(rockchip_pcie_driver); + +MODULE_AUTHOR("Rockchip Inc"); +MODULE_DESCRIPTION("Rockchip AXI PCIe driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c new file mode 100644 index 000000000000..c53d1322a3d6 --- /dev/null +++ b/drivers/pci/controller/pcie-rockchip.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Rockchip AXI PCIe host controller driver + * + * Copyright (c) 2016 Rockchip, Inc. + * + * Author: Shawn Lin + * Wenrui Li + * + * Bits taken from Synopsys DesignWare Host controller driver and + * ARM PCI Host generic driver. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" +#include "pcie-rockchip.h" + +int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + struct platform_device *pdev = to_platform_device(dev); + struct device_node *node = dev->of_node; + struct resource *regs; + int err; + + if (rockchip->is_rc) { + regs = platform_get_resource_byname(pdev, + IORESOURCE_MEM, + "axi-base"); + rockchip->reg_base = devm_pci_remap_cfg_resource(dev, regs); + if (IS_ERR(rockchip->reg_base)) + return PTR_ERR(rockchip->reg_base); + } else { + rockchip->mem_res = + platform_get_resource_byname(pdev, IORESOURCE_MEM, + "mem-base"); + if (!rockchip->mem_res) + return -EINVAL; + } + + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "apb-base"); + rockchip->apb_base = devm_ioremap_resource(dev, regs); + if (IS_ERR(rockchip->apb_base)) + return PTR_ERR(rockchip->apb_base); + + err = rockchip_pcie_get_phys(rockchip); + if (err) + return err; + + rockchip->lanes = 1; + err = of_property_read_u32(node, "num-lanes", &rockchip->lanes); + if (!err && (rockchip->lanes == 0 || + rockchip->lanes == 3 || + rockchip->lanes > 4)) { + dev_warn(dev, "invalid num-lanes, default to use one lane\n"); + rockchip->lanes = 1; + } + + rockchip->link_gen = of_pci_get_max_link_speed(node); + if (rockchip->link_gen < 0 || rockchip->link_gen > 2) + rockchip->link_gen = 2; + + rockchip->core_rst = devm_reset_control_get_exclusive(dev, "core"); + if (IS_ERR(rockchip->core_rst)) { + if (PTR_ERR(rockchip->core_rst) != -EPROBE_DEFER) + dev_err(dev, "missing core reset property in node\n"); + return PTR_ERR(rockchip->core_rst); + } + + rockchip->mgmt_rst = devm_reset_control_get_exclusive(dev, "mgmt"); + if (IS_ERR(rockchip->mgmt_rst)) { + if (PTR_ERR(rockchip->mgmt_rst) != -EPROBE_DEFER) + dev_err(dev, "missing mgmt reset property in node\n"); + return PTR_ERR(rockchip->mgmt_rst); + } + + rockchip->mgmt_sticky_rst = devm_reset_control_get_exclusive(dev, + "mgmt-sticky"); + if (IS_ERR(rockchip->mgmt_sticky_rst)) { + if (PTR_ERR(rockchip->mgmt_sticky_rst) != -EPROBE_DEFER) + dev_err(dev, "missing mgmt-sticky reset property in node\n"); + return PTR_ERR(rockchip->mgmt_sticky_rst); + } + + rockchip->pipe_rst = devm_reset_control_get_exclusive(dev, "pipe"); + if (IS_ERR(rockchip->pipe_rst)) { + if (PTR_ERR(rockchip->pipe_rst) != -EPROBE_DEFER) + dev_err(dev, "missing pipe reset property in node\n"); + return PTR_ERR(rockchip->pipe_rst); + } + + rockchip->pm_rst = devm_reset_control_get_exclusive(dev, "pm"); + if (IS_ERR(rockchip->pm_rst)) { + if (PTR_ERR(rockchip->pm_rst) != -EPROBE_DEFER) + dev_err(dev, "missing pm reset property in node\n"); + return PTR_ERR(rockchip->pm_rst); + } + + rockchip->pclk_rst = devm_reset_control_get_exclusive(dev, "pclk"); + if (IS_ERR(rockchip->pclk_rst)) { + if (PTR_ERR(rockchip->pclk_rst) != -EPROBE_DEFER) + dev_err(dev, "missing pclk reset property in node\n"); + return PTR_ERR(rockchip->pclk_rst); + } + + rockchip->aclk_rst = devm_reset_control_get_exclusive(dev, "aclk"); + if (IS_ERR(rockchip->aclk_rst)) { + if (PTR_ERR(rockchip->aclk_rst) != -EPROBE_DEFER) + dev_err(dev, "missing aclk reset property in node\n"); + return PTR_ERR(rockchip->aclk_rst); + } + + if (rockchip->is_rc) { + rockchip->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH); + if (IS_ERR(rockchip->ep_gpio)) { + dev_err(dev, "missing ep-gpios property in node\n"); + return PTR_ERR(rockchip->ep_gpio); + } + } + + rockchip->aclk_pcie = devm_clk_get(dev, "aclk"); + if (IS_ERR(rockchip->aclk_pcie)) { + dev_err(dev, "aclk clock not found\n"); + return PTR_ERR(rockchip->aclk_pcie); + } + + rockchip->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf"); + if (IS_ERR(rockchip->aclk_perf_pcie)) { + dev_err(dev, "aclk_perf clock not found\n"); + return PTR_ERR(rockchip->aclk_perf_pcie); + } + + rockchip->hclk_pcie = devm_clk_get(dev, "hclk"); + if (IS_ERR(rockchip->hclk_pcie)) { + dev_err(dev, "hclk clock not found\n"); + return PTR_ERR(rockchip->hclk_pcie); + } + + rockchip->clk_pcie_pm = devm_clk_get(dev, "pm"); + if (IS_ERR(rockchip->clk_pcie_pm)) { + dev_err(dev, "pm clock not found\n"); + return PTR_ERR(rockchip->clk_pcie_pm); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rockchip_pcie_parse_dt); + +int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + int err, i; + u32 regs; + + err = reset_control_assert(rockchip->aclk_rst); + if (err) { + dev_err(dev, "assert aclk_rst err %d\n", err); + return err; + } + + err = reset_control_assert(rockchip->pclk_rst); + if (err) { + dev_err(dev, "assert pclk_rst err %d\n", err); + return err; + } + + err = reset_control_assert(rockchip->pm_rst); + if (err) { + dev_err(dev, "assert pm_rst err %d\n", err); + return err; + } + + for (i = 0; i < MAX_LANE_NUM; i++) { + err = phy_init(rockchip->phys[i]); + if (err) { + dev_err(dev, "init phy%d err %d\n", i, err); + goto err_exit_phy; + } + } + + err = reset_control_assert(rockchip->core_rst); + if (err) { + dev_err(dev, "assert core_rst err %d\n", err); + goto err_exit_phy; + } + + err = reset_control_assert(rockchip->mgmt_rst); + if (err) { + dev_err(dev, "assert mgmt_rst err %d\n", err); + goto err_exit_phy; + } + + err = reset_control_assert(rockchip->mgmt_sticky_rst); + if (err) { + dev_err(dev, "assert mgmt_sticky_rst err %d\n", err); + goto err_exit_phy; + } + + err = reset_control_assert(rockchip->pipe_rst); + if (err) { + dev_err(dev, "assert pipe_rst err %d\n", err); + goto err_exit_phy; + } + + udelay(10); + + err = reset_control_deassert(rockchip->pm_rst); + if (err) { + dev_err(dev, "deassert pm_rst err %d\n", err); + goto err_exit_phy; + } + + err = reset_control_deassert(rockchip->aclk_rst); + if (err) { + dev_err(dev, "deassert aclk_rst err %d\n", err); + goto err_exit_phy; + } + + err = reset_control_deassert(rockchip->pclk_rst); + if (err) { + dev_err(dev, "deassert pclk_rst err %d\n", err); + goto err_exit_phy; + } + + if (rockchip->link_gen == 2) + rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_2, + PCIE_CLIENT_CONFIG); + else + rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1, + PCIE_CLIENT_CONFIG); + + regs = PCIE_CLIENT_LINK_TRAIN_ENABLE | PCIE_CLIENT_ARI_ENABLE | + PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes); + + if (rockchip->is_rc) + regs |= PCIE_CLIENT_CONF_ENABLE | PCIE_CLIENT_MODE_RC; + else + regs |= PCIE_CLIENT_CONF_DISABLE | PCIE_CLIENT_MODE_EP; + + rockchip_pcie_write(rockchip, regs, PCIE_CLIENT_CONFIG); + + for (i = 0; i < MAX_LANE_NUM; i++) { + err = phy_power_on(rockchip->phys[i]); + if (err) { + dev_err(dev, "power on phy%d err %d\n", i, err); + goto err_power_off_phy; + } + } + + /* + * Please don't reorder the deassert sequence of the following + * four reset pins. + */ + err = reset_control_deassert(rockchip->mgmt_sticky_rst); + if (err) { + dev_err(dev, "deassert mgmt_sticky_rst err %d\n", err); + goto err_power_off_phy; + } + + err = reset_control_deassert(rockchip->core_rst); + if (err) { + dev_err(dev, "deassert core_rst err %d\n", err); + goto err_power_off_phy; + } + + err = reset_control_deassert(rockchip->mgmt_rst); + if (err) { + dev_err(dev, "deassert mgmt_rst err %d\n", err); + goto err_power_off_phy; + } + + err = reset_control_deassert(rockchip->pipe_rst); + if (err) { + dev_err(dev, "deassert pipe_rst err %d\n", err); + goto err_power_off_phy; + } + + return 0; +err_power_off_phy: + while (i--) + phy_power_off(rockchip->phys[i]); + i = MAX_LANE_NUM; +err_exit_phy: + while (i--) + phy_exit(rockchip->phys[i]); + return err; +} +EXPORT_SYMBOL_GPL(rockchip_pcie_init_port); + +int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + struct phy *phy; + char *name; + u32 i; + + phy = devm_phy_get(dev, "pcie-phy"); + if (!IS_ERR(phy)) { + rockchip->legacy_phy = true; + rockchip->phys[0] = phy; + dev_warn(dev, "legacy phy model is deprecated!\n"); + return 0; + } + + if (PTR_ERR(phy) == -EPROBE_DEFER) + return PTR_ERR(phy); + + dev_dbg(dev, "missing legacy phy; search for per-lane PHY\n"); + + for (i = 0; i < MAX_LANE_NUM; i++) { + name = kasprintf(GFP_KERNEL, "pcie-phy-%u", i); + if (!name) + return -ENOMEM; + + phy = devm_of_phy_get(dev, dev->of_node, name); + kfree(name); + + if (IS_ERR(phy)) { + if (PTR_ERR(phy) != -EPROBE_DEFER) + dev_err(dev, "missing phy for lane %d: %ld\n", + i, PTR_ERR(phy)); + return PTR_ERR(phy); + } + + rockchip->phys[i] = phy; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rockchip_pcie_get_phys); + +void rockchip_pcie_deinit_phys(struct rockchip_pcie *rockchip) +{ + int i; + + for (i = 0; i < MAX_LANE_NUM; i++) { + /* inactive lanes are already powered off */ + if (rockchip->lanes_map & BIT(i)) + phy_power_off(rockchip->phys[i]); + phy_exit(rockchip->phys[i]); + } +} +EXPORT_SYMBOL_GPL(rockchip_pcie_deinit_phys); + +int rockchip_pcie_enable_clocks(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->dev; + int err; + + err = clk_prepare_enable(rockchip->aclk_pcie); + if (err) { + dev_err(dev, "unable to enable aclk_pcie clock\n"); + return err; + } + + err = clk_prepare_enable(rockchip->aclk_perf_pcie); + if (err) { + dev_err(dev, "unable to enable aclk_perf_pcie clock\n"); + goto err_aclk_perf_pcie; + } + + err = clk_prepare_enable(rockchip->hclk_pcie); + if (err) { + dev_err(dev, "unable to enable hclk_pcie clock\n"); + goto err_hclk_pcie; + } + + err = clk_prepare_enable(rockchip->clk_pcie_pm); + if (err) { + dev_err(dev, "unable to enable clk_pcie_pm clock\n"); + goto err_clk_pcie_pm; + } + + return 0; + +err_clk_pcie_pm: + clk_disable_unprepare(rockchip->hclk_pcie); +err_hclk_pcie: + clk_disable_unprepare(rockchip->aclk_perf_pcie); +err_aclk_perf_pcie: + clk_disable_unprepare(rockchip->aclk_pcie); + return err; +} +EXPORT_SYMBOL_GPL(rockchip_pcie_enable_clocks); + +void rockchip_pcie_disable_clocks(void *data) +{ + struct rockchip_pcie *rockchip = data; + + clk_disable_unprepare(rockchip->clk_pcie_pm); + clk_disable_unprepare(rockchip->hclk_pcie); + clk_disable_unprepare(rockchip->aclk_perf_pcie); + clk_disable_unprepare(rockchip->aclk_pcie); +} +EXPORT_SYMBOL_GPL(rockchip_pcie_disable_clocks); + +void rockchip_pcie_cfg_configuration_accesses( + struct rockchip_pcie *rockchip, u32 type) +{ + u32 ob_desc_0; + + /* Configuration Accesses for region 0 */ + rockchip_pcie_write(rockchip, 0x0, PCIE_RC_BAR_CONF); + + rockchip_pcie_write(rockchip, + (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS), + PCIE_CORE_OB_REGION_ADDR0); + rockchip_pcie_write(rockchip, RC_REGION_0_ADDR_TRANS_H, + PCIE_CORE_OB_REGION_ADDR1); + ob_desc_0 = rockchip_pcie_read(rockchip, PCIE_CORE_OB_REGION_DESC0); + ob_desc_0 &= ~(RC_REGION_0_TYPE_MASK); + ob_desc_0 |= (type | (0x1 << 23)); + rockchip_pcie_write(rockchip, ob_desc_0, PCIE_CORE_OB_REGION_DESC0); + rockchip_pcie_write(rockchip, 0x0, PCIE_CORE_OB_REGION_DESC1); +} +EXPORT_SYMBOL_GPL(rockchip_pcie_cfg_configuration_accesses); diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h new file mode 100644 index 000000000000..8e87a059ce73 --- /dev/null +++ b/drivers/pci/controller/pcie-rockchip.h @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Rockchip AXI PCIe controller driver + * + * Copyright (c) 2018 Rockchip, Inc. + * + * Author: Shawn Lin + * + */ + +#ifndef _PCIE_ROCKCHIP_H +#define _PCIE_ROCKCHIP_H + +#include +#include + +/* + * The upper 16 bits of PCIE_CLIENT_CONFIG are a write mask for the lower 16 + * bits. This allows atomic updates of the register without locking. + */ +#define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val)) +#define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val) + +#define ENCODE_LANES(x) ((((x) >> 1) & 3) << 4) +#define MAX_LANE_NUM 4 +#define MAX_REGION_LIMIT 32 +#define MIN_EP_APERTURE 28 + +#define PCIE_CLIENT_BASE 0x0 +#define PCIE_CLIENT_CONFIG (PCIE_CLIENT_BASE + 0x00) +#define PCIE_CLIENT_CONF_ENABLE HIWORD_UPDATE_BIT(0x0001) +#define PCIE_CLIENT_CONF_DISABLE HIWORD_UPDATE(0x0001, 0) +#define PCIE_CLIENT_LINK_TRAIN_ENABLE HIWORD_UPDATE_BIT(0x0002) +#define PCIE_CLIENT_ARI_ENABLE HIWORD_UPDATE_BIT(0x0008) +#define PCIE_CLIENT_CONF_LANE_NUM(x) HIWORD_UPDATE(0x0030, ENCODE_LANES(x)) +#define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040) +#define PCIE_CLIENT_MODE_EP HIWORD_UPDATE(0x0040, 0) +#define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0) +#define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080) +#define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c) +#define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0) +#define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18 +#define PCIE_CLIENT_DEBUG_LTSSM_L2 0x19 +#define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48) +#define PCIE_CLIENT_LINK_STATUS_UP 0x00300000 +#define PCIE_CLIENT_LINK_STATUS_MASK 0x00300000 +#define PCIE_CLIENT_INT_MASK (PCIE_CLIENT_BASE + 0x4c) +#define PCIE_CLIENT_INT_STATUS (PCIE_CLIENT_BASE + 0x50) +#define PCIE_CLIENT_INTR_MASK GENMASK(8, 5) +#define PCIE_CLIENT_INTR_SHIFT 5 +#define PCIE_CLIENT_INT_LEGACY_DONE BIT(15) +#define PCIE_CLIENT_INT_MSG BIT(14) +#define PCIE_CLIENT_INT_HOT_RST BIT(13) +#define PCIE_CLIENT_INT_DPA BIT(12) +#define PCIE_CLIENT_INT_FATAL_ERR BIT(11) +#define PCIE_CLIENT_INT_NFATAL_ERR BIT(10) +#define PCIE_CLIENT_INT_CORR_ERR BIT(9) +#define PCIE_CLIENT_INT_INTD BIT(8) +#define PCIE_CLIENT_INT_INTC BIT(7) +#define PCIE_CLIENT_INT_INTB BIT(6) +#define PCIE_CLIENT_INT_INTA BIT(5) +#define PCIE_CLIENT_INT_LOCAL BIT(4) +#define PCIE_CLIENT_INT_UDMA BIT(3) +#define PCIE_CLIENT_INT_PHY BIT(2) +#define PCIE_CLIENT_INT_HOT_PLUG BIT(1) +#define PCIE_CLIENT_INT_PWR_STCG BIT(0) + +#define PCIE_CLIENT_INT_LEGACY \ + (PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \ + PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD) + +#define PCIE_CLIENT_INT_CLI \ + (PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \ + PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \ + PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \ + PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY | \ + PCIE_CLIENT_INT_PHY) + +#define PCIE_CORE_CTRL_MGMT_BASE 0x900000 +#define PCIE_CORE_CTRL (PCIE_CORE_CTRL_MGMT_BASE + 0x000) +#define PCIE_CORE_PL_CONF_SPEED_5G 0x00000008 +#define PCIE_CORE_PL_CONF_SPEED_MASK 0x00000018 +#define PCIE_CORE_PL_CONF_LANE_MASK 0x00000006 +#define PCIE_CORE_PL_CONF_LANE_SHIFT 1 +#define PCIE_CORE_CTRL_PLC1 (PCIE_CORE_CTRL_MGMT_BASE + 0x004) +#define PCIE_CORE_CTRL_PLC1_FTS_MASK GENMASK(23, 8) +#define PCIE_CORE_CTRL_PLC1_FTS_SHIFT 8 +#define PCIE_CORE_CTRL_PLC1_FTS_CNT 0xffff +#define PCIE_CORE_TXCREDIT_CFG1 (PCIE_CORE_CTRL_MGMT_BASE + 0x020) +#define PCIE_CORE_TXCREDIT_CFG1_MUI_MASK 0xFFFF0000 +#define PCIE_CORE_TXCREDIT_CFG1_MUI_SHIFT 16 +#define PCIE_CORE_TXCREDIT_CFG1_MUI_ENCODE(x) \ + (((x) >> 3) << PCIE_CORE_TXCREDIT_CFG1_MUI_SHIFT) +#define PCIE_CORE_LANE_MAP (PCIE_CORE_CTRL_MGMT_BASE + 0x200) +#define PCIE_CORE_LANE_MAP_MASK 0x0000000f +#define PCIE_CORE_LANE_MAP_REVERSE BIT(16) +#define PCIE_CORE_INT_STATUS (PCIE_CORE_CTRL_MGMT_BASE + 0x20c) +#define PCIE_CORE_INT_PRFPE BIT(0) +#define PCIE_CORE_INT_CRFPE BIT(1) +#define PCIE_CORE_INT_RRPE BIT(2) +#define PCIE_CORE_INT_PRFO BIT(3) +#define PCIE_CORE_INT_CRFO BIT(4) +#define PCIE_CORE_INT_RT BIT(5) +#define PCIE_CORE_INT_RTR BIT(6) +#define PCIE_CORE_INT_PE BIT(7) +#define PCIE_CORE_INT_MTR BIT(8) +#define PCIE_CORE_INT_UCR BIT(9) +#define PCIE_CORE_INT_FCE BIT(10) +#define PCIE_CORE_INT_CT BIT(11) +#define PCIE_CORE_INT_UTC BIT(18) +#define PCIE_CORE_INT_MMVC BIT(19) +#define PCIE_CORE_CONFIG_VENDOR (PCIE_CORE_CTRL_MGMT_BASE + 0x44) +#define PCIE_CORE_INT_MASK (PCIE_CORE_CTRL_MGMT_BASE + 0x210) +#define PCIE_CORE_PHY_FUNC_CFG (PCIE_CORE_CTRL_MGMT_BASE + 0x2c0) +#define PCIE_RC_BAR_CONF (PCIE_CORE_CTRL_MGMT_BASE + 0x300) +#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_DISABLED 0x0 +#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_IO_32BITS 0x1 +#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_32BITS 0x4 +#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5 +#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_64BITS 0x6 +#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7 + +#define PCIE_CORE_INT \ + (PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \ + PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \ + PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \ + PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \ + PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \ + PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \ + PCIE_CORE_INT_MMVC) + +#define PCIE_RC_RP_ATS_BASE 0x400000 +#define PCIE_RC_CONFIG_NORMAL_BASE 0x800000 +#define PCIE_RC_CONFIG_BASE 0xa00000 +#define PCIE_RC_CONFIG_RID_CCR (PCIE_RC_CONFIG_BASE + 0x08) +#define PCIE_RC_CONFIG_SCC_SHIFT 16 +#define PCIE_RC_CONFIG_DCR (PCIE_RC_CONFIG_BASE + 0xc4) +#define PCIE_RC_CONFIG_DCR_CSPL_SHIFT 18 +#define PCIE_RC_CONFIG_DCR_CSPL_LIMIT 0xff +#define PCIE_RC_CONFIG_DCR_CPLS_SHIFT 26 +#define PCIE_RC_CONFIG_DCSR (PCIE_RC_CONFIG_BASE + 0xc8) +#define PCIE_RC_CONFIG_DCSR_MPS_MASK GENMASK(7, 5) +#define PCIE_RC_CONFIG_DCSR_MPS_256 (0x1 << 5) +#define PCIE_RC_CONFIG_LINK_CAP (PCIE_RC_CONFIG_BASE + 0xcc) +#define PCIE_RC_CONFIG_LINK_CAP_L0S BIT(10) +#define PCIE_RC_CONFIG_LCS (PCIE_RC_CONFIG_BASE + 0xd0) +#define PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2 (PCIE_RC_CONFIG_BASE + 0x90c) +#define PCIE_RC_CONFIG_THP_CAP (PCIE_RC_CONFIG_BASE + 0x274) +#define PCIE_RC_CONFIG_THP_CAP_NEXT_MASK GENMASK(31, 20) + +#define PCIE_CORE_AXI_CONF_BASE 0xc00000 +#define PCIE_CORE_OB_REGION_ADDR0 (PCIE_CORE_AXI_CONF_BASE + 0x0) +#define PCIE_CORE_OB_REGION_ADDR0_NUM_BITS 0x3f +#define PCIE_CORE_OB_REGION_ADDR0_LO_ADDR 0xffffff00 +#define PCIE_CORE_OB_REGION_ADDR1 (PCIE_CORE_AXI_CONF_BASE + 0x4) +#define PCIE_CORE_OB_REGION_DESC0 (PCIE_CORE_AXI_CONF_BASE + 0x8) +#define PCIE_CORE_OB_REGION_DESC1 (PCIE_CORE_AXI_CONF_BASE + 0xc) + +#define PCIE_CORE_AXI_INBOUND_BASE 0xc00800 +#define PCIE_RP_IB_ADDR0 (PCIE_CORE_AXI_INBOUND_BASE + 0x0) +#define PCIE_CORE_IB_REGION_ADDR0_NUM_BITS 0x3f +#define PCIE_CORE_IB_REGION_ADDR0_LO_ADDR 0xffffff00 +#define PCIE_RP_IB_ADDR1 (PCIE_CORE_AXI_INBOUND_BASE + 0x4) + +/* Size of one AXI Region (not Region 0) */ +#define AXI_REGION_SIZE BIT(20) +/* Size of Region 0, equal to sum of sizes of other regions */ +#define AXI_REGION_0_SIZE (32 * (0x1 << 20)) +#define OB_REG_SIZE_SHIFT 5 +#define IB_ROOT_PORT_REG_SIZE_SHIFT 3 +#define AXI_WRAPPER_IO_WRITE 0x6 +#define AXI_WRAPPER_MEM_WRITE 0x2 +#define AXI_WRAPPER_TYPE0_CFG 0xa +#define AXI_WRAPPER_TYPE1_CFG 0xb +#define AXI_WRAPPER_NOR_MSG 0xc + +#define MAX_AXI_IB_ROOTPORT_REGION_NUM 3 +#define MIN_AXI_ADDR_BITS_PASSED 8 +#define PCIE_RC_SEND_PME_OFF 0x11960 +#define ROCKCHIP_VENDOR_ID 0x1d87 +#define PCIE_ECAM_BUS(x) (((x) & 0xff) << 20) +#define PCIE_ECAM_DEV(x) (((x) & 0x1f) << 15) +#define PCIE_ECAM_FUNC(x) (((x) & 0x7) << 12) +#define PCIE_ECAM_REG(x) (((x) & 0xfff) << 0) +#define PCIE_ECAM_ADDR(bus, dev, func, reg) \ + (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \ + PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg)) +#define PCIE_LINK_IS_L2(x) \ + (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2) +#define PCIE_LINK_UP(x) \ + (((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP) +#define PCIE_LINK_IS_GEN2(x) \ + (((x) & PCIE_CORE_PL_CONF_SPEED_MASK) == PCIE_CORE_PL_CONF_SPEED_5G) + +#define RC_REGION_0_ADDR_TRANS_H 0x00000000 +#define RC_REGION_0_ADDR_TRANS_L 0x00000000 +#define RC_REGION_0_PASS_BITS (25 - 1) +#define RC_REGION_0_TYPE_MASK GENMASK(3, 0) +#define MAX_AXI_WRAPPER_REGION_NUM 33 + +#define ROCKCHIP_PCIE_MSG_ROUTING_TO_RC 0x0 +#define ROCKCHIP_PCIE_MSG_ROUTING_VIA_ADDR 0x1 +#define ROCKCHIP_PCIE_MSG_ROUTING_VIA_ID 0x2 +#define ROCKCHIP_PCIE_MSG_ROUTING_BROADCAST 0x3 +#define ROCKCHIP_PCIE_MSG_ROUTING_LOCAL_INTX 0x4 +#define ROCKCHIP_PCIE_MSG_ROUTING_PME_ACK 0x5 +#define ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTA 0x20 +#define ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTB 0x21 +#define ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTC 0x22 +#define ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTD 0x23 +#define ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTA 0x24 +#define ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTB 0x25 +#define ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTC 0x26 +#define ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTD 0x27 +#define ROCKCHIP_PCIE_MSG_ROUTING_MASK GENMASK(7, 5) +#define ROCKCHIP_PCIE_MSG_ROUTING(route) \ + (((route) << 5) & ROCKCHIP_PCIE_MSG_ROUTING_MASK) +#define ROCKCHIP_PCIE_MSG_CODE_MASK GENMASK(15, 8) +#define ROCKCHIP_PCIE_MSG_CODE(code) \ + (((code) << 8) & ROCKCHIP_PCIE_MSG_CODE_MASK) +#define ROCKCHIP_PCIE_MSG_NO_DATA BIT(16) + +#define ROCKCHIP_PCIE_EP_CMD_STATUS 0x4 +#define ROCKCHIP_PCIE_EP_CMD_STATUS_IS BIT(19) +#define ROCKCHIP_PCIE_EP_MSI_CTRL_REG 0x90 +#define ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET 17 +#define ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_MASK GENMASK(19, 17) +#define ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET 20 +#define ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK GENMASK(22, 20) +#define ROCKCHIP_PCIE_EP_MSI_CTRL_ME BIT(16) +#define ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP BIT(24) +#define ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR 0x1 +#define ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR 0x3 +#define ROCKCHIP_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12)) +#define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ + (PCIE_RC_RP_ATS_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008) +#define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \ + (PCIE_RC_RP_ATS_BASE + 0x0844 + (fn) * 0x0040 + (bar) * 0x0008) +#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r) \ + (PCIE_RC_RP_ATS_BASE + 0x0000 + ((r) & 0x1f) * 0x0020) +#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(19, 12) +#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \ + (((devfn) << 12) & \ + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK) +#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(27, 20) +#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(bus) \ + (((bus) << 20) & ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK) +#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r) \ + (PCIE_RC_RP_ATS_BASE + 0x0004 + ((r) & 0x1f) * 0x0020) +#define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID BIT(23) +#define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK GENMASK(31, 24) +#define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN(devfn) \ + (((devfn) << 24) & ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK) +#define ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r) \ + (PCIE_RC_RP_ATS_BASE + 0x0008 + ((r) & 0x1f) * 0x0020) +#define ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r) \ + (PCIE_RC_RP_ATS_BASE + 0x000c + ((r) & 0x1f) * 0x0020) +#define ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(r) \ + (PCIE_RC_RP_ATS_BASE + 0x0018 + ((r) & 0x1f) * 0x0020) +#define ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(r) \ + (PCIE_RC_RP_ATS_BASE + 0x001c + ((r) & 0x1f) * 0x0020) + +#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0(fn) \ + (PCIE_CORE_CTRL_MGMT_BASE + 0x0240 + (fn) * 0x0008) +#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG1(fn) \ + (PCIE_CORE_CTRL_MGMT_BASE + 0x0244 + (fn) * 0x0008) +#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \ + (GENMASK(4, 0) << ((b) * 8)) +#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \ + (((a) << ((b) * 8)) & \ + ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b)) +#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b) \ + (GENMASK(7, 5) << ((b) * 8)) +#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL(b, c) \ + (((c) << ((b) * 8 + 5)) & \ + ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)) + +struct rockchip_pcie { + void __iomem *reg_base; /* DT axi-base */ + void __iomem *apb_base; /* DT apb-base */ + bool legacy_phy; + struct phy *phys[MAX_LANE_NUM]; + struct reset_control *core_rst; + struct reset_control *mgmt_rst; + struct reset_control *mgmt_sticky_rst; + struct reset_control *pipe_rst; + struct reset_control *pm_rst; + struct reset_control *aclk_rst; + struct reset_control *pclk_rst; + struct clk *aclk_pcie; + struct clk *aclk_perf_pcie; + struct clk *hclk_pcie; + struct clk *clk_pcie_pm; + struct regulator *vpcie12v; /* 12V power supply */ + struct regulator *vpcie3v3; /* 3.3V power supply */ + struct regulator *vpcie1v8; /* 1.8V power supply */ + struct regulator *vpcie0v9; /* 0.9V power supply */ + struct gpio_desc *ep_gpio; + u32 lanes; + u8 lanes_map; + u8 root_bus_nr; + int link_gen; + struct device *dev; + struct irq_domain *irq_domain; + int offset; + struct pci_bus *root_bus; + struct resource *io; + phys_addr_t io_bus_addr; + u32 io_size; + void __iomem *msg_region; + u32 mem_size; + phys_addr_t msg_bus_addr; + phys_addr_t mem_bus_addr; + bool is_rc; + struct resource *mem_res; +}; + +static u32 rockchip_pcie_read(struct rockchip_pcie *rockchip, u32 reg) +{ + return readl(rockchip->apb_base + reg); +} + +static void rockchip_pcie_write(struct rockchip_pcie *rockchip, u32 val, + u32 reg) +{ + writel(val, rockchip->apb_base + reg); +} + +int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip); +int rockchip_pcie_init_port(struct rockchip_pcie *rockchip); +int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip); +void rockchip_pcie_deinit_phys(struct rockchip_pcie *rockchip); +int rockchip_pcie_enable_clocks(struct rockchip_pcie *rockchip); +void rockchip_pcie_disable_clocks(void *data); +void rockchip_pcie_cfg_configuration_accesses( + struct rockchip_pcie *rockchip, u32 type); + +#endif /* _PCIE_ROCKCHIP_H */ diff --git a/drivers/pci/controller/pcie-tango.c b/drivers/pci/controller/pcie-tango.c new file mode 100644 index 000000000000..21a208da3f59 --- /dev/null +++ b/drivers/pci/controller/pcie-tango.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + +#define MSI_MAX 256 + +#define SMP8759_MUX 0x48 +#define SMP8759_TEST_OUT 0x74 +#define SMP8759_DOORBELL 0x7c +#define SMP8759_STATUS 0x80 +#define SMP8759_ENABLE 0xa0 + +struct tango_pcie { + DECLARE_BITMAP(used_msi, MSI_MAX); + u64 msi_doorbell; + spinlock_t used_msi_lock; + void __iomem *base; + struct irq_domain *dom; +}; + +static void tango_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct tango_pcie *pcie = irq_desc_get_handler_data(desc); + unsigned long status, base, virq, idx, pos = 0; + + chained_irq_enter(chip, desc); + spin_lock(&pcie->used_msi_lock); + + while ((pos = find_next_bit(pcie->used_msi, MSI_MAX, pos)) < MSI_MAX) { + base = round_down(pos, 32); + status = readl_relaxed(pcie->base + SMP8759_STATUS + base / 8); + for_each_set_bit(idx, &status, 32) { + virq = irq_find_mapping(pcie->dom, base + idx); + generic_handle_irq(virq); + } + pos = base + 32; + } + + spin_unlock(&pcie->used_msi_lock); + chained_irq_exit(chip, desc); +} + +static void tango_ack(struct irq_data *d) +{ + struct tango_pcie *pcie = d->chip_data; + u32 offset = (d->hwirq / 32) * 4; + u32 bit = BIT(d->hwirq % 32); + + writel_relaxed(bit, pcie->base + SMP8759_STATUS + offset); +} + +static void update_msi_enable(struct irq_data *d, bool unmask) +{ + unsigned long flags; + struct tango_pcie *pcie = d->chip_data; + u32 offset = (d->hwirq / 32) * 4; + u32 bit = BIT(d->hwirq % 32); + u32 val; + + spin_lock_irqsave(&pcie->used_msi_lock, flags); + val = readl_relaxed(pcie->base + SMP8759_ENABLE + offset); + val = unmask ? val | bit : val & ~bit; + writel_relaxed(val, pcie->base + SMP8759_ENABLE + offset); + spin_unlock_irqrestore(&pcie->used_msi_lock, flags); +} + +static void tango_mask(struct irq_data *d) +{ + update_msi_enable(d, false); +} + +static void tango_unmask(struct irq_data *d) +{ + update_msi_enable(d, true); +} + +static int tango_set_affinity(struct irq_data *d, const struct cpumask *mask, + bool force) +{ + return -EINVAL; +} + +static void tango_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct tango_pcie *pcie = d->chip_data; + msg->address_lo = lower_32_bits(pcie->msi_doorbell); + msg->address_hi = upper_32_bits(pcie->msi_doorbell); + msg->data = d->hwirq; +} + +static struct irq_chip tango_chip = { + .irq_ack = tango_ack, + .irq_mask = tango_mask, + .irq_unmask = tango_unmask, + .irq_set_affinity = tango_set_affinity, + .irq_compose_msi_msg = tango_compose_msi_msg, +}; + +static void msi_ack(struct irq_data *d) +{ + irq_chip_ack_parent(d); +} + +static void msi_mask(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void msi_unmask(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip msi_chip = { + .name = "MSI", + .irq_ack = msi_ack, + .irq_mask = msi_mask, + .irq_unmask = msi_unmask, +}; + +static struct msi_domain_info msi_dom_info = { + .flags = MSI_FLAG_PCI_MSIX + | MSI_FLAG_USE_DEF_DOM_OPS + | MSI_FLAG_USE_DEF_CHIP_OPS, + .chip = &msi_chip, +}; + +static int tango_irq_domain_alloc(struct irq_domain *dom, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct tango_pcie *pcie = dom->host_data; + unsigned long flags; + int pos; + + spin_lock_irqsave(&pcie->used_msi_lock, flags); + pos = find_first_zero_bit(pcie->used_msi, MSI_MAX); + if (pos >= MSI_MAX) { + spin_unlock_irqrestore(&pcie->used_msi_lock, flags); + return -ENOSPC; + } + __set_bit(pos, pcie->used_msi); + spin_unlock_irqrestore(&pcie->used_msi_lock, flags); + irq_domain_set_info(dom, virq, pos, &tango_chip, + pcie, handle_edge_irq, NULL, NULL); + + return 0; +} + +static void tango_irq_domain_free(struct irq_domain *dom, unsigned int virq, + unsigned int nr_irqs) +{ + unsigned long flags; + struct irq_data *d = irq_domain_get_irq_data(dom, virq); + struct tango_pcie *pcie = d->chip_data; + + spin_lock_irqsave(&pcie->used_msi_lock, flags); + __clear_bit(d->hwirq, pcie->used_msi); + spin_unlock_irqrestore(&pcie->used_msi_lock, flags); +} + +static const struct irq_domain_ops dom_ops = { + .alloc = tango_irq_domain_alloc, + .free = tango_irq_domain_free, +}; + +static int smp8759_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct pci_config_window *cfg = bus->sysdata; + struct tango_pcie *pcie = dev_get_drvdata(cfg->parent); + int ret; + + /* Reads in configuration space outside devfn 0 return garbage */ + if (devfn != 0) + return PCIBIOS_FUNC_NOT_SUPPORTED; + + /* + * PCI config and MMIO accesses are muxed. Linux doesn't have a + * mutual exclusion mechanism for config vs. MMIO accesses, so + * concurrent accesses may cause corruption. + */ + writel_relaxed(1, pcie->base + SMP8759_MUX); + ret = pci_generic_config_read(bus, devfn, where, size, val); + writel_relaxed(0, pcie->base + SMP8759_MUX); + + return ret; +} + +static int smp8759_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct pci_config_window *cfg = bus->sysdata; + struct tango_pcie *pcie = dev_get_drvdata(cfg->parent); + int ret; + + writel_relaxed(1, pcie->base + SMP8759_MUX); + ret = pci_generic_config_write(bus, devfn, where, size, val); + writel_relaxed(0, pcie->base + SMP8759_MUX); + + return ret; +} + +static struct pci_ecam_ops smp8759_ecam_ops = { + .bus_shift = 20, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = smp8759_config_read, + .write = smp8759_config_write, + } +}; + +static int tango_pcie_link_up(struct tango_pcie *pcie) +{ + void __iomem *test_out = pcie->base + SMP8759_TEST_OUT; + int i; + + writel_relaxed(16, test_out); + for (i = 0; i < 10; ++i) { + u32 ltssm_state = readl_relaxed(test_out) >> 8; + if ((ltssm_state & 0x1f) == 0xf) /* L0 */ + return 1; + usleep_range(3000, 4000); + } + + return 0; +} + +static int tango_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tango_pcie *pcie; + struct resource *res; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); + struct irq_domain *msi_dom, *irq_dom; + struct of_pci_range_parser parser; + struct of_pci_range range; + int virq, offset; + + dev_warn(dev, "simultaneous PCI config and MMIO accesses may cause data corruption\n"); + add_taint(TAINT_CRAP, LOCKDEP_STILL_OK); + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pcie->base = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->base)) + return PTR_ERR(pcie->base); + + platform_set_drvdata(pdev, pcie); + + if (!tango_pcie_link_up(pcie)) + return -ENODEV; + + if (of_pci_dma_range_parser_init(&parser, dev->of_node) < 0) + return -ENOENT; + + if (of_pci_range_parser_one(&parser, &range) == NULL) + return -ENOENT; + + range.pci_addr += range.size; + pcie->msi_doorbell = range.pci_addr + res->start + SMP8759_DOORBELL; + + for (offset = 0; offset < MSI_MAX / 8; offset += 4) + writel_relaxed(0, pcie->base + SMP8759_ENABLE + offset); + + virq = platform_get_irq(pdev, 1); + if (virq <= 0) { + dev_err(dev, "Failed to map IRQ\n"); + return -ENXIO; + } + + irq_dom = irq_domain_create_linear(fwnode, MSI_MAX, &dom_ops, pcie); + if (!irq_dom) { + dev_err(dev, "Failed to create IRQ domain\n"); + return -ENOMEM; + } + + msi_dom = pci_msi_create_irq_domain(fwnode, &msi_dom_info, irq_dom); + if (!msi_dom) { + dev_err(dev, "Failed to create MSI domain\n"); + irq_domain_remove(irq_dom); + return -ENOMEM; + } + + pcie->dom = irq_dom; + 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); +} + +static const struct of_device_id tango_pcie_ids[] = { + { .compatible = "sigma,smp8759-pcie" }, + { }, +}; + +static struct platform_driver tango_pcie_driver = { + .probe = tango_pcie_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = tango_pcie_ids, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(tango_pcie_driver); + +/* + * The root complex advertises the wrong device class. + * Header Type 1 is for PCI-to-PCI bridges. + */ +static void tango_fixup_class(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_class); + +/* + * The root complex exposes a "fake" BAR, which is used to filter + * bus-to-system accesses. Only accesses within the range defined by this + * BAR are forwarded to the host, others are ignored. + * + * By default, the DMA framework expects an identity mapping, and DRAM0 is + * mapped at 0x80000000. + */ +static void tango_fixup_bar(struct pci_dev *dev) +{ + dev->non_compliant_bars = true; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0x80000000); +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_bar); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_bar); diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c new file mode 100644 index 000000000000..6a4bbb5b3de0 --- /dev/null +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -0,0 +1,917 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for NWL PCIe Bridge + * Based on pcie-xilinx.c, pci-tegra.c + * + * (C) Copyright 2014 - 2015, Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* Bridge core config registers */ +#define BRCFG_PCIE_RX0 0x00000000 +#define BRCFG_INTERRUPT 0x00000010 +#define BRCFG_PCIE_RX_MSG_FILTER 0x00000020 + +/* Egress - Bridge translation registers */ +#define E_BREG_CAPABILITIES 0x00000200 +#define E_BREG_CONTROL 0x00000208 +#define E_BREG_BASE_LO 0x00000210 +#define E_BREG_BASE_HI 0x00000214 +#define E_ECAM_CAPABILITIES 0x00000220 +#define E_ECAM_CONTROL 0x00000228 +#define E_ECAM_BASE_LO 0x00000230 +#define E_ECAM_BASE_HI 0x00000234 + +/* Ingress - address translations */ +#define I_MSII_CAPABILITIES 0x00000300 +#define I_MSII_CONTROL 0x00000308 +#define I_MSII_BASE_LO 0x00000310 +#define I_MSII_BASE_HI 0x00000314 + +#define I_ISUB_CONTROL 0x000003E8 +#define SET_ISUB_CONTROL BIT(0) +/* Rxed msg fifo - Interrupt status registers */ +#define MSGF_MISC_STATUS 0x00000400 +#define MSGF_MISC_MASK 0x00000404 +#define MSGF_LEG_STATUS 0x00000420 +#define MSGF_LEG_MASK 0x00000424 +#define MSGF_MSI_STATUS_LO 0x00000440 +#define MSGF_MSI_STATUS_HI 0x00000444 +#define MSGF_MSI_MASK_LO 0x00000448 +#define MSGF_MSI_MASK_HI 0x0000044C + +/* Msg filter mask bits */ +#define CFG_ENABLE_PM_MSG_FWD BIT(1) +#define CFG_ENABLE_INT_MSG_FWD BIT(2) +#define CFG_ENABLE_ERR_MSG_FWD BIT(3) +#define CFG_ENABLE_MSG_FILTER_MASK (CFG_ENABLE_PM_MSG_FWD | \ + CFG_ENABLE_INT_MSG_FWD | \ + CFG_ENABLE_ERR_MSG_FWD) + +/* Misc interrupt status mask bits */ +#define MSGF_MISC_SR_RXMSG_AVAIL BIT(0) +#define MSGF_MISC_SR_RXMSG_OVER BIT(1) +#define MSGF_MISC_SR_SLAVE_ERR BIT(4) +#define MSGF_MISC_SR_MASTER_ERR BIT(5) +#define MSGF_MISC_SR_I_ADDR_ERR BIT(6) +#define MSGF_MISC_SR_E_ADDR_ERR BIT(7) +#define MSGF_MISC_SR_FATAL_AER BIT(16) +#define MSGF_MISC_SR_NON_FATAL_AER BIT(17) +#define MSGF_MISC_SR_CORR_AER BIT(18) +#define MSGF_MISC_SR_UR_DETECT BIT(20) +#define MSGF_MISC_SR_NON_FATAL_DEV BIT(22) +#define MSGF_MISC_SR_FATAL_DEV BIT(23) +#define MSGF_MISC_SR_LINK_DOWN BIT(24) +#define MSGF_MSIC_SR_LINK_AUTO_BWIDTH BIT(25) +#define MSGF_MSIC_SR_LINK_BWIDTH BIT(26) + +#define MSGF_MISC_SR_MASKALL (MSGF_MISC_SR_RXMSG_AVAIL | \ + MSGF_MISC_SR_RXMSG_OVER | \ + MSGF_MISC_SR_SLAVE_ERR | \ + MSGF_MISC_SR_MASTER_ERR | \ + MSGF_MISC_SR_I_ADDR_ERR | \ + MSGF_MISC_SR_E_ADDR_ERR | \ + MSGF_MISC_SR_FATAL_AER | \ + MSGF_MISC_SR_NON_FATAL_AER | \ + MSGF_MISC_SR_CORR_AER | \ + MSGF_MISC_SR_UR_DETECT | \ + MSGF_MISC_SR_NON_FATAL_DEV | \ + MSGF_MISC_SR_FATAL_DEV | \ + MSGF_MISC_SR_LINK_DOWN | \ + MSGF_MSIC_SR_LINK_AUTO_BWIDTH | \ + MSGF_MSIC_SR_LINK_BWIDTH) + +/* Legacy interrupt status mask bits */ +#define MSGF_LEG_SR_INTA BIT(0) +#define MSGF_LEG_SR_INTB BIT(1) +#define MSGF_LEG_SR_INTC BIT(2) +#define MSGF_LEG_SR_INTD BIT(3) +#define MSGF_LEG_SR_MASKALL (MSGF_LEG_SR_INTA | MSGF_LEG_SR_INTB | \ + MSGF_LEG_SR_INTC | MSGF_LEG_SR_INTD) + +/* MSI interrupt status mask bits */ +#define MSGF_MSI_SR_LO_MASK GENMASK(31, 0) +#define MSGF_MSI_SR_HI_MASK GENMASK(31, 0) + +#define MSII_PRESENT BIT(0) +#define MSII_ENABLE BIT(0) +#define MSII_STATUS_ENABLE BIT(15) + +/* Bridge config interrupt mask */ +#define BRCFG_INTERRUPT_MASK BIT(0) +#define BREG_PRESENT BIT(0) +#define BREG_ENABLE BIT(0) +#define BREG_ENABLE_FORCE BIT(1) + +/* E_ECAM status mask bits */ +#define E_ECAM_PRESENT BIT(0) +#define E_ECAM_CR_ENABLE BIT(0) +#define E_ECAM_SIZE_LOC GENMASK(20, 16) +#define E_ECAM_SIZE_SHIFT 16 +#define ECAM_BUS_LOC_SHIFT 20 +#define ECAM_DEV_LOC_SHIFT 12 +#define NWL_ECAM_VALUE_DEFAULT 12 + +#define CFG_DMA_REG_BAR GENMASK(2, 0) + +#define INT_PCI_MSI_NR (2 * 32) + +/* Readin the PS_LINKUP */ +#define PS_LINKUP_OFFSET 0x00000238 +#define PCIE_PHY_LINKUP_BIT BIT(0) +#define PHY_RDY_LINKUP_BIT BIT(1) + +/* Parameters for the waiting for link up routine */ +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP_MIN 90000 +#define LINK_WAIT_USLEEP_MAX 100000 + +struct nwl_msi { /* MSI information */ + struct irq_domain *msi_domain; + unsigned long *bitmap; + struct irq_domain *dev_domain; + struct mutex lock; /* protect bitmap variable */ + int irq_msi0; + int irq_msi1; +}; + +struct nwl_pcie { + struct device *dev; + void __iomem *breg_base; + void __iomem *pcireg_base; + void __iomem *ecam_base; + phys_addr_t phys_breg_base; /* Physical Bridge Register Base */ + phys_addr_t phys_pcie_reg_base; /* Physical PCIe Controller Base */ + phys_addr_t phys_ecam_base; /* Physical Configuration Base */ + u32 breg_size; + u32 pcie_reg_size; + u32 ecam_size; + int irq_intx; + int irq_misc; + u32 ecam_value; + u8 last_busno; + u8 root_busno; + struct nwl_msi msi; + struct irq_domain *legacy_irq_domain; + raw_spinlock_t leg_mask_lock; +}; + +static inline u32 nwl_bridge_readl(struct nwl_pcie *pcie, u32 off) +{ + return readl(pcie->breg_base + off); +} + +static inline void nwl_bridge_writel(struct nwl_pcie *pcie, u32 val, u32 off) +{ + writel(val, pcie->breg_base + off); +} + +static bool nwl_pcie_link_up(struct nwl_pcie *pcie) +{ + if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PCIE_PHY_LINKUP_BIT) + return true; + return false; +} + +static bool nwl_phy_link_up(struct nwl_pcie *pcie) +{ + if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PHY_RDY_LINKUP_BIT) + return true; + return false; +} + +static int nwl_wait_for_link(struct nwl_pcie *pcie) +{ + struct device *dev = pcie->dev; + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (nwl_phy_link_up(pcie)) + return 0; + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + dev_err(dev, "PHY link never came up\n"); + return -ETIMEDOUT; +} + +static bool nwl_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) +{ + struct nwl_pcie *pcie = bus->sysdata; + + /* Check link before accessing downstream ports */ + if (bus->number != pcie->root_busno) { + if (!nwl_pcie_link_up(pcie)) + return false; + } + + /* Only one device down on each root port */ + if (bus->number == pcie->root_busno && devfn > 0) + return false; + + return true; +} + +/** + * nwl_pcie_map_bus - Get configuration base + * + * @bus: Bus structure of current bus + * @devfn: Device/function + * @where: Offset from base + * + * Return: Base address of the configuration space needed to be + * accessed. + */ +static void __iomem *nwl_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct nwl_pcie *pcie = bus->sysdata; + int relbus; + + if (!nwl_pcie_valid_device(bus, devfn)) + return NULL; + + relbus = (bus->number << ECAM_BUS_LOC_SHIFT) | + (devfn << ECAM_DEV_LOC_SHIFT); + + return pcie->ecam_base + relbus + where; +} + +/* PCIe operations */ +static struct pci_ops nwl_pcie_ops = { + .map_bus = nwl_pcie_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static irqreturn_t nwl_pcie_misc_handler(int irq, void *data) +{ + struct nwl_pcie *pcie = data; + struct device *dev = pcie->dev; + u32 misc_stat; + + /* Checking for misc interrupts */ + misc_stat = nwl_bridge_readl(pcie, MSGF_MISC_STATUS) & + MSGF_MISC_SR_MASKALL; + if (!misc_stat) + return IRQ_NONE; + + if (misc_stat & MSGF_MISC_SR_RXMSG_OVER) + dev_err(dev, "Received Message FIFO Overflow\n"); + + if (misc_stat & MSGF_MISC_SR_SLAVE_ERR) + dev_err(dev, "Slave error\n"); + + if (misc_stat & MSGF_MISC_SR_MASTER_ERR) + dev_err(dev, "Master error\n"); + + if (misc_stat & MSGF_MISC_SR_I_ADDR_ERR) + dev_err(dev, "In Misc Ingress address translation error\n"); + + if (misc_stat & MSGF_MISC_SR_E_ADDR_ERR) + dev_err(dev, "In Misc Egress address translation error\n"); + + if (misc_stat & MSGF_MISC_SR_FATAL_AER) + dev_err(dev, "Fatal Error in AER Capability\n"); + + if (misc_stat & MSGF_MISC_SR_NON_FATAL_AER) + dev_err(dev, "Non-Fatal Error in AER Capability\n"); + + if (misc_stat & MSGF_MISC_SR_CORR_AER) + dev_err(dev, "Correctable Error in AER Capability\n"); + + if (misc_stat & MSGF_MISC_SR_UR_DETECT) + dev_err(dev, "Unsupported request Detected\n"); + + if (misc_stat & MSGF_MISC_SR_NON_FATAL_DEV) + dev_err(dev, "Non-Fatal Error Detected\n"); + + if (misc_stat & MSGF_MISC_SR_FATAL_DEV) + dev_err(dev, "Fatal Error Detected\n"); + + if (misc_stat & MSGF_MSIC_SR_LINK_AUTO_BWIDTH) + dev_info(dev, "Link Autonomous Bandwidth Management Status bit set\n"); + + if (misc_stat & MSGF_MSIC_SR_LINK_BWIDTH) + dev_info(dev, "Link Bandwidth Management Status bit set\n"); + + /* Clear misc interrupt status */ + nwl_bridge_writel(pcie, misc_stat, MSGF_MISC_STATUS); + + return IRQ_HANDLED; +} + +static void nwl_pcie_leg_handler(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct nwl_pcie *pcie; + unsigned long status; + u32 bit; + u32 virq; + + chained_irq_enter(chip, desc); + pcie = irq_desc_get_handler_data(desc); + + while ((status = nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & + MSGF_LEG_SR_MASKALL) != 0) { + for_each_set_bit(bit, &status, PCI_NUM_INTX) { + virq = irq_find_mapping(pcie->legacy_irq_domain, bit); + if (virq) + generic_handle_irq(virq); + } + } + + chained_irq_exit(chip, desc); +} + +static void nwl_pcie_handle_msi_irq(struct nwl_pcie *pcie, u32 status_reg) +{ + struct nwl_msi *msi; + unsigned long status; + u32 bit; + u32 virq; + + msi = &pcie->msi; + + while ((status = nwl_bridge_readl(pcie, status_reg)) != 0) { + for_each_set_bit(bit, &status, 32) { + nwl_bridge_writel(pcie, 1 << bit, status_reg); + virq = irq_find_mapping(msi->dev_domain, bit); + if (virq) + generic_handle_irq(virq); + } + } +} + +static void nwl_pcie_msi_handler_high(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct nwl_pcie *pcie = irq_desc_get_handler_data(desc); + + chained_irq_enter(chip, desc); + nwl_pcie_handle_msi_irq(pcie, MSGF_MSI_STATUS_HI); + chained_irq_exit(chip, desc); +} + +static void nwl_pcie_msi_handler_low(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct nwl_pcie *pcie = irq_desc_get_handler_data(desc); + + chained_irq_enter(chip, desc); + nwl_pcie_handle_msi_irq(pcie, MSGF_MSI_STATUS_LO); + chained_irq_exit(chip, desc); +} + +static void nwl_mask_leg_irq(struct irq_data *data) +{ + struct irq_desc *desc = irq_to_desc(data->irq); + struct nwl_pcie *pcie; + unsigned long flags; + u32 mask; + u32 val; + + pcie = irq_desc_get_chip_data(desc); + mask = 1 << (data->hwirq - 1); + raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags); + val = nwl_bridge_readl(pcie, MSGF_LEG_MASK); + nwl_bridge_writel(pcie, (val & (~mask)), MSGF_LEG_MASK); + raw_spin_unlock_irqrestore(&pcie->leg_mask_lock, flags); +} + +static void nwl_unmask_leg_irq(struct irq_data *data) +{ + struct irq_desc *desc = irq_to_desc(data->irq); + struct nwl_pcie *pcie; + unsigned long flags; + u32 mask; + u32 val; + + pcie = irq_desc_get_chip_data(desc); + mask = 1 << (data->hwirq - 1); + raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags); + val = nwl_bridge_readl(pcie, MSGF_LEG_MASK); + nwl_bridge_writel(pcie, (val | mask), MSGF_LEG_MASK); + raw_spin_unlock_irqrestore(&pcie->leg_mask_lock, flags); +} + +static struct irq_chip nwl_leg_irq_chip = { + .name = "nwl_pcie:legacy", + .irq_enable = nwl_unmask_leg_irq, + .irq_disable = nwl_mask_leg_irq, + .irq_mask = nwl_mask_leg_irq, + .irq_unmask = nwl_unmask_leg_irq, +}; + +static int nwl_legacy_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &nwl_leg_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + irq_set_status_flags(irq, IRQ_LEVEL); + + return 0; +} + +static const struct irq_domain_ops legacy_domain_ops = { + .map = nwl_legacy_map, + .xlate = pci_irqd_intx_xlate, +}; + +#ifdef CONFIG_PCI_MSI +static struct irq_chip nwl_msi_irq_chip = { + .name = "nwl_pcie:msi", + .irq_enable = unmask_msi_irq, + .irq_disable = mask_msi_irq, + .irq_mask = mask_msi_irq, + .irq_unmask = unmask_msi_irq, + +}; + +static struct msi_domain_info nwl_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_MULTI_PCI_MSI), + .chip = &nwl_msi_irq_chip, +}; +#endif + +static void nwl_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); + phys_addr_t msi_addr = pcie->phys_pcie_reg_base; + + msg->address_lo = lower_32_bits(msi_addr); + msg->address_hi = upper_32_bits(msi_addr); + msg->data = data->hwirq; +} + +static int nwl_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static struct irq_chip nwl_irq_chip = { + .name = "Xilinx MSI", + .irq_compose_msi_msg = nwl_compose_msi_msg, + .irq_set_affinity = nwl_msi_set_affinity, +}; + +static int nwl_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct nwl_pcie *pcie = domain->host_data; + struct nwl_msi *msi = &pcie->msi; + int bit; + int i; + + mutex_lock(&msi->lock); + bit = bitmap_find_next_zero_area(msi->bitmap, INT_PCI_MSI_NR, 0, + nr_irqs, 0); + if (bit >= INT_PCI_MSI_NR) { + mutex_unlock(&msi->lock); + return -ENOSPC; + } + + bitmap_set(msi->bitmap, bit, nr_irqs); + + for (i = 0; i < nr_irqs; i++) { + irq_domain_set_info(domain, virq + i, bit + i, &nwl_irq_chip, + domain->host_data, handle_simple_irq, + NULL, NULL); + } + mutex_unlock(&msi->lock); + return 0; +} + +static void nwl_irq_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); + struct nwl_msi *msi = &pcie->msi; + + mutex_lock(&msi->lock); + bitmap_clear(msi->bitmap, data->hwirq, nr_irqs); + mutex_unlock(&msi->lock); +} + +static const struct irq_domain_ops dev_msi_domain_ops = { + .alloc = nwl_irq_domain_alloc, + .free = nwl_irq_domain_free, +}; + +static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie) +{ +#ifdef CONFIG_PCI_MSI + struct device *dev = pcie->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); + struct nwl_msi *msi = &pcie->msi; + + msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR, + &dev_msi_domain_ops, pcie); + if (!msi->dev_domain) { + dev_err(dev, "failed to create dev IRQ domain\n"); + return -ENOMEM; + } + msi->msi_domain = pci_msi_create_irq_domain(fwnode, + &nwl_msi_domain_info, + msi->dev_domain); + if (!msi->msi_domain) { + dev_err(dev, "failed to create msi IRQ domain\n"); + irq_domain_remove(msi->dev_domain); + return -ENOMEM; + } +#endif + return 0; +} + +static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node; + struct device_node *legacy_intc_node; + + legacy_intc_node = of_get_next_child(node, NULL); + if (!legacy_intc_node) { + dev_err(dev, "No legacy intc node found\n"); + return -EINVAL; + } + + pcie->legacy_irq_domain = irq_domain_add_linear(legacy_intc_node, + PCI_NUM_INTX, + &legacy_domain_ops, + pcie); + + if (!pcie->legacy_irq_domain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + raw_spin_lock_init(&pcie->leg_mask_lock); + nwl_pcie_init_msi_irq_domain(pcie); + return 0; +} + +static int nwl_pcie_enable_msi(struct nwl_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); + struct nwl_msi *msi = &pcie->msi; + unsigned long base; + int ret; + int size = BITS_TO_LONGS(INT_PCI_MSI_NR) * sizeof(long); + + mutex_init(&msi->lock); + + msi->bitmap = kzalloc(size, GFP_KERNEL); + if (!msi->bitmap) + return -ENOMEM; + + /* Get msi_1 IRQ number */ + msi->irq_msi1 = platform_get_irq_byname(pdev, "msi1"); + if (msi->irq_msi1 < 0) { + dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi1); + ret = -EINVAL; + goto err; + } + + irq_set_chained_handler_and_data(msi->irq_msi1, + nwl_pcie_msi_handler_high, pcie); + + /* Get msi_0 IRQ number */ + msi->irq_msi0 = platform_get_irq_byname(pdev, "msi0"); + if (msi->irq_msi0 < 0) { + dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi0); + ret = -EINVAL; + goto err; + } + + irq_set_chained_handler_and_data(msi->irq_msi0, + nwl_pcie_msi_handler_low, pcie); + + /* Check for msii_present bit */ + ret = nwl_bridge_readl(pcie, I_MSII_CAPABILITIES) & MSII_PRESENT; + if (!ret) { + dev_err(dev, "MSI not present\n"); + ret = -EIO; + goto err; + } + + /* Enable MSII */ + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, I_MSII_CONTROL) | + MSII_ENABLE, I_MSII_CONTROL); + + /* Enable MSII status */ + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, I_MSII_CONTROL) | + MSII_STATUS_ENABLE, I_MSII_CONTROL); + + /* setup AFI/FPCI range */ + base = pcie->phys_pcie_reg_base; + nwl_bridge_writel(pcie, lower_32_bits(base), I_MSII_BASE_LO); + nwl_bridge_writel(pcie, upper_32_bits(base), I_MSII_BASE_HI); + + /* + * For high range MSI interrupts: disable, clear any pending, + * and enable + */ + nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_HI); + + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_HI) & + MSGF_MSI_SR_HI_MASK, MSGF_MSI_STATUS_HI); + + nwl_bridge_writel(pcie, MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI); + + /* + * For low range MSI interrupts: disable, clear any pending, + * and enable + */ + nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_LO); + + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_LO) & + MSGF_MSI_SR_LO_MASK, MSGF_MSI_STATUS_LO); + + nwl_bridge_writel(pcie, MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO); + + return 0; +err: + kfree(msi->bitmap); + msi->bitmap = NULL; + return ret; +} + +static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); + u32 breg_val, ecam_val, first_busno = 0; + int err; + + breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT; + if (!breg_val) { + dev_err(dev, "BREG is not present\n"); + return breg_val; + } + + /* Write bridge_off to breg base */ + nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_breg_base), + E_BREG_BASE_LO); + nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_breg_base), + E_BREG_BASE_HI); + + /* Enable BREG */ + nwl_bridge_writel(pcie, ~BREG_ENABLE_FORCE & BREG_ENABLE, + E_BREG_CONTROL); + + /* Disable DMA channel registers */ + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_PCIE_RX0) | + CFG_DMA_REG_BAR, BRCFG_PCIE_RX0); + + /* Enable Ingress subtractive decode translation */ + nwl_bridge_writel(pcie, SET_ISUB_CONTROL, I_ISUB_CONTROL); + + /* Enable msg filtering details */ + nwl_bridge_writel(pcie, CFG_ENABLE_MSG_FILTER_MASK, + BRCFG_PCIE_RX_MSG_FILTER); + + err = nwl_wait_for_link(pcie); + if (err) + return err; + + ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT; + if (!ecam_val) { + dev_err(dev, "ECAM is not present\n"); + return ecam_val; + } + + /* Enable ECAM */ + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | + E_ECAM_CR_ENABLE, E_ECAM_CONTROL); + + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | + (pcie->ecam_value << E_ECAM_SIZE_SHIFT), + E_ECAM_CONTROL); + + nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_ecam_base), + E_ECAM_BASE_LO); + nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_ecam_base), + E_ECAM_BASE_HI); + + /* Get bus range */ + ecam_val = nwl_bridge_readl(pcie, E_ECAM_CONTROL); + pcie->last_busno = (ecam_val & E_ECAM_SIZE_LOC) >> E_ECAM_SIZE_SHIFT; + /* Write primary, secondary and subordinate bus numbers */ + ecam_val = first_busno; + ecam_val |= (first_busno + 1) << 8; + ecam_val |= (pcie->last_busno << E_ECAM_SIZE_SHIFT); + writel(ecam_val, (pcie->ecam_base + PCI_PRIMARY_BUS)); + + if (nwl_pcie_link_up(pcie)) + dev_info(dev, "Link is UP\n"); + else + dev_info(dev, "Link is DOWN\n"); + + /* Get misc IRQ number */ + pcie->irq_misc = platform_get_irq_byname(pdev, "misc"); + if (pcie->irq_misc < 0) { + dev_err(dev, "failed to get misc IRQ %d\n", + pcie->irq_misc); + return -EINVAL; + } + + err = devm_request_irq(dev, pcie->irq_misc, + nwl_pcie_misc_handler, IRQF_SHARED, + "nwl_pcie:misc", pcie); + if (err) { + dev_err(dev, "fail to register misc IRQ#%d\n", + pcie->irq_misc); + return err; + } + + /* Disable all misc interrupts */ + nwl_bridge_writel(pcie, (u32)~MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); + + /* Clear pending misc interrupts */ + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MISC_STATUS) & + MSGF_MISC_SR_MASKALL, MSGF_MISC_STATUS); + + /* Enable all misc interrupts */ + nwl_bridge_writel(pcie, MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); + + + /* Disable all legacy interrupts */ + nwl_bridge_writel(pcie, (u32)~MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); + + /* Clear pending legacy interrupts */ + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & + MSGF_LEG_SR_MASKALL, MSGF_LEG_STATUS); + + /* Enable all legacy interrupts */ + nwl_bridge_writel(pcie, MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); + + /* Enable the bridge config interrupt */ + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_INTERRUPT) | + BRCFG_INTERRUPT_MASK, BRCFG_INTERRUPT); + + return 0; +} + +static int nwl_pcie_parse_dt(struct nwl_pcie *pcie, + struct platform_device *pdev) +{ + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node; + struct resource *res; + const char *type; + + /* Check for device type */ + type = of_get_property(node, "device_type", NULL); + if (!type || strcmp(type, "pci")) { + dev_err(dev, "invalid \"device_type\" %s\n", type); + return -EINVAL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg"); + pcie->breg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->breg_base)) + return PTR_ERR(pcie->breg_base); + pcie->phys_breg_base = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcireg"); + pcie->pcireg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pcie->pcireg_base)) + return PTR_ERR(pcie->pcireg_base); + pcie->phys_pcie_reg_base = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + pcie->ecam_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(pcie->ecam_base)) + return PTR_ERR(pcie->ecam_base); + pcie->phys_ecam_base = res->start; + + /* Get intx IRQ number */ + pcie->irq_intx = platform_get_irq_byname(pdev, "intx"); + if (pcie->irq_intx < 0) { + dev_err(dev, "failed to get intx IRQ %d\n", pcie->irq_intx); + return pcie->irq_intx; + } + + irq_set_chained_handler_and_data(pcie->irq_intx, + nwl_pcie_leg_handler, pcie); + + return 0; +} + +static const struct of_device_id nwl_pcie_of_match[] = { + { .compatible = "xlnx,nwl-pcie-2.11", }, + {} +}; + +static int nwl_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nwl_pcie *pcie; + struct pci_bus *bus; + struct pci_bus *child; + struct pci_host_bridge *bridge; + int err; + resource_size_t iobase = 0; + LIST_HEAD(res); + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!bridge) + return -ENODEV; + + pcie = pci_host_bridge_priv(bridge); + + pcie->dev = dev; + pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT; + + err = nwl_pcie_parse_dt(pcie, pdev); + if (err) { + dev_err(dev, "Parsing DT failed\n"); + return err; + } + + err = nwl_pcie_bridge_init(pcie); + if (err) { + dev_err(dev, "HW Initialization failed\n"); + return err; + } + + err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, + &iobase); + if (err) { + dev_err(dev, "Getting bridge resources failed\n"); + return err; + } + + err = devm_request_pci_bus_resources(dev, &res); + if (err) + goto error; + + err = nwl_pcie_init_irq_domain(pcie); + if (err) { + dev_err(dev, "Failed creating IRQ Domain\n"); + goto error; + } + + list_splice_init(&res, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = pcie; + bridge->busnr = pcie->root_busno; + bridge->ops = &nwl_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + err = nwl_pcie_enable_msi(pcie); + if (err < 0) { + dev_err(dev, "failed to enable MSI support: %d\n", err); + goto error; + } + } + + err = pci_scan_root_bus_bridge(bridge); + if (err) + goto error; + + bus = bridge->bus; + + pci_assign_unassigned_bus_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + pci_bus_add_devices(bus); + return 0; + +error: + pci_free_resource_list(&res); + return err; +} + +static struct platform_driver nwl_pcie_driver = { + .driver = { + .name = "nwl-pcie", + .suppress_bind_attrs = true, + .of_match_table = nwl_pcie_of_match, + }, + .probe = nwl_pcie_probe, +}; +builtin_platform_driver(nwl_pcie_driver); diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c new file mode 100644 index 000000000000..b110a3a814e3 --- /dev/null +++ b/drivers/pci/controller/pcie-xilinx.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for Xilinx AXI PCIe Bridge + * + * Copyright (c) 2012 - 2014 Xilinx, Inc. + * + * Based on the Tegra PCIe driver + * + * Bits taken from Synopsys DesignWare Host controller driver and + * ARM PCI Host generic driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* Register definitions */ +#define XILINX_PCIE_REG_BIR 0x00000130 +#define XILINX_PCIE_REG_IDR 0x00000138 +#define XILINX_PCIE_REG_IMR 0x0000013c +#define XILINX_PCIE_REG_PSCR 0x00000144 +#define XILINX_PCIE_REG_RPSC 0x00000148 +#define XILINX_PCIE_REG_MSIBASE1 0x0000014c +#define XILINX_PCIE_REG_MSIBASE2 0x00000150 +#define XILINX_PCIE_REG_RPEFR 0x00000154 +#define XILINX_PCIE_REG_RPIFR1 0x00000158 +#define XILINX_PCIE_REG_RPIFR2 0x0000015c + +/* Interrupt registers definitions */ +#define XILINX_PCIE_INTR_LINK_DOWN BIT(0) +#define XILINX_PCIE_INTR_ECRC_ERR BIT(1) +#define XILINX_PCIE_INTR_STR_ERR BIT(2) +#define XILINX_PCIE_INTR_HOT_RESET BIT(3) +#define XILINX_PCIE_INTR_CFG_TIMEOUT BIT(8) +#define XILINX_PCIE_INTR_CORRECTABLE BIT(9) +#define XILINX_PCIE_INTR_NONFATAL BIT(10) +#define XILINX_PCIE_INTR_FATAL BIT(11) +#define XILINX_PCIE_INTR_INTX BIT(16) +#define XILINX_PCIE_INTR_MSI BIT(17) +#define XILINX_PCIE_INTR_SLV_UNSUPP BIT(20) +#define XILINX_PCIE_INTR_SLV_UNEXP BIT(21) +#define XILINX_PCIE_INTR_SLV_COMPL BIT(22) +#define XILINX_PCIE_INTR_SLV_ERRP BIT(23) +#define XILINX_PCIE_INTR_SLV_CMPABT BIT(24) +#define XILINX_PCIE_INTR_SLV_ILLBUR BIT(25) +#define XILINX_PCIE_INTR_MST_DECERR BIT(26) +#define XILINX_PCIE_INTR_MST_SLVERR BIT(27) +#define XILINX_PCIE_INTR_MST_ERRP BIT(28) +#define XILINX_PCIE_IMR_ALL_MASK 0x1FF30FED +#define XILINX_PCIE_IMR_ENABLE_MASK 0x1FF30F0D +#define XILINX_PCIE_IDR_ALL_MASK 0xFFFFFFFF + +/* Root Port Error FIFO Read Register definitions */ +#define XILINX_PCIE_RPEFR_ERR_VALID BIT(18) +#define XILINX_PCIE_RPEFR_REQ_ID GENMASK(15, 0) +#define XILINX_PCIE_RPEFR_ALL_MASK 0xFFFFFFFF + +/* Root Port Interrupt FIFO Read Register 1 definitions */ +#define XILINX_PCIE_RPIFR1_INTR_VALID BIT(31) +#define XILINX_PCIE_RPIFR1_MSI_INTR BIT(30) +#define XILINX_PCIE_RPIFR1_INTR_MASK GENMASK(28, 27) +#define XILINX_PCIE_RPIFR1_ALL_MASK 0xFFFFFFFF +#define XILINX_PCIE_RPIFR1_INTR_SHIFT 27 + +/* Bridge Info Register definitions */ +#define XILINX_PCIE_BIR_ECAM_SZ_MASK GENMASK(18, 16) +#define XILINX_PCIE_BIR_ECAM_SZ_SHIFT 16 + +/* Root Port Interrupt FIFO Read Register 2 definitions */ +#define XILINX_PCIE_RPIFR2_MSG_DATA GENMASK(15, 0) + +/* Root Port Status/control Register definitions */ +#define XILINX_PCIE_REG_RPSC_BEN BIT(0) + +/* Phy Status/Control Register definitions */ +#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11) + +/* ECAM definitions */ +#define ECAM_BUS_NUM_SHIFT 20 +#define ECAM_DEV_NUM_SHIFT 12 + +/* Number of MSI IRQs */ +#define XILINX_NUM_MSI_IRQS 128 + +/** + * struct xilinx_pcie_port - PCIe port information + * @reg_base: IO Mapped Register Base + * @irq: Interrupt number + * @msi_pages: MSI pages + * @root_busno: Root Bus number + * @dev: Device pointer + * @msi_domain: MSI IRQ domain pointer + * @leg_domain: Legacy IRQ domain pointer + * @resources: Bus Resources + */ +struct xilinx_pcie_port { + void __iomem *reg_base; + u32 irq; + unsigned long msi_pages; + u8 root_busno; + struct device *dev; + struct irq_domain *msi_domain; + struct irq_domain *leg_domain; + struct list_head resources; +}; + +static DECLARE_BITMAP(msi_irq_in_use, XILINX_NUM_MSI_IRQS); + +static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg) +{ + return readl(port->reg_base + reg); +} + +static inline void pcie_write(struct xilinx_pcie_port *port, u32 val, u32 reg) +{ + writel(val, port->reg_base + reg); +} + +static inline bool xilinx_pcie_link_up(struct xilinx_pcie_port *port) +{ + return (pcie_read(port, XILINX_PCIE_REG_PSCR) & + XILINX_PCIE_REG_PSCR_LNKUP) ? 1 : 0; +} + +/** + * xilinx_pcie_clear_err_interrupts - Clear Error Interrupts + * @port: PCIe port information + */ +static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port) +{ + struct device *dev = port->dev; + unsigned long val = pcie_read(port, XILINX_PCIE_REG_RPEFR); + + if (val & XILINX_PCIE_RPEFR_ERR_VALID) { + dev_dbg(dev, "Requester ID %lu\n", + val & XILINX_PCIE_RPEFR_REQ_ID); + pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK, + XILINX_PCIE_REG_RPEFR); + } +} + +/** + * xilinx_pcie_valid_device - Check if a valid device is present on bus + * @bus: PCI Bus structure + * @devfn: device/function + * + * Return: 'true' on success and 'false' if invalid device is found + */ +static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) +{ + struct xilinx_pcie_port *port = bus->sysdata; + + /* Check if link is up when trying to access downstream ports */ + if (bus->number != port->root_busno) + if (!xilinx_pcie_link_up(port)) + return false; + + /* Only one device down on each root port */ + if (bus->number == port->root_busno && devfn > 0) + return false; + + return true; +} + +/** + * xilinx_pcie_map_bus - Get configuration base + * @bus: PCI Bus structure + * @devfn: Device/function + * @where: Offset from base + * + * Return: Base address of the configuration space needed to be + * accessed. + */ +static void __iomem *xilinx_pcie_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct xilinx_pcie_port *port = bus->sysdata; + int relbus; + + if (!xilinx_pcie_valid_device(bus, devfn)) + return NULL; + + relbus = (bus->number << ECAM_BUS_NUM_SHIFT) | + (devfn << ECAM_DEV_NUM_SHIFT); + + return port->reg_base + relbus + where; +} + +/* PCIe operations */ +static struct pci_ops xilinx_pcie_ops = { + .map_bus = xilinx_pcie_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +/* MSI functions */ + +/** + * xilinx_pcie_destroy_msi - Free MSI number + * @irq: IRQ to be freed + */ +static void xilinx_pcie_destroy_msi(unsigned int irq) +{ + struct msi_desc *msi; + struct xilinx_pcie_port *port; + struct irq_data *d = irq_get_irq_data(irq); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + if (!test_bit(hwirq, msi_irq_in_use)) { + msi = irq_get_msi_desc(irq); + port = msi_desc_to_pci_sysdata(msi); + dev_err(port->dev, "Trying to free unused MSI#%d\n", irq); + } else { + clear_bit(hwirq, msi_irq_in_use); + } +} + +/** + * xilinx_pcie_assign_msi - Allocate MSI number + * + * Return: A valid IRQ on success and error value on failure. + */ +static int xilinx_pcie_assign_msi(void) +{ + int pos; + + pos = find_first_zero_bit(msi_irq_in_use, XILINX_NUM_MSI_IRQS); + if (pos < XILINX_NUM_MSI_IRQS) + set_bit(pos, msi_irq_in_use); + else + return -ENOSPC; + + return pos; +} + +/** + * xilinx_msi_teardown_irq - Destroy the MSI + * @chip: MSI Chip descriptor + * @irq: MSI IRQ to destroy + */ +static void xilinx_msi_teardown_irq(struct msi_controller *chip, + unsigned int irq) +{ + xilinx_pcie_destroy_msi(irq); + irq_dispose_mapping(irq); +} + +/** + * xilinx_pcie_msi_setup_irq - Setup MSI request + * @chip: MSI chip pointer + * @pdev: PCIe device pointer + * @desc: MSI descriptor pointer + * + * Return: '0' on success and error value on failure + */ +static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip, + struct pci_dev *pdev, + struct msi_desc *desc) +{ + struct xilinx_pcie_port *port = pdev->bus->sysdata; + unsigned int irq; + int hwirq; + struct msi_msg msg; + phys_addr_t msg_addr; + + hwirq = xilinx_pcie_assign_msi(); + if (hwirq < 0) + return hwirq; + + irq = irq_create_mapping(port->msi_domain, hwirq); + if (!irq) + return -EINVAL; + + irq_set_msi_desc(irq, desc); + + msg_addr = virt_to_phys((void *)port->msi_pages); + + msg.address_hi = 0; + msg.address_lo = msg_addr; + msg.data = irq; + + pci_write_msi_msg(irq, &msg); + + return 0; +} + +/* MSI Chip Descriptor */ +static struct msi_controller xilinx_pcie_msi_chip = { + .setup_irq = xilinx_pcie_msi_setup_irq, + .teardown_irq = xilinx_msi_teardown_irq, +}; + +/* HW Interrupt Chip Descriptor */ +static struct irq_chip xilinx_msi_irq_chip = { + .name = "Xilinx PCIe MSI", + .irq_enable = pci_msi_unmask_irq, + .irq_disable = pci_msi_mask_irq, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +/** + * xilinx_pcie_msi_map - Set the handler for the MSI and mark IRQ as valid + * @domain: IRQ domain + * @irq: Virtual IRQ number + * @hwirq: HW interrupt number + * + * Return: Always returns 0. + */ +static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +/* IRQ Domain operations */ +static const struct irq_domain_ops msi_domain_ops = { + .map = xilinx_pcie_msi_map, +}; + +/** + * xilinx_pcie_enable_msi - Enable MSI support + * @port: PCIe port information + */ +static void xilinx_pcie_enable_msi(struct xilinx_pcie_port *port) +{ + phys_addr_t msg_addr; + + port->msi_pages = __get_free_pages(GFP_KERNEL, 0); + msg_addr = virt_to_phys((void *)port->msi_pages); + pcie_write(port, 0x0, XILINX_PCIE_REG_MSIBASE1); + pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2); +} + +/* INTx Functions */ + +/** + * xilinx_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid + * @domain: IRQ domain + * @irq: Virtual IRQ number + * @hwirq: HW interrupt number + * + * Return: Always returns 0. + */ +static int xilinx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +/* INTx IRQ Domain operations */ +static const struct irq_domain_ops intx_domain_ops = { + .map = xilinx_pcie_intx_map, + .xlate = pci_irqd_intx_xlate, +}; + +/* PCIe HW Functions */ + +/** + * xilinx_pcie_intr_handler - Interrupt Service Handler + * @irq: IRQ number + * @data: PCIe port information + * + * Return: IRQ_HANDLED on success and IRQ_NONE on failure + */ +static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) +{ + struct xilinx_pcie_port *port = (struct xilinx_pcie_port *)data; + struct device *dev = port->dev; + u32 val, mask, status; + + /* Read interrupt decode and mask registers */ + val = pcie_read(port, XILINX_PCIE_REG_IDR); + mask = pcie_read(port, XILINX_PCIE_REG_IMR); + + status = val & mask; + if (!status) + return IRQ_NONE; + + if (status & XILINX_PCIE_INTR_LINK_DOWN) + dev_warn(dev, "Link Down\n"); + + if (status & XILINX_PCIE_INTR_ECRC_ERR) + dev_warn(dev, "ECRC failed\n"); + + if (status & XILINX_PCIE_INTR_STR_ERR) + dev_warn(dev, "Streaming error\n"); + + if (status & XILINX_PCIE_INTR_HOT_RESET) + dev_info(dev, "Hot reset\n"); + + if (status & XILINX_PCIE_INTR_CFG_TIMEOUT) + dev_warn(dev, "ECAM access timeout\n"); + + if (status & XILINX_PCIE_INTR_CORRECTABLE) { + dev_warn(dev, "Correctable error message\n"); + xilinx_pcie_clear_err_interrupts(port); + } + + if (status & XILINX_PCIE_INTR_NONFATAL) { + dev_warn(dev, "Non fatal error message\n"); + xilinx_pcie_clear_err_interrupts(port); + } + + if (status & XILINX_PCIE_INTR_FATAL) { + dev_warn(dev, "Fatal error message\n"); + xilinx_pcie_clear_err_interrupts(port); + } + + if (status & (XILINX_PCIE_INTR_INTX | XILINX_PCIE_INTR_MSI)) { + val = pcie_read(port, XILINX_PCIE_REG_RPIFR1); + + /* Check whether interrupt valid */ + if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) { + dev_warn(dev, "RP Intr FIFO1 read error\n"); + goto error; + } + + /* Decode the IRQ number */ + if (val & XILINX_PCIE_RPIFR1_MSI_INTR) { + val = pcie_read(port, XILINX_PCIE_REG_RPIFR2) & + XILINX_PCIE_RPIFR2_MSG_DATA; + } else { + val = (val & XILINX_PCIE_RPIFR1_INTR_MASK) >> + XILINX_PCIE_RPIFR1_INTR_SHIFT; + val = irq_find_mapping(port->leg_domain, val); + } + + /* Clear interrupt FIFO register 1 */ + pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK, + XILINX_PCIE_REG_RPIFR1); + + /* Handle the interrupt */ + if (IS_ENABLED(CONFIG_PCI_MSI) || + !(val & XILINX_PCIE_RPIFR1_MSI_INTR)) + generic_handle_irq(val); + } + + if (status & XILINX_PCIE_INTR_SLV_UNSUPP) + dev_warn(dev, "Slave unsupported request\n"); + + if (status & XILINX_PCIE_INTR_SLV_UNEXP) + dev_warn(dev, "Slave unexpected completion\n"); + + if (status & XILINX_PCIE_INTR_SLV_COMPL) + dev_warn(dev, "Slave completion timeout\n"); + + if (status & XILINX_PCIE_INTR_SLV_ERRP) + dev_warn(dev, "Slave Error Poison\n"); + + if (status & XILINX_PCIE_INTR_SLV_CMPABT) + dev_warn(dev, "Slave Completer Abort\n"); + + if (status & XILINX_PCIE_INTR_SLV_ILLBUR) + dev_warn(dev, "Slave Illegal Burst\n"); + + if (status & XILINX_PCIE_INTR_MST_DECERR) + dev_warn(dev, "Master decode error\n"); + + if (status & XILINX_PCIE_INTR_MST_SLVERR) + dev_warn(dev, "Master slave error\n"); + + if (status & XILINX_PCIE_INTR_MST_ERRP) + dev_warn(dev, "Master error poison\n"); + +error: + /* Clear the Interrupt Decode register */ + pcie_write(port, status, XILINX_PCIE_REG_IDR); + + return IRQ_HANDLED; +} + +/** + * xilinx_pcie_init_irq_domain - Initialize IRQ domain + * @port: PCIe port information + * + * Return: '0' on success and error value on failure + */ +static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port) +{ + struct device *dev = port->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; + + /* Setup INTx */ + pcie_intc_node = of_get_next_child(node, NULL); + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return -ENODEV; + } + + port->leg_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &intx_domain_ops, + port); + if (!port->leg_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return -ENODEV; + } + + /* Setup MSI */ + if (IS_ENABLED(CONFIG_PCI_MSI)) { + port->msi_domain = irq_domain_add_linear(node, + XILINX_NUM_MSI_IRQS, + &msi_domain_ops, + &xilinx_pcie_msi_chip); + if (!port->msi_domain) { + dev_err(dev, "Failed to get a MSI IRQ domain\n"); + return -ENODEV; + } + + xilinx_pcie_enable_msi(port); + } + + return 0; +} + +/** + * xilinx_pcie_init_port - Initialize hardware + * @port: PCIe port information + */ +static void xilinx_pcie_init_port(struct xilinx_pcie_port *port) +{ + struct device *dev = port->dev; + + if (xilinx_pcie_link_up(port)) + dev_info(dev, "PCIe Link is UP\n"); + else + dev_info(dev, "PCIe Link is DOWN\n"); + + /* Disable all interrupts */ + pcie_write(port, ~XILINX_PCIE_IDR_ALL_MASK, + XILINX_PCIE_REG_IMR); + + /* Clear pending interrupts */ + pcie_write(port, pcie_read(port, XILINX_PCIE_REG_IDR) & + XILINX_PCIE_IMR_ALL_MASK, + XILINX_PCIE_REG_IDR); + + /* Enable all interrupts we handle */ + pcie_write(port, XILINX_PCIE_IMR_ENABLE_MASK, XILINX_PCIE_REG_IMR); + + /* Enable the Bridge enable bit */ + pcie_write(port, pcie_read(port, XILINX_PCIE_REG_RPSC) | + XILINX_PCIE_REG_RPSC_BEN, + XILINX_PCIE_REG_RPSC); +} + +/** + * xilinx_pcie_parse_dt - Parse Device tree + * @port: PCIe port information + * + * Return: '0' on success and error value on failure + */ +static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port) +{ + struct device *dev = port->dev; + struct device_node *node = dev->of_node; + struct resource regs; + const char *type; + int err; + + type = of_get_property(node, "device_type", NULL); + if (!type || strcmp(type, "pci")) { + dev_err(dev, "invalid \"device_type\" %s\n", type); + return -EINVAL; + } + + err = of_address_to_resource(node, 0, ®s); + if (err) { + dev_err(dev, "missing \"reg\" property\n"); + return err; + } + + port->reg_base = devm_pci_remap_cfg_resource(dev, ®s); + if (IS_ERR(port->reg_base)) + return PTR_ERR(port->reg_base); + + port->irq = irq_of_parse_and_map(node, 0); + err = devm_request_irq(dev, port->irq, xilinx_pcie_intr_handler, + IRQF_SHARED | IRQF_NO_THREAD, + "xilinx-pcie", port); + if (err) { + dev_err(dev, "unable to request irq %d\n", port->irq); + return err; + } + + return 0; +} + +/** + * xilinx_pcie_probe - Probe function + * @pdev: Platform device pointer + * + * Return: '0' on success and error value on failure + */ +static int xilinx_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct xilinx_pcie_port *port; + struct pci_bus *bus, *child; + struct pci_host_bridge *bridge; + int err; + resource_size_t iobase = 0; + LIST_HEAD(res); + + if (!dev->of_node) + return -ENODEV; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port)); + if (!bridge) + return -ENODEV; + + port = pci_host_bridge_priv(bridge); + + port->dev = dev; + + err = xilinx_pcie_parse_dt(port); + if (err) { + dev_err(dev, "Parsing DT failed\n"); + return err; + } + + xilinx_pcie_init_port(port); + + err = xilinx_pcie_init_irq_domain(port); + if (err) { + dev_err(dev, "Failed creating IRQ Domain\n"); + return err; + } + + err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, + &iobase); + if (err) { + dev_err(dev, "Getting bridge resources failed\n"); + return err; + } + + err = devm_request_pci_bus_resources(dev, &res); + if (err) + goto error; + + + list_splice_init(&res, &bridge->windows); + bridge->dev.parent = dev; + bridge->sysdata = port; + bridge->busnr = 0; + bridge->ops = &xilinx_pcie_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + +#ifdef CONFIG_PCI_MSI + xilinx_pcie_msi_chip.dev = dev; + bridge->msi = &xilinx_pcie_msi_chip; +#endif + err = pci_scan_root_bus_bridge(bridge); + if (err < 0) + goto error; + + bus = bridge->bus; + + pci_assign_unassigned_bus_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + pci_bus_add_devices(bus); + return 0; + +error: + pci_free_resource_list(&res); + return err; +} + +static const struct of_device_id xilinx_pcie_of_match[] = { + { .compatible = "xlnx,axi-pcie-host-1.00.a", }, + {} +}; + +static struct platform_driver xilinx_pcie_driver = { + .driver = { + .name = "xilinx-pcie", + .of_match_table = xilinx_pcie_of_match, + .suppress_bind_attrs = true, + }, + .probe = xilinx_pcie_probe, +}; +builtin_platform_driver(xilinx_pcie_driver); diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c new file mode 100644 index 000000000000..942b64fc7f1f --- /dev/null +++ b/drivers/pci/controller/vmd.c @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Volume Management Device driver + * Copyright (c) 2015, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define VMD_CFGBAR 0 +#define VMD_MEMBAR1 2 +#define VMD_MEMBAR2 4 + +#define PCI_REG_VMCAP 0x40 +#define BUS_RESTRICT_CAP(vmcap) (vmcap & 0x1) +#define PCI_REG_VMCONFIG 0x44 +#define BUS_RESTRICT_CFG(vmcfg) ((vmcfg >> 8) & 0x3) +#define PCI_REG_VMLOCK 0x70 +#define MB2_SHADOW_EN(vmlock) (vmlock & 0x2) + +enum vmd_features { + /* + * Device may contain registers which hint the physical location of the + * membars, in order to allow proper address translation during + * resource assignment to enable guest virtualization + */ + VMD_FEAT_HAS_MEMBAR_SHADOW = (1 << 0), + + /* + * Device may provide root port configuration information which limits + * bus numbering + */ + VMD_FEAT_HAS_BUS_RESTRICTIONS = (1 << 1), +}; + +/* + * Lock for manipulating VMD IRQ lists. + */ +static DEFINE_RAW_SPINLOCK(list_lock); + +/** + * struct vmd_irq - private data to map driver IRQ to the VMD shared vector + * @node: list item for parent traversal. + * @irq: back pointer to parent. + * @enabled: true if driver enabled IRQ + * @virq: the virtual IRQ value provided to the requesting driver. + * + * Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to + * a VMD IRQ using this structure. + */ +struct vmd_irq { + struct list_head node; + struct vmd_irq_list *irq; + bool enabled; + unsigned int virq; +}; + +/** + * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector + * @irq_list: the list of irq's the VMD one demuxes to. + * @srcu: SRCU struct for local synchronization. + * @count: number of child IRQs assigned to this vector; used to track + * sharing. + */ +struct vmd_irq_list { + struct list_head irq_list; + struct srcu_struct srcu; + unsigned int count; +}; + +struct vmd_dev { + struct pci_dev *dev; + + spinlock_t cfg_lock; + char __iomem *cfgbar; + + int msix_count; + struct vmd_irq_list *irqs; + + struct pci_sysdata sysdata; + struct resource resources[3]; + struct irq_domain *irq_domain; + struct pci_bus *bus; + +#ifdef CONFIG_X86_DEV_DMA_OPS + struct dma_map_ops dma_ops; + struct dma_domain dma_domain; +#endif +}; + +static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) +{ + return container_of(bus->sysdata, struct vmd_dev, sysdata); +} + +static inline unsigned int index_from_irqs(struct vmd_dev *vmd, + struct vmd_irq_list *irqs) +{ + return irqs - vmd->irqs; +} + +/* + * Drivers managing a device in a VMD domain allocate their own IRQs as before, + * but the MSI entry for the hardware it's driving will be programmed with a + * destination ID for the VMD MSI-X table. The VMD muxes interrupts in its + * domain into one of its own, and the VMD driver de-muxes these for the + * handlers sharing that VMD IRQ. The vmd irq_domain provides the operations + * and irq_chip to set this up. + */ +static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct vmd_irq *vmdirq = data->chip_data; + struct vmd_irq_list *irq = vmdirq->irq; + struct vmd_dev *vmd = irq_data_get_irq_handler_data(data); + + msg->address_hi = MSI_ADDR_BASE_HI; + msg->address_lo = MSI_ADDR_BASE_LO | + MSI_ADDR_DEST_ID(index_from_irqs(vmd, irq)); + msg->data = 0; +} + +/* + * We rely on MSI_FLAG_USE_DEF_CHIP_OPS to set the IRQ mask/unmask ops. + */ +static void vmd_irq_enable(struct irq_data *data) +{ + struct vmd_irq *vmdirq = data->chip_data; + unsigned long flags; + + raw_spin_lock_irqsave(&list_lock, flags); + WARN_ON(vmdirq->enabled); + list_add_tail_rcu(&vmdirq->node, &vmdirq->irq->irq_list); + vmdirq->enabled = true; + raw_spin_unlock_irqrestore(&list_lock, flags); + + data->chip->irq_unmask(data); +} + +static void vmd_irq_disable(struct irq_data *data) +{ + struct vmd_irq *vmdirq = data->chip_data; + unsigned long flags; + + data->chip->irq_mask(data); + + raw_spin_lock_irqsave(&list_lock, flags); + if (vmdirq->enabled) { + list_del_rcu(&vmdirq->node); + vmdirq->enabled = false; + } + raw_spin_unlock_irqrestore(&list_lock, flags); +} + +/* + * XXX: Stubbed until we develop acceptable way to not create conflicts with + * other devices sharing the same vector. + */ +static int vmd_irq_set_affinity(struct irq_data *data, + const struct cpumask *dest, bool force) +{ + return -EINVAL; +} + +static struct irq_chip vmd_msi_controller = { + .name = "VMD-MSI", + .irq_enable = vmd_irq_enable, + .irq_disable = vmd_irq_disable, + .irq_compose_msi_msg = vmd_compose_msi_msg, + .irq_set_affinity = vmd_irq_set_affinity, +}; + +static irq_hw_number_t vmd_get_hwirq(struct msi_domain_info *info, + msi_alloc_info_t *arg) +{ + return 0; +} + +/* + * XXX: We can be even smarter selecting the best IRQ once we solve the + * affinity problem. + */ +static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd, struct msi_desc *desc) +{ + int i, best = 1; + unsigned long flags; + + if (pci_is_bridge(msi_desc_to_pci_dev(desc)) || vmd->msix_count == 1) + return &vmd->irqs[0]; + + raw_spin_lock_irqsave(&list_lock, flags); + for (i = 1; i < vmd->msix_count; i++) + if (vmd->irqs[i].count < vmd->irqs[best].count) + best = i; + vmd->irqs[best].count++; + raw_spin_unlock_irqrestore(&list_lock, flags); + + return &vmd->irqs[best]; +} + +static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info, + unsigned int virq, irq_hw_number_t hwirq, + msi_alloc_info_t *arg) +{ + struct msi_desc *desc = arg->desc; + struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(desc)->bus); + struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL); + unsigned int index, vector; + + if (!vmdirq) + return -ENOMEM; + + INIT_LIST_HEAD(&vmdirq->node); + vmdirq->irq = vmd_next_irq(vmd, desc); + vmdirq->virq = virq; + index = index_from_irqs(vmd, vmdirq->irq); + vector = pci_irq_vector(vmd->dev, index); + + irq_domain_set_info(domain, virq, vector, info->chip, vmdirq, + handle_untracked_irq, vmd, NULL); + return 0; +} + +static void vmd_msi_free(struct irq_domain *domain, + struct msi_domain_info *info, unsigned int virq) +{ + struct vmd_irq *vmdirq = irq_get_chip_data(virq); + unsigned long flags; + + synchronize_srcu(&vmdirq->irq->srcu); + + /* XXX: Potential optimization to rebalance */ + raw_spin_lock_irqsave(&list_lock, flags); + vmdirq->irq->count--; + raw_spin_unlock_irqrestore(&list_lock, flags); + + kfree(vmdirq); +} + +static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev, + int nvec, msi_alloc_info_t *arg) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct vmd_dev *vmd = vmd_from_bus(pdev->bus); + + if (nvec > vmd->msix_count) + return vmd->msix_count; + + memset(arg, 0, sizeof(*arg)); + return 0; +} + +static void vmd_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) +{ + arg->desc = desc; +} + +static struct msi_domain_ops vmd_msi_domain_ops = { + .get_hwirq = vmd_get_hwirq, + .msi_init = vmd_msi_init, + .msi_free = vmd_msi_free, + .msi_prepare = vmd_msi_prepare, + .set_desc = vmd_set_desc, +}; + +static struct msi_domain_info vmd_msi_domain_info = { + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX, + .ops = &vmd_msi_domain_ops, + .chip = &vmd_msi_controller, +}; + +#ifdef CONFIG_X86_DEV_DMA_OPS +/* + * VMD replaces the requester ID with its own. DMA mappings for devices in a + * VMD domain need to be mapped for the VMD, not the device requiring + * the mapping. + */ +static struct device *to_vmd_dev(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct vmd_dev *vmd = vmd_from_bus(pdev->bus); + + return &vmd->dev->dev; +} + +static const struct dma_map_ops *vmd_dma_ops(struct device *dev) +{ + return get_dma_ops(to_vmd_dev(dev)); +} + +static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr, + gfp_t flag, unsigned long attrs) +{ + return vmd_dma_ops(dev)->alloc(to_vmd_dev(dev), size, addr, flag, + attrs); +} + +static void vmd_free(struct device *dev, size_t size, void *vaddr, + dma_addr_t addr, unsigned long attrs) +{ + return vmd_dma_ops(dev)->free(to_vmd_dev(dev), size, vaddr, addr, + attrs); +} + +static int vmd_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t addr, size_t size, + unsigned long attrs) +{ + return vmd_dma_ops(dev)->mmap(to_vmd_dev(dev), vma, cpu_addr, addr, + size, attrs); +} + +static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t addr, size_t size, + unsigned long attrs) +{ + return vmd_dma_ops(dev)->get_sgtable(to_vmd_dev(dev), sgt, cpu_addr, + addr, size, attrs); +} + +static dma_addr_t vmd_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + return vmd_dma_ops(dev)->map_page(to_vmd_dev(dev), page, offset, size, + dir, attrs); +} + +static void vmd_unmap_page(struct device *dev, dma_addr_t addr, size_t size, + enum dma_data_direction dir, unsigned long attrs) +{ + vmd_dma_ops(dev)->unmap_page(to_vmd_dev(dev), addr, size, dir, attrs); +} + +static int vmd_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir, unsigned long attrs) +{ + return vmd_dma_ops(dev)->map_sg(to_vmd_dev(dev), sg, nents, dir, attrs); +} + +static void vmd_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir, unsigned long attrs) +{ + vmd_dma_ops(dev)->unmap_sg(to_vmd_dev(dev), sg, nents, dir, attrs); +} + +static void vmd_sync_single_for_cpu(struct device *dev, dma_addr_t addr, + size_t size, enum dma_data_direction dir) +{ + vmd_dma_ops(dev)->sync_single_for_cpu(to_vmd_dev(dev), addr, size, dir); +} + +static void vmd_sync_single_for_device(struct device *dev, dma_addr_t addr, + size_t size, enum dma_data_direction dir) +{ + vmd_dma_ops(dev)->sync_single_for_device(to_vmd_dev(dev), addr, size, + dir); +} + +static void vmd_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ + vmd_dma_ops(dev)->sync_sg_for_cpu(to_vmd_dev(dev), sg, nents, dir); +} + +static void vmd_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ + vmd_dma_ops(dev)->sync_sg_for_device(to_vmd_dev(dev), sg, nents, dir); +} + +static int vmd_mapping_error(struct device *dev, dma_addr_t addr) +{ + return vmd_dma_ops(dev)->mapping_error(to_vmd_dev(dev), addr); +} + +static int vmd_dma_supported(struct device *dev, u64 mask) +{ + return vmd_dma_ops(dev)->dma_supported(to_vmd_dev(dev), mask); +} + +#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK +static u64 vmd_get_required_mask(struct device *dev) +{ + return vmd_dma_ops(dev)->get_required_mask(to_vmd_dev(dev)); +} +#endif + +static void vmd_teardown_dma_ops(struct vmd_dev *vmd) +{ + struct dma_domain *domain = &vmd->dma_domain; + + if (get_dma_ops(&vmd->dev->dev)) + del_dma_domain(domain); +} + +#define ASSIGN_VMD_DMA_OPS(source, dest, fn) \ + do { \ + if (source->fn) \ + dest->fn = vmd_##fn; \ + } while (0) + +static void vmd_setup_dma_ops(struct vmd_dev *vmd) +{ + const struct dma_map_ops *source = get_dma_ops(&vmd->dev->dev); + struct dma_map_ops *dest = &vmd->dma_ops; + struct dma_domain *domain = &vmd->dma_domain; + + domain->domain_nr = vmd->sysdata.domain; + domain->dma_ops = dest; + + if (!source) + return; + ASSIGN_VMD_DMA_OPS(source, dest, alloc); + ASSIGN_VMD_DMA_OPS(source, dest, free); + ASSIGN_VMD_DMA_OPS(source, dest, mmap); + ASSIGN_VMD_DMA_OPS(source, dest, get_sgtable); + ASSIGN_VMD_DMA_OPS(source, dest, map_page); + ASSIGN_VMD_DMA_OPS(source, dest, unmap_page); + ASSIGN_VMD_DMA_OPS(source, dest, map_sg); + ASSIGN_VMD_DMA_OPS(source, dest, unmap_sg); + ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_cpu); + ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_device); + ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_cpu); + ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_device); + ASSIGN_VMD_DMA_OPS(source, dest, mapping_error); + ASSIGN_VMD_DMA_OPS(source, dest, dma_supported); +#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK + ASSIGN_VMD_DMA_OPS(source, dest, get_required_mask); +#endif + add_dma_domain(domain); +} +#undef ASSIGN_VMD_DMA_OPS +#else +static void vmd_teardown_dma_ops(struct vmd_dev *vmd) {} +static void vmd_setup_dma_ops(struct vmd_dev *vmd) {} +#endif + +static char __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, + unsigned int devfn, int reg, int len) +{ + char __iomem *addr = vmd->cfgbar + + (bus->number << 20) + (devfn << 12) + reg; + + if ((addr - vmd->cfgbar) + len >= + resource_size(&vmd->dev->resource[VMD_CFGBAR])) + return NULL; + + return addr; +} + +/* + * CPU may deadlock if config space is not serialized on some versions of this + * hardware, so all config space access is done under a spinlock. + */ +static int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg, + int len, u32 *value) +{ + struct vmd_dev *vmd = vmd_from_bus(bus); + char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len); + unsigned long flags; + int ret = 0; + + if (!addr) + return -EFAULT; + + spin_lock_irqsave(&vmd->cfg_lock, flags); + switch (len) { + case 1: + *value = readb(addr); + break; + case 2: + *value = readw(addr); + break; + case 4: + *value = readl(addr); + break; + default: + ret = -EINVAL; + break; + } + spin_unlock_irqrestore(&vmd->cfg_lock, flags); + return ret; +} + +/* + * VMD h/w converts non-posted config writes to posted memory writes. The + * read-back in this function forces the completion so it returns only after + * the config space was written, as expected. + */ +static int vmd_pci_write(struct pci_bus *bus, unsigned int devfn, int reg, + int len, u32 value) +{ + struct vmd_dev *vmd = vmd_from_bus(bus); + char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len); + unsigned long flags; + int ret = 0; + + if (!addr) + return -EFAULT; + + spin_lock_irqsave(&vmd->cfg_lock, flags); + switch (len) { + case 1: + writeb(value, addr); + readb(addr); + break; + case 2: + writew(value, addr); + readw(addr); + break; + case 4: + writel(value, addr); + readl(addr); + break; + default: + ret = -EINVAL; + break; + } + spin_unlock_irqrestore(&vmd->cfg_lock, flags); + return ret; +} + +static struct pci_ops vmd_ops = { + .read = vmd_pci_read, + .write = vmd_pci_write, +}; + +static void vmd_attach_resources(struct vmd_dev *vmd) +{ + vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1]; + vmd->dev->resource[VMD_MEMBAR2].child = &vmd->resources[2]; +} + +static void vmd_detach_resources(struct vmd_dev *vmd) +{ + vmd->dev->resource[VMD_MEMBAR1].child = NULL; + vmd->dev->resource[VMD_MEMBAR2].child = NULL; +} + +/* + * VMD domains start at 0x10000 to not clash with ACPI _SEG domains. + * Per ACPI r6.0, sec 6.5.6, _SEG returns an integer, of which the lower + * 16 bits are the PCI Segment Group (domain) number. Other bits are + * currently reserved. + */ +static int vmd_find_free_domain(void) +{ + int domain = 0xffff; + struct pci_bus *bus = NULL; + + while ((bus = pci_find_next_bus(bus)) != NULL) + domain = max_t(int, domain, pci_domain_nr(bus)); + return domain + 1; +} + +static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) +{ + struct pci_sysdata *sd = &vmd->sysdata; + struct fwnode_handle *fn; + struct resource *res; + u32 upper_bits; + unsigned long flags; + LIST_HEAD(resources); + resource_size_t offset[2] = {0}; + resource_size_t membar2_offset = 0x2000, busn_start = 0; + + /* + * Shadow registers may exist in certain VMD device ids which allow + * guests to correctly assign host physical addresses to the root ports + * and child devices. These registers will either return the host value + * or 0, depending on an enable bit in the VMD device. + */ + if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) { + u32 vmlock; + int ret; + + membar2_offset = 0x2018; + ret = pci_read_config_dword(vmd->dev, PCI_REG_VMLOCK, &vmlock); + if (ret || vmlock == ~0) + return -ENODEV; + + if (MB2_SHADOW_EN(vmlock)) { + void __iomem *membar2; + + membar2 = pci_iomap(vmd->dev, VMD_MEMBAR2, 0); + if (!membar2) + return -ENOMEM; + offset[0] = vmd->dev->resource[VMD_MEMBAR1].start - + readq(membar2 + 0x2008); + offset[1] = vmd->dev->resource[VMD_MEMBAR2].start - + readq(membar2 + 0x2010); + pci_iounmap(vmd->dev, membar2); + } + } + + /* + * Certain VMD devices may have a root port configuration option which + * limits the bus range to between 0-127 or 128-255 + */ + if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { + u32 vmcap, vmconfig; + + pci_read_config_dword(vmd->dev, PCI_REG_VMCAP, &vmcap); + pci_read_config_dword(vmd->dev, PCI_REG_VMCONFIG, &vmconfig); + if (BUS_RESTRICT_CAP(vmcap) && + (BUS_RESTRICT_CFG(vmconfig) == 0x1)) + busn_start = 128; + } + + res = &vmd->dev->resource[VMD_CFGBAR]; + vmd->resources[0] = (struct resource) { + .name = "VMD CFGBAR", + .start = busn_start, + .end = busn_start + (resource_size(res) >> 20) - 1, + .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED, + }; + + /* + * If the window is below 4GB, clear IORESOURCE_MEM_64 so we can + * put 32-bit resources in the window. + * + * There's no hardware reason why a 64-bit window *couldn't* + * contain a 32-bit resource, but pbus_size_mem() computes the + * bridge window size assuming a 64-bit window will contain no + * 32-bit resources. __pci_assign_resource() enforces that + * artificial restriction to make sure everything will fit. + * + * The only way we could use a 64-bit non-prefechable MEMBAR is + * if its address is <4GB so that we can convert it to a 32-bit + * resource. To be visible to the host OS, all VMD endpoints must + * be initially configured by platform BIOS, which includes setting + * up these resources. We can assume the device is configured + * according to the platform needs. + */ + res = &vmd->dev->resource[VMD_MEMBAR1]; + upper_bits = upper_32_bits(res->end); + flags = res->flags & ~IORESOURCE_SIZEALIGN; + if (!upper_bits) + flags &= ~IORESOURCE_MEM_64; + vmd->resources[1] = (struct resource) { + .name = "VMD MEMBAR1", + .start = res->start, + .end = res->end, + .flags = flags, + .parent = res, + }; + + res = &vmd->dev->resource[VMD_MEMBAR2]; + upper_bits = upper_32_bits(res->end); + flags = res->flags & ~IORESOURCE_SIZEALIGN; + if (!upper_bits) + flags &= ~IORESOURCE_MEM_64; + vmd->resources[2] = (struct resource) { + .name = "VMD MEMBAR2", + .start = res->start + membar2_offset, + .end = res->end, + .flags = flags, + .parent = res, + }; + + sd->vmd_domain = true; + sd->domain = vmd_find_free_domain(); + if (sd->domain < 0) + return sd->domain; + + sd->node = pcibus_to_node(vmd->dev->bus); + + fn = irq_domain_alloc_named_id_fwnode("VMD-MSI", vmd->sysdata.domain); + if (!fn) + return -ENODEV; + + vmd->irq_domain = pci_msi_create_irq_domain(fn, &vmd_msi_domain_info, + x86_vector_domain); + irq_domain_free_fwnode(fn); + if (!vmd->irq_domain) + return -ENODEV; + + pci_add_resource(&resources, &vmd->resources[0]); + pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]); + pci_add_resource_offset(&resources, &vmd->resources[2], offset[1]); + + vmd->bus = pci_create_root_bus(&vmd->dev->dev, busn_start, &vmd_ops, + sd, &resources); + if (!vmd->bus) { + pci_free_resource_list(&resources); + irq_domain_remove(vmd->irq_domain); + return -ENODEV; + } + + vmd_attach_resources(vmd); + vmd_setup_dma_ops(vmd); + dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain); + pci_rescan_bus(vmd->bus); + + WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj, + "domain"), "Can't create symlink to domain\n"); + return 0; +} + +static irqreturn_t vmd_irq(int irq, void *data) +{ + struct vmd_irq_list *irqs = data; + struct vmd_irq *vmdirq; + int idx; + + idx = srcu_read_lock(&irqs->srcu); + list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) + generic_handle_irq(vmdirq->virq); + srcu_read_unlock(&irqs->srcu, idx); + + return IRQ_HANDLED; +} + +static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct vmd_dev *vmd; + int i, err; + + if (resource_size(&dev->resource[VMD_CFGBAR]) < (1 << 20)) + return -ENOMEM; + + vmd = devm_kzalloc(&dev->dev, sizeof(*vmd), GFP_KERNEL); + if (!vmd) + return -ENOMEM; + + vmd->dev = dev; + err = pcim_enable_device(dev); + if (err < 0) + return err; + + vmd->cfgbar = pcim_iomap(dev, VMD_CFGBAR, 0); + if (!vmd->cfgbar) + return -ENOMEM; + + pci_set_master(dev); + if (dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64)) && + dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32))) + return -ENODEV; + + vmd->msix_count = pci_msix_vec_count(dev); + if (vmd->msix_count < 0) + return -ENODEV; + + vmd->msix_count = pci_alloc_irq_vectors(dev, 1, vmd->msix_count, + PCI_IRQ_MSIX); + if (vmd->msix_count < 0) + return vmd->msix_count; + + vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs), + GFP_KERNEL); + if (!vmd->irqs) + return -ENOMEM; + + for (i = 0; i < vmd->msix_count; i++) { + err = init_srcu_struct(&vmd->irqs[i].srcu); + if (err) + return err; + + INIT_LIST_HEAD(&vmd->irqs[i].irq_list); + err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i), + vmd_irq, IRQF_NO_THREAD, + "vmd", &vmd->irqs[i]); + if (err) + return err; + } + + spin_lock_init(&vmd->cfg_lock); + pci_set_drvdata(dev, vmd); + err = vmd_enable_domain(vmd, (unsigned long) id->driver_data); + if (err) + return err; + + dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n", + vmd->sysdata.domain); + return 0; +} + +static void vmd_cleanup_srcu(struct vmd_dev *vmd) +{ + int i; + + for (i = 0; i < vmd->msix_count; i++) + cleanup_srcu_struct(&vmd->irqs[i].srcu); +} + +static void vmd_remove(struct pci_dev *dev) +{ + struct vmd_dev *vmd = pci_get_drvdata(dev); + + vmd_detach_resources(vmd); + sysfs_remove_link(&vmd->dev->dev.kobj, "domain"); + pci_stop_root_bus(vmd->bus); + pci_remove_root_bus(vmd->bus); + vmd_cleanup_srcu(vmd); + vmd_teardown_dma_ops(vmd); + irq_domain_remove(vmd->irq_domain); +} + +#ifdef CONFIG_PM_SLEEP +static int vmd_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct vmd_dev *vmd = pci_get_drvdata(pdev); + int i; + + for (i = 0; i < vmd->msix_count; i++) + devm_free_irq(dev, pci_irq_vector(pdev, i), &vmd->irqs[i]); + + pci_save_state(pdev); + return 0; +} + +static int vmd_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct vmd_dev *vmd = pci_get_drvdata(pdev); + int err, i; + + for (i = 0; i < vmd->msix_count; i++) { + err = devm_request_irq(dev, pci_irq_vector(pdev, i), + vmd_irq, IRQF_NO_THREAD, + "vmd", &vmd->irqs[i]); + if (err) + return err; + } + + pci_restore_state(pdev); + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(vmd_dev_pm_ops, vmd_suspend, vmd_resume); + +static const struct pci_device_id vmd_ids[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_201D),}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0), + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | + VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, vmd_ids); + +static struct pci_driver vmd_drv = { + .name = "vmd", + .id_table = vmd_ids, + .probe = vmd_probe, + .remove = vmd_remove, + .driver = { + .pm = &vmd_dev_pm_ops, + }, +}; +module_pci_driver(vmd_drv); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.6"); diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig deleted file mode 100644 index 16f52c626b4b..000000000000 --- a/drivers/pci/dwc/Kconfig +++ /dev/null @@ -1,197 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -menu "DesignWare PCI Core Support" - depends on PCI - -config PCIE_DW - bool - -config PCIE_DW_HOST - bool - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW - -config PCIE_DW_EP - bool - depends on PCI_ENDPOINT - select PCIE_DW - -config PCI_DRA7XX - bool - -config PCI_DRA7XX_HOST - bool "TI DRA7xx PCIe controller Host Mode" - depends on SOC_DRA7XX || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN - depends on OF && HAS_IOMEM && TI_PIPE3 - select PCIE_DW_HOST - select PCI_DRA7XX - default y - 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. - This controller can work either as EP or RC. In order to enable - host-specific features PCI_DRA7XX_HOST must be selected and in order - to enable device-specific features PCI_DRA7XX_EP must be selected. - This uses the DesignWare core. - -config PCI_DRA7XX_EP - bool "TI DRA7xx PCIe controller Endpoint Mode" - depends on SOC_DRA7XX || COMPILE_TEST - depends on PCI_ENDPOINT - depends on OF && HAS_IOMEM && TI_PIPE3 - select PCIE_DW_EP - select PCI_DRA7XX - help - Enables support for the PCIe controller in the DRA7xx SoC to work in - endpoint mode. There are two instances of PCIe controller in DRA7xx. - This controller can work either as EP or RC. In order to enable - host-specific features PCI_DRA7XX_HOST must be selected and in order - to enable device-specific features PCI_DRA7XX_EP must be selected. - This uses the DesignWare core. - -config PCIE_DW_PLAT - bool - -config PCIE_DW_PLAT_HOST - bool "Platform bus based DesignWare PCIe Controller - Host mode" - depends on PCI && PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - select PCIE_DW_PLAT - default y - help - Enables support for the PCIe controller in the Designware IP to - work in host mode. There are two instances of PCIe controller in - Designware IP. - This controller can work either as EP or RC. In order to enable - host-specific features PCIE_DW_PLAT_HOST must be selected and in - order to enable device-specific features PCI_DW_PLAT_EP must be - selected. - -config PCIE_DW_PLAT_EP - bool "Platform bus based DesignWare PCIe Controller - Endpoint mode" - depends on PCI && PCI_MSI_IRQ_DOMAIN - depends on PCI_ENDPOINT - select PCIE_DW_EP - select PCIE_DW_PLAT - help - Enables support for the PCIe controller in the Designware IP to - work in endpoint mode. There are two instances of PCIe controller - in Designware IP. - This controller can work either as EP or RC. In order to enable - host-specific features PCIE_DW_PLAT_HOST must be selected and in - order to enable device-specific features PCI_DW_PLAT_EP must be - selected. - -config PCI_EXYNOS - bool "Samsung Exynos PCIe controller" - depends on SOC_EXYNOS5440 || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - -config PCI_IMX6 - bool "Freescale i.MX6 PCIe controller" - depends on SOC_IMX6Q || (ARM && COMPILE_TEST) - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - -config PCIE_SPEAR13XX - bool "STMicroelectronics SPEAr PCIe controller" - depends on ARCH_SPEAR13XX || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - help - Say Y here if you want PCIe support on SPEAr13XX SoCs. - -config PCI_KEYSTONE - bool "TI Keystone PCIe controller" - depends on ARCH_KEYSTONE || (ARM && COMPILE_TEST) - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - help - Say Y here if you want to enable PCI controller support on Keystone - SoCs. The PCI controller on Keystone is based on DesignWare hardware - and therefore the driver re-uses the DesignWare core functions to - implement the driver. - -config PCI_LAYERSCAPE - bool "Freescale Layerscape PCIe controller" - depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST) - depends on PCI_MSI_IRQ_DOMAIN - select MFD_SYSCON - select PCIE_DW_HOST - help - Say Y here if you want PCIe controller support on Layerscape SoCs. - -config PCI_HISI - depends on OF && (ARM64 || COMPILE_TEST) - bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers" - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - select PCI_HOST_COMMON - help - Say Y here if you want PCIe controller support on HiSilicon - Hip05 and Hip06 SoCs - -config PCIE_QCOM - bool "Qualcomm PCIe controller" - depends on OF && (ARCH_QCOM || COMPILE_TEST) - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - help - Say Y here to enable PCIe controller support on Qualcomm SoCs. The - PCIe controller uses the DesignWare core plus Qualcomm-specific - hardware wrappers. - -config PCIE_ARMADA_8K - bool "Marvell Armada-8K PCIe controller" - depends on ARCH_MVEBU || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - help - Say Y here if you want to enable PCIe controller support on - Armada-8K SoCs. The PCIe controller on Armada-8K is based on - DesignWare hardware and therefore the driver re-uses the - DesignWare core functions to implement the driver. - -config PCIE_ARTPEC6 - bool - -config PCIE_ARTPEC6_HOST - bool "Axis ARTPEC-6 PCIe controller Host Mode" - depends on MACH_ARTPEC6 || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - select PCIE_ARTPEC6 - help - Enables support for the PCIe controller in the ARTPEC-6 SoC to work in - host mode. This uses the DesignWare core. - -config PCIE_ARTPEC6_EP - bool "Axis ARTPEC-6 PCIe controller Endpoint Mode" - depends on MACH_ARTPEC6 || COMPILE_TEST - depends on PCI_ENDPOINT - select PCIE_DW_EP - select PCIE_ARTPEC6 - help - Enables support for the PCIe controller in the ARTPEC-6 SoC to work in - endpoint mode. This uses the DesignWare core. - -config PCIE_KIRIN - depends on OF && (ARM64 || COMPILE_TEST) - bool "HiSilicon Kirin series SoCs PCIe controllers" - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - help - Say Y here if you want PCIe controller support - on HiSilicon Kirin series SoCs. - -config PCIE_HISI_STB - bool "HiSilicon STB SoCs PCIe controllers" - depends on ARCH_HISI || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW_HOST - help - Say Y here if you want PCIe controller support on HiSilicon STB SoCs - -endmenu diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile deleted file mode 100644 index 5d2ce72c7a52..000000000000 --- a/drivers/pci/dwc/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PCIE_DW) += pcie-designware.o -obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o -obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o -obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o -obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o -obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o -obj-$(CONFIG_PCI_IMX6) += pci-imx6.o -obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o -obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o -obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o -obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o -obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o -obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o -obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o -obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o - -# The following drivers are for devices that use the generic ACPI -# pci_root.c driver but don't support standard ECAM config access. -# They contain MCFG quirks to replace the generic ECAM accessors with -# device-specific ones that are shared with the DT driver. - -# The ACPI driver is generic and should not require driver-specific -# config options to be enabled, so we always build these drivers on -# ARM64 and use internal ifdefs to only build the pieces we need -# depending on whether ACPI, the DT driver, or both are enabled. - -ifdef CONFIG_PCI -obj-$(CONFIG_ARM64) += pcie-hisi.o -endif diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c deleted file mode 100644 index f688204e50c5..000000000000 --- a/drivers/pci/dwc/pci-dra7xx.c +++ /dev/null @@ -1,846 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs - * - * Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com - * - * Authors: Kishon Vijay Abraham I - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" -#include "pcie-designware.h" - -/* PCIe controller wrapper DRA7XX configuration registers */ - -#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN 0x0024 -#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN 0x0028 -#define ERR_SYS BIT(0) -#define ERR_FATAL BIT(1) -#define ERR_NONFATAL BIT(2) -#define ERR_COR BIT(3) -#define ERR_AXI BIT(4) -#define ERR_ECRC BIT(5) -#define PME_TURN_OFF BIT(8) -#define PME_TO_ACK BIT(9) -#define PM_PME BIT(10) -#define LINK_REQ_RST BIT(11) -#define LINK_UP_EVT BIT(12) -#define CFG_BME_EVT BIT(13) -#define CFG_MSE_EVT BIT(14) -#define INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \ - ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \ - LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT) - -#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI 0x0034 -#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI 0x0038 -#define INTA BIT(0) -#define INTB BIT(1) -#define INTC BIT(2) -#define INTD BIT(3) -#define MSI BIT(4) -#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD) - -#define PCIECTRL_TI_CONF_DEVICE_TYPE 0x0100 -#define DEVICE_TYPE_EP 0x0 -#define DEVICE_TYPE_LEG_EP 0x1 -#define DEVICE_TYPE_RC 0x4 - -#define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104 -#define LTSSM_EN 0x1 - -#define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C -#define LINK_UP BIT(16) -#define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF - -#define EXP_CAP_ID_OFFSET 0x70 - -#define PCIECTRL_TI_CONF_INTX_ASSERT 0x0124 -#define PCIECTRL_TI_CONF_INTX_DEASSERT 0x0128 - -#define PCIECTRL_TI_CONF_MSI_XMT 0x012c -#define MSI_REQ_GRANT BIT(0) -#define MSI_VECTOR_SHIFT 7 - -struct dra7xx_pcie { - struct dw_pcie *pci; - void __iomem *base; /* DT ti_conf */ - int phy_count; /* DT phy-names count */ - struct phy **phy; - int link_gen; - struct irq_domain *irq_domain; - enum dw_pcie_device_mode mode; -}; - -struct dra7xx_pcie_of_data { - enum dw_pcie_device_mode mode; -}; - -#define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev) - -static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset) -{ - return readl(pcie->base + offset); -} - -static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, - u32 value) -{ - writel(value, pcie->base + offset); -} - -static u64 dra7xx_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) -{ - return pci_addr & DRA7XX_CPU_TO_BUS_ADDR; -} - -static int dra7xx_pcie_link_up(struct dw_pcie *pci) -{ - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); - u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); - - return !!(reg & LINK_UP); -} - -static void dra7xx_pcie_stop_link(struct dw_pcie *pci) -{ - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); - u32 reg; - - reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); - reg &= ~LTSSM_EN; - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); -} - -static int dra7xx_pcie_establish_link(struct dw_pcie *pci) -{ - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); - struct device *dev = pci->dev; - u32 reg; - u32 exp_cap_off = EXP_CAP_ID_OFFSET; - - if (dw_pcie_link_up(pci)) { - dev_err(dev, "link is already up\n"); - return 0; - } - - if (dra7xx->link_gen == 1) { - dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCAP, - 4, ®); - if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { - reg &= ~((u32)PCI_EXP_LNKCAP_SLS); - reg |= PCI_EXP_LNKCAP_SLS_2_5GB; - dw_pcie_write(pci->dbi_base + exp_cap_off + - PCI_EXP_LNKCAP, 4, reg); - } - - dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2, - 2, ®); - if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { - reg &= ~((u32)PCI_EXP_LNKCAP_SLS); - reg |= PCI_EXP_LNKCAP_SLS_2_5GB; - dw_pcie_write(pci->dbi_base + exp_cap_off + - PCI_EXP_LNKCTL2, 2, reg); - } - } - - reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); - reg |= LTSSM_EN; - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); - - return 0; -} - -static void dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie *dra7xx) -{ - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, - LEG_EP_INTERRUPTS | MSI); - - dra7xx_pcie_writel(dra7xx, - PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, - MSI | LEG_EP_INTERRUPTS); -} - -static void dra7xx_pcie_enable_wrapper_interrupts(struct dra7xx_pcie *dra7xx) -{ - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, - INTERRUPTS); - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, - INTERRUPTS); -} - -static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) -{ - dra7xx_pcie_enable_wrapper_interrupts(dra7xx); - dra7xx_pcie_enable_msi_interrupts(dra7xx); -} - -static int dra7xx_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); - - dw_pcie_setup_rc(pp); - - dra7xx_pcie_establish_link(pci); - dw_pcie_wait_for_link(pci); - dw_pcie_msi_init(pp); - dra7xx_pcie_enable_interrupts(dra7xx); - - return 0; -} - -static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = { - .host_init = dra7xx_pcie_host_init, -}; - -static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -static const struct irq_domain_ops intx_domain_ops = { - .map = dra7xx_pcie_intx_map, - .xlate = pci_irqd_intx_xlate, -}; - -static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct device *dev = pci->dev; - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); - struct device_node *node = dev->of_node; - struct device_node *pcie_intc_node = of_get_next_child(node, NULL); - - if (!pcie_intc_node) { - dev_err(dev, "No PCIe Intc node found\n"); - return -ENODEV; - } - - dra7xx->irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, - &intx_domain_ops, pp); - if (!dra7xx->irq_domain) { - dev_err(dev, "Failed to get a INTx IRQ domain\n"); - return -ENODEV; - } - - return 0; -} - -static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) -{ - struct dra7xx_pcie *dra7xx = arg; - struct dw_pcie *pci = dra7xx->pci; - struct pcie_port *pp = &pci->pp; - unsigned long reg; - u32 virq, bit; - - reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI); - - switch (reg) { - case MSI: - dw_handle_msi_irq(pp); - break; - case INTA: - case INTB: - case INTC: - case INTD: - for_each_set_bit(bit, ®, PCI_NUM_INTX) { - virq = irq_find_mapping(dra7xx->irq_domain, bit); - if (virq) - generic_handle_irq(virq); - } - break; - } - - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, reg); - - return IRQ_HANDLED; -} - -static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) -{ - struct dra7xx_pcie *dra7xx = arg; - struct dw_pcie *pci = dra7xx->pci; - struct device *dev = pci->dev; - struct dw_pcie_ep *ep = &pci->ep; - u32 reg; - - reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); - - if (reg & ERR_SYS) - dev_dbg(dev, "System Error\n"); - - if (reg & ERR_FATAL) - dev_dbg(dev, "Fatal Error\n"); - - if (reg & ERR_NONFATAL) - dev_dbg(dev, "Non Fatal Error\n"); - - if (reg & ERR_COR) - dev_dbg(dev, "Correctable Error\n"); - - if (reg & ERR_AXI) - dev_dbg(dev, "AXI tag lookup fatal Error\n"); - - if (reg & ERR_ECRC) - dev_dbg(dev, "ECRC Error\n"); - - if (reg & PME_TURN_OFF) - dev_dbg(dev, - "Power Management Event Turn-Off message received\n"); - - if (reg & PME_TO_ACK) - dev_dbg(dev, - "Power Management Turn-Off Ack message received\n"); - - if (reg & PM_PME) - dev_dbg(dev, "PM Power Management Event message received\n"); - - if (reg & LINK_REQ_RST) - dev_dbg(dev, "Link Request Reset\n"); - - if (reg & LINK_UP_EVT) { - if (dra7xx->mode == DW_PCIE_EP_TYPE) - dw_pcie_ep_linkup(ep); - dev_dbg(dev, "Link-up state change\n"); - } - - if (reg & CFG_BME_EVT) - dev_dbg(dev, "CFG 'Bus Master Enable' change\n"); - - if (reg & CFG_MSE_EVT) - dev_dbg(dev, "CFG 'Memory Space Enable' change\n"); - - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg); - - return IRQ_HANDLED; -} - -static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); - enum pci_barno bar; - - for (bar = BAR_0; bar <= BAR_5; bar++) - dw_pcie_ep_reset_bar(pci, bar); - - dra7xx_pcie_enable_wrapper_interrupts(dra7xx); -} - -static void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx) -{ - dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1); - mdelay(1); - dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_DEASSERT, 0x1); -} - -static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx, - u8 interrupt_num) -{ - u32 reg; - - reg = (interrupt_num - 1) << MSI_VECTOR_SHIFT; - reg |= MSI_REQ_GRANT; - dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_MSI_XMT, reg); -} - -static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); - - switch (type) { - case PCI_EPC_IRQ_LEGACY: - dra7xx_pcie_raise_legacy_irq(dra7xx); - break; - case PCI_EPC_IRQ_MSI: - dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num); - break; - default: - dev_err(pci->dev, "UNKNOWN IRQ type\n"); - } - - return 0; -} - -static struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = dra7xx_pcie_ep_init, - .raise_irq = dra7xx_pcie_raise_irq, -}; - -static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, - struct platform_device *pdev) -{ - int ret; - struct dw_pcie_ep *ep; - struct resource *res; - struct device *dev = &pdev->dev; - struct dw_pcie *pci = dra7xx->pci; - - ep = &pci->ep; - ep->ops = &pcie_ep_ops; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics"); - pci->dbi_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics2"); - pci->dbi_base2 = devm_ioremap_resource(dev, res); - if (IS_ERR(pci->dbi_base2)) - return PTR_ERR(pci->dbi_base2); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); - if (!res) - return -EINVAL; - - ep->phys_base = res->start; - ep->addr_size = resource_size(res); - - ret = dw_pcie_ep_init(ep); - if (ret) { - dev_err(dev, "failed to initialize endpoint\n"); - return ret; - } - - return 0; -} - -static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, - struct platform_device *pdev) -{ - int ret; - struct dw_pcie *pci = dra7xx->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = pci->dev; - struct resource *res; - - pp->irq = platform_get_irq(pdev, 1); - if (pp->irq < 0) { - dev_err(dev, "missing IRQ resource\n"); - return pp->irq; - } - - ret = devm_request_irq(dev, pp->irq, dra7xx_pcie_msi_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "dra7-pcie-msi", dra7xx); - if (ret) { - dev_err(dev, "failed to request irq\n"); - return ret; - } - - ret = dra7xx_pcie_init_irq_domain(pp); - if (ret < 0) - return ret; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics"); - pci->dbi_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - - pp->ops = &dra7xx_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .cpu_addr_fixup = dra7xx_pcie_cpu_addr_fixup, - .start_link = dra7xx_pcie_establish_link, - .stop_link = dra7xx_pcie_stop_link, - .link_up = dra7xx_pcie_link_up, -}; - -static void dra7xx_pcie_disable_phy(struct dra7xx_pcie *dra7xx) -{ - int phy_count = dra7xx->phy_count; - - while (phy_count--) { - phy_power_off(dra7xx->phy[phy_count]); - phy_exit(dra7xx->phy[phy_count]); - } -} - -static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx) -{ - int phy_count = dra7xx->phy_count; - int ret; - int i; - - for (i = 0; i < phy_count; i++) { - ret = phy_init(dra7xx->phy[i]); - if (ret < 0) - goto err_phy; - - ret = phy_power_on(dra7xx->phy[i]); - if (ret < 0) { - phy_exit(dra7xx->phy[i]); - goto err_phy; - } - } - - return 0; - -err_phy: - while (--i >= 0) { - phy_power_off(dra7xx->phy[i]); - phy_exit(dra7xx->phy[i]); - } - - return ret; -} - -static const struct dra7xx_pcie_of_data dra7xx_pcie_rc_of_data = { - .mode = DW_PCIE_RC_TYPE, -}; - -static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = { - .mode = DW_PCIE_EP_TYPE, -}; - -static const struct of_device_id of_dra7xx_pcie_match[] = { - { - .compatible = "ti,dra7-pcie", - .data = &dra7xx_pcie_rc_of_data, - }, - { - .compatible = "ti,dra7-pcie-ep", - .data = &dra7xx_pcie_ep_of_data, - }, - {}, -}; - -/* - * dra7xx_pcie_ep_unaligned_memaccess: workaround for AM572x/AM571x Errata i870 - * @dra7xx: the dra7xx device where the workaround should be applied - * - * Access to the PCIe slave port that are not 32-bit aligned will result - * in incorrect mapping to TLP Address and Byte enable fields. Therefore, - * byte and half-word accesses are not possible to byte offset 0x1, 0x2, or - * 0x3. - * - * To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1. - */ -static int dra7xx_pcie_ep_unaligned_memaccess(struct device *dev) -{ - int ret; - struct device_node *np = dev->of_node; - struct of_phandle_args args; - struct regmap *regmap; - - regmap = syscon_regmap_lookup_by_phandle(np, - "ti,syscon-unaligned-access"); - if (IS_ERR(regmap)) { - dev_dbg(dev, "can't get ti,syscon-unaligned-access\n"); - return -EINVAL; - } - - ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-unaligned-access", - 2, 0, &args); - if (ret) { - dev_err(dev, "failed to parse ti,syscon-unaligned-access\n"); - return ret; - } - - ret = regmap_update_bits(regmap, args.args[0], args.args[1], - args.args[1]); - if (ret) - dev_err(dev, "failed to enable unaligned access\n"); - - of_node_put(args.np); - - return ret; -} - -static int __init dra7xx_pcie_probe(struct platform_device *pdev) -{ - u32 reg; - int ret; - int irq; - int i; - int phy_count; - struct phy **phy; - struct device_link **link; - void __iomem *base; - struct resource *res; - struct dw_pcie *pci; - struct dra7xx_pcie *dra7xx; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - char name[10]; - struct gpio_desc *reset; - const struct of_device_id *match; - const struct dra7xx_pcie_of_data *data; - enum dw_pcie_device_mode mode; - - match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev); - if (!match) - return -EINVAL; - - data = (struct dra7xx_pcie_of_data *)match->data; - mode = (enum dw_pcie_device_mode)data->mode; - - dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); - if (!dra7xx) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "missing IRQ resource: %d\n", irq); - return irq; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf"); - base = devm_ioremap_nocache(dev, res->start, resource_size(res)); - if (!base) - return -ENOMEM; - - phy_count = of_property_count_strings(np, "phy-names"); - if (phy_count < 0) { - dev_err(dev, "unable to find the strings\n"); - return phy_count; - } - - phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL); - if (!phy) - return -ENOMEM; - - link = devm_kzalloc(dev, sizeof(*link) * phy_count, GFP_KERNEL); - if (!link) - return -ENOMEM; - - for (i = 0; i < phy_count; i++) { - snprintf(name, sizeof(name), "pcie-phy%d", i); - phy[i] = devm_phy_get(dev, name); - if (IS_ERR(phy[i])) - return PTR_ERR(phy[i]); - - link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS); - if (!link[i]) { - ret = -EINVAL; - goto err_link; - } - } - - dra7xx->base = base; - dra7xx->phy = phy; - dra7xx->pci = pci; - dra7xx->phy_count = phy_count; - - ret = dra7xx_pcie_enable_phy(dra7xx); - if (ret) { - dev_err(dev, "failed to enable phy\n"); - return ret; - } - - platform_set_drvdata(pdev, dra7xx); - - pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - dev_err(dev, "pm_runtime_get_sync failed\n"); - goto err_get_sync; - } - - reset = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); - if (IS_ERR(reset)) { - ret = PTR_ERR(reset); - dev_err(&pdev->dev, "gpio request failed, ret %d\n", ret); - goto err_gpio; - } - - reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); - reg &= ~LTSSM_EN; - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); - - dra7xx->link_gen = of_pci_get_max_link_speed(np); - if (dra7xx->link_gen < 0 || dra7xx->link_gen > 2) - dra7xx->link_gen = 2; - - switch (mode) { - case DW_PCIE_RC_TYPE: - if (!IS_ENABLED(CONFIG_PCI_DRA7XX_HOST)) { - ret = -ENODEV; - goto err_gpio; - } - - dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE, - DEVICE_TYPE_RC); - ret = dra7xx_add_pcie_port(dra7xx, pdev); - if (ret < 0) - goto err_gpio; - break; - case DW_PCIE_EP_TYPE: - if (!IS_ENABLED(CONFIG_PCI_DRA7XX_EP)) { - ret = -ENODEV; - goto err_gpio; - } - - dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE, - DEVICE_TYPE_EP); - - ret = dra7xx_pcie_ep_unaligned_memaccess(dev); - if (ret) - goto err_gpio; - - ret = dra7xx_add_pcie_ep(dra7xx, pdev); - if (ret < 0) - goto err_gpio; - break; - default: - dev_err(dev, "INVALID device type %d\n", mode); - } - dra7xx->mode = mode; - - ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler, - IRQF_SHARED, "dra7xx-pcie-main", dra7xx); - if (ret) { - dev_err(dev, "failed to request irq\n"); - goto err_gpio; - } - - return 0; - -err_gpio: - pm_runtime_put(dev); - -err_get_sync: - pm_runtime_disable(dev); - dra7xx_pcie_disable_phy(dra7xx); - -err_link: - while (--i >= 0) - device_link_del(link[i]); - - return ret; -} - -#ifdef CONFIG_PM_SLEEP -static int dra7xx_pcie_suspend(struct device *dev) -{ - struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - struct dw_pcie *pci = dra7xx->pci; - u32 val; - - if (dra7xx->mode != DW_PCIE_RC_TYPE) - return 0; - - /* clear MSE */ - val = dw_pcie_readl_dbi(pci, PCI_COMMAND); - val &= ~PCI_COMMAND_MEMORY; - dw_pcie_writel_dbi(pci, PCI_COMMAND, val); - - return 0; -} - -static int dra7xx_pcie_resume(struct device *dev) -{ - struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - struct dw_pcie *pci = dra7xx->pci; - u32 val; - - if (dra7xx->mode != DW_PCIE_RC_TYPE) - return 0; - - /* set MSE */ - val = dw_pcie_readl_dbi(pci, PCI_COMMAND); - val |= PCI_COMMAND_MEMORY; - dw_pcie_writel_dbi(pci, PCI_COMMAND, val); - - return 0; -} - -static int dra7xx_pcie_suspend_noirq(struct device *dev) -{ - struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - - dra7xx_pcie_disable_phy(dra7xx); - - return 0; -} - -static int dra7xx_pcie_resume_noirq(struct device *dev) -{ - struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - int ret; - - ret = dra7xx_pcie_enable_phy(dra7xx); - if (ret) { - dev_err(dev, "failed to enable phy\n"); - return ret; - } - - return 0; -} -#endif - -static void dra7xx_pcie_shutdown(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - int ret; - - dra7xx_pcie_stop_link(dra7xx->pci); - - ret = pm_runtime_put_sync(dev); - if (ret < 0) - dev_dbg(dev, "pm_runtime_put_sync failed\n"); - - pm_runtime_disable(dev); - dra7xx_pcie_disable_phy(dra7xx); -} - -static const struct dev_pm_ops dra7xx_pcie_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend, dra7xx_pcie_resume) - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend_noirq, - dra7xx_pcie_resume_noirq) -}; - -static struct platform_driver dra7xx_pcie_driver = { - .driver = { - .name = "dra7-pcie", - .of_match_table = of_dra7xx_pcie_match, - .suppress_bind_attrs = true, - .pm = &dra7xx_pcie_pm_ops, - }, - .shutdown = dra7xx_pcie_shutdown, -}; -builtin_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe); diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c deleted file mode 100644 index 4cc1e5df8c79..000000000000 --- a/drivers/pci/dwc/pci-exynos.c +++ /dev/null @@ -1,539 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for Samsung EXYNOS SoCs - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Jingoo Han - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -#define to_exynos_pcie(x) dev_get_drvdata((x)->dev) - -/* PCIe ELBI registers */ -#define PCIE_IRQ_PULSE 0x000 -#define IRQ_INTA_ASSERT BIT(0) -#define IRQ_INTB_ASSERT BIT(2) -#define IRQ_INTC_ASSERT BIT(4) -#define IRQ_INTD_ASSERT BIT(6) -#define PCIE_IRQ_LEVEL 0x004 -#define PCIE_IRQ_SPECIAL 0x008 -#define PCIE_IRQ_EN_PULSE 0x00c -#define PCIE_IRQ_EN_LEVEL 0x010 -#define IRQ_MSI_ENABLE BIT(2) -#define PCIE_IRQ_EN_SPECIAL 0x014 -#define PCIE_PWR_RESET 0x018 -#define PCIE_CORE_RESET 0x01c -#define PCIE_CORE_RESET_ENABLE BIT(0) -#define PCIE_STICKY_RESET 0x020 -#define PCIE_NONSTICKY_RESET 0x024 -#define PCIE_APP_INIT_RESET 0x028 -#define PCIE_APP_LTSSM_ENABLE 0x02c -#define PCIE_ELBI_RDLH_LINKUP 0x064 -#define PCIE_ELBI_LTSSM_ENABLE 0x1 -#define PCIE_ELBI_SLV_AWMISC 0x11c -#define PCIE_ELBI_SLV_ARMISC 0x120 -#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21) - -struct exynos_pcie_mem_res { - void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */ -}; - -struct exynos_pcie_clk_res { - struct clk *clk; - struct clk *bus_clk; -}; - -struct exynos_pcie { - struct dw_pcie *pci; - struct exynos_pcie_mem_res *mem_res; - struct exynos_pcie_clk_res *clk_res; - const struct exynos_pcie_ops *ops; - int reset_gpio; - - struct phy *phy; -}; - -struct exynos_pcie_ops { - int (*get_mem_resources)(struct platform_device *pdev, - struct exynos_pcie *ep); - int (*get_clk_resources)(struct exynos_pcie *ep); - int (*init_clk_resources)(struct exynos_pcie *ep); - void (*deinit_clk_resources)(struct exynos_pcie *ep); -}; - -static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev, - struct exynos_pcie *ep) -{ - struct dw_pcie *pci = ep->pci; - struct device *dev = pci->dev; - struct resource *res; - - ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL); - if (!ep->mem_res) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ep->mem_res->elbi_base = devm_ioremap_resource(dev, res); - if (IS_ERR(ep->mem_res->elbi_base)) - return PTR_ERR(ep->mem_res->elbi_base); - - return 0; -} - -static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep) -{ - struct dw_pcie *pci = ep->pci; - struct device *dev = pci->dev; - - ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL); - if (!ep->clk_res) - return -ENOMEM; - - ep->clk_res->clk = devm_clk_get(dev, "pcie"); - if (IS_ERR(ep->clk_res->clk)) { - dev_err(dev, "Failed to get pcie rc clock\n"); - return PTR_ERR(ep->clk_res->clk); - } - - ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus"); - if (IS_ERR(ep->clk_res->bus_clk)) { - dev_err(dev, "Failed to get pcie bus clock\n"); - return PTR_ERR(ep->clk_res->bus_clk); - } - - return 0; -} - -static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep) -{ - struct dw_pcie *pci = ep->pci; - struct device *dev = pci->dev; - int ret; - - ret = clk_prepare_enable(ep->clk_res->clk); - if (ret) { - dev_err(dev, "cannot enable pcie rc clock"); - return ret; - } - - ret = clk_prepare_enable(ep->clk_res->bus_clk); - if (ret) { - dev_err(dev, "cannot enable pcie bus clock"); - goto err_bus_clk; - } - - return 0; - -err_bus_clk: - clk_disable_unprepare(ep->clk_res->clk); - - return ret; -} - -static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep) -{ - clk_disable_unprepare(ep->clk_res->bus_clk); - clk_disable_unprepare(ep->clk_res->clk); -} - -static const struct exynos_pcie_ops exynos5440_pcie_ops = { - .get_mem_resources = exynos5440_pcie_get_mem_resources, - .get_clk_resources = exynos5440_pcie_get_clk_resources, - .init_clk_resources = exynos5440_pcie_init_clk_resources, - .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources, -}; - -static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg) -{ - writel(val, base + reg); -} - -static u32 exynos_pcie_readl(void __iomem *base, u32 reg) -{ - return readl(base + reg); -} - -static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on) -{ - u32 val; - - val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC); - if (on) - val |= PCIE_ELBI_SLV_DBI_ENABLE; - else - val &= ~PCIE_ELBI_SLV_DBI_ENABLE; - exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC); -} - -static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on) -{ - u32 val; - - val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC); - if (on) - val |= PCIE_ELBI_SLV_DBI_ENABLE; - else - val &= ~PCIE_ELBI_SLV_DBI_ENABLE; - exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC); -} - -static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep) -{ - u32 val; - - val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET); - val &= ~PCIE_CORE_RESET_ENABLE; - exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET); - exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET); - exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET); - exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET); -} - -static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep) -{ - u32 val; - - val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET); - val |= PCIE_CORE_RESET_ENABLE; - - exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET); - exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET); - exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET); - exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET); - exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET); -} - -static void exynos_pcie_assert_reset(struct exynos_pcie *ep) -{ - struct dw_pcie *pci = ep->pci; - struct device *dev = pci->dev; - - if (ep->reset_gpio >= 0) - devm_gpio_request_one(dev, ep->reset_gpio, - GPIOF_OUT_INIT_HIGH, "RESET"); -} - -static int exynos_pcie_establish_link(struct exynos_pcie *ep) -{ - struct dw_pcie *pci = ep->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = pci->dev; - - if (dw_pcie_link_up(pci)) { - dev_err(dev, "Link already up\n"); - return 0; - } - - exynos_pcie_assert_core_reset(ep); - - phy_reset(ep->phy); - - exynos_pcie_writel(ep->mem_res->elbi_base, 1, - PCIE_PWR_RESET); - - phy_power_on(ep->phy); - phy_init(ep->phy); - - exynos_pcie_deassert_core_reset(ep); - dw_pcie_setup_rc(pp); - exynos_pcie_assert_reset(ep); - - /* assert LTSSM enable */ - exynos_pcie_writel(ep->mem_res->elbi_base, PCIE_ELBI_LTSSM_ENABLE, - PCIE_APP_LTSSM_ENABLE); - - /* check if the link is up or not */ - if (!dw_pcie_wait_for_link(pci)) - return 0; - - phy_power_off(ep->phy); - return -ETIMEDOUT; -} - -static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep) -{ - u32 val; - - val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE); - exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE); -} - -static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep) -{ - u32 val; - - /* enable INTX interrupt */ - val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | - IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; - exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE); -} - -static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) -{ - struct exynos_pcie *ep = arg; - - exynos_pcie_clear_irq_pulse(ep); - return IRQ_HANDLED; -} - -static void exynos_pcie_msi_init(struct exynos_pcie *ep) -{ - struct dw_pcie *pci = ep->pci; - struct pcie_port *pp = &pci->pp; - u32 val; - - dw_pcie_msi_init(pp); - - /* enable MSI interrupt */ - val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL); - val |= IRQ_MSI_ENABLE; - exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL); -} - -static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep) -{ - exynos_pcie_enable_irq_pulse(ep); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - exynos_pcie_msi_init(ep); -} - -static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, - u32 reg, size_t size) -{ - struct exynos_pcie *ep = to_exynos_pcie(pci); - u32 val; - - exynos_pcie_sideband_dbi_r_mode(ep, true); - dw_pcie_read(base + reg, size, &val); - exynos_pcie_sideband_dbi_r_mode(ep, false); - return val; -} - -static void exynos_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, - u32 reg, size_t size, u32 val) -{ - struct exynos_pcie *ep = to_exynos_pcie(pci); - - exynos_pcie_sideband_dbi_w_mode(ep, true); - dw_pcie_write(base + reg, size, val); - exynos_pcie_sideband_dbi_w_mode(ep, false); -} - -static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct exynos_pcie *ep = to_exynos_pcie(pci); - int ret; - - exynos_pcie_sideband_dbi_r_mode(ep, true); - ret = dw_pcie_read(pci->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_r_mode(ep, false); - return ret; -} - -static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct exynos_pcie *ep = to_exynos_pcie(pci); - int ret; - - exynos_pcie_sideband_dbi_w_mode(ep, true); - ret = dw_pcie_write(pci->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_w_mode(ep, false); - return ret; -} - -static int exynos_pcie_link_up(struct dw_pcie *pci) -{ - struct exynos_pcie *ep = to_exynos_pcie(pci); - u32 val; - - val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_RDLH_LINKUP); - if (val == PCIE_ELBI_LTSSM_ENABLE) - return 1; - - return 0; -} - -static int exynos_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct exynos_pcie *ep = to_exynos_pcie(pci); - - exynos_pcie_establish_link(ep); - exynos_pcie_enable_interrupts(ep); - - return 0; -} - -static const struct dw_pcie_host_ops exynos_pcie_host_ops = { - .rd_own_conf = exynos_pcie_rd_own_conf, - .wr_own_conf = exynos_pcie_wr_own_conf, - .host_init = exynos_pcie_host_init, -}; - -static int __init exynos_add_pcie_port(struct exynos_pcie *ep, - struct platform_device *pdev) -{ - struct dw_pcie *pci = ep->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = &pdev->dev; - int ret; - - pp->irq = platform_get_irq(pdev, 1); - if (pp->irq < 0) { - dev_err(dev, "failed to get irq\n"); - return pp->irq; - } - ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler, - IRQF_SHARED, "exynos-pcie", ep); - if (ret) { - dev_err(dev, "failed to request irq\n"); - return ret; - } - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->msi_irq = platform_get_irq(pdev, 0); - if (pp->msi_irq < 0) { - dev_err(dev, "failed to get msi irq\n"); - return pp->msi_irq; - } - } - - pp->root_bus_nr = -1; - pp->ops = &exynos_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .read_dbi = exynos_pcie_read_dbi, - .write_dbi = exynos_pcie_write_dbi, - .link_up = exynos_pcie_link_up, -}; - -static int __init exynos_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_pcie *pci; - struct exynos_pcie *ep; - struct device_node *np = dev->of_node; - int ret; - - ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); - if (!ep) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - ep->pci = pci; - ep->ops = (const struct exynos_pcie_ops *) - of_device_get_match_data(dev); - - ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); - - ep->phy = devm_of_phy_get(dev, np, NULL); - if (IS_ERR(ep->phy)) { - if (PTR_ERR(ep->phy) == -EPROBE_DEFER) - return PTR_ERR(ep->phy); - - ep->phy = NULL; - } - - if (ep->ops && ep->ops->get_mem_resources) { - ret = ep->ops->get_mem_resources(pdev, ep); - if (ret) - return ret; - } - - if (ep->ops && ep->ops->get_clk_resources && - ep->ops->init_clk_resources) { - ret = ep->ops->get_clk_resources(ep); - if (ret) - return ret; - ret = ep->ops->init_clk_resources(ep); - if (ret) - return ret; - } - - platform_set_drvdata(pdev, ep); - - ret = exynos_add_pcie_port(ep, pdev); - if (ret < 0) - goto fail_probe; - - return 0; - -fail_probe: - phy_exit(ep->phy); - - if (ep->ops && ep->ops->deinit_clk_resources) - ep->ops->deinit_clk_resources(ep); - return ret; -} - -static int __exit exynos_pcie_remove(struct platform_device *pdev) -{ - struct exynos_pcie *ep = platform_get_drvdata(pdev); - - if (ep->ops && ep->ops->deinit_clk_resources) - ep->ops->deinit_clk_resources(ep); - - return 0; -} - -static const struct of_device_id exynos_pcie_of_match[] = { - { - .compatible = "samsung,exynos5440-pcie", - .data = &exynos5440_pcie_ops - }, - {}, -}; - -static struct platform_driver exynos_pcie_driver = { - .remove = __exit_p(exynos_pcie_remove), - .driver = { - .name = "exynos-pcie", - .of_match_table = exynos_pcie_of_match, - }, -}; - -/* Exynos PCIe driver does not allow module unload */ - -static int __init exynos_pcie_init(void) -{ - return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe); -} -subsys_initcall(exynos_pcie_init); diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c deleted file mode 100644 index 80f604602783..000000000000 --- a/drivers/pci/dwc/pci-imx6.c +++ /dev/null @@ -1,871 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for Freescale i.MX6 SoCs - * - * Copyright (C) 2013 Kosagi - * http://www.kosagi.com - * - * Author: Sean Cross - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -#define to_imx6_pcie(x) dev_get_drvdata((x)->dev) - -enum imx6_pcie_variants { - IMX6Q, - IMX6SX, - IMX6QP, - IMX7D, -}; - -struct imx6_pcie { - struct dw_pcie *pci; - int reset_gpio; - bool gpio_active_high; - struct clk *pcie_bus; - struct clk *pcie_phy; - struct clk *pcie_inbound_axi; - struct clk *pcie; - struct regmap *iomuxc_gpr; - struct reset_control *pciephy_reset; - struct reset_control *apps_reset; - enum imx6_pcie_variants variant; - u32 tx_deemph_gen1; - u32 tx_deemph_gen2_3p5db; - u32 tx_deemph_gen2_6db; - u32 tx_swing_full; - u32 tx_swing_low; - int link_gen; - struct regulator *vpcie; -}; - -/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ -#define PHY_PLL_LOCK_WAIT_MAX_RETRIES 2000 -#define PHY_PLL_LOCK_WAIT_USLEEP_MIN 50 -#define PHY_PLL_LOCK_WAIT_USLEEP_MAX 200 - -/* PCIe Root Complex registers (memory-mapped) */ -#define PCIE_RC_LCR 0x7c -#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1 -#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2 -#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf - -#define PCIE_RC_LCSR 0x80 - -/* PCIe Port Logic registers (memory-mapped) */ -#define PL_OFFSET 0x700 -#define PCIE_PL_PFLR (PL_OFFSET + 0x08) -#define PCIE_PL_PFLR_LINK_STATE_MASK (0x3f << 16) -#define PCIE_PL_PFLR_FORCE_LINK (1 << 15) -#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) -#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) -#define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29) -#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP (1 << 4) - -#define PCIE_PHY_CTRL (PL_OFFSET + 0x114) -#define PCIE_PHY_CTRL_DATA_LOC 0 -#define PCIE_PHY_CTRL_CAP_ADR_LOC 16 -#define PCIE_PHY_CTRL_CAP_DAT_LOC 17 -#define PCIE_PHY_CTRL_WR_LOC 18 -#define PCIE_PHY_CTRL_RD_LOC 19 - -#define PCIE_PHY_STAT (PL_OFFSET + 0x110) -#define PCIE_PHY_STAT_ACK_LOC 16 - -#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C -#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) - -/* PHY registers (not memory-mapped) */ -#define PCIE_PHY_RX_ASIC_OUT 0x100D -#define PCIE_PHY_RX_ASIC_OUT_VALID (1 << 0) - -#define PHY_RX_OVRD_IN_LO 0x1005 -#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) -#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) - -static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val) -{ - struct dw_pcie *pci = imx6_pcie->pci; - u32 val; - u32 max_iterations = 10; - u32 wait_counter = 0; - - do { - val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); - val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; - wait_counter++; - - if (val == exp_val) - return 0; - - udelay(1); - } while (wait_counter < max_iterations); - - return -ETIMEDOUT; -} - -static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr) -{ - struct dw_pcie *pci = imx6_pcie->pci; - u32 val; - int ret; - - val = addr << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); - - val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); - - ret = pcie_phy_poll_ack(imx6_pcie, 1); - if (ret) - return ret; - - val = addr << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); - - return pcie_phy_poll_ack(imx6_pcie, 0); -} - -/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ -static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data) -{ - struct dw_pcie *pci = imx6_pcie->pci; - u32 val, phy_ctl; - int ret; - - ret = pcie_phy_wait_ack(imx6_pcie, addr); - if (ret) - return ret; - - /* assert Read signal */ - phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, phy_ctl); - - ret = pcie_phy_poll_ack(imx6_pcie, 1); - if (ret) - return ret; - - val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); - *data = val & 0xffff; - - /* deassert Read signal */ - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x00); - - return pcie_phy_poll_ack(imx6_pcie, 0); -} - -static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) -{ - struct dw_pcie *pci = imx6_pcie->pci; - u32 var; - int ret; - - /* write addr */ - /* cap addr */ - ret = pcie_phy_wait_ack(imx6_pcie, addr); - if (ret) - return ret; - - var = data << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); - - /* capture data */ - var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); - - ret = pcie_phy_poll_ack(imx6_pcie, 1); - if (ret) - return ret; - - /* deassert cap data */ - var = data << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); - - /* wait for ack de-assertion */ - ret = pcie_phy_poll_ack(imx6_pcie, 0); - if (ret) - return ret; - - /* assert wr signal */ - var = 0x1 << PCIE_PHY_CTRL_WR_LOC; - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); - - /* wait for ack */ - ret = pcie_phy_poll_ack(imx6_pcie, 1); - if (ret) - return ret; - - /* deassert wr signal */ - var = data << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); - - /* wait for ack de-assertion */ - ret = pcie_phy_poll_ack(imx6_pcie, 0); - if (ret) - return ret; - - dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x0); - - return 0; -} - -static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) -{ - u32 tmp; - - pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); - tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | - PHY_RX_OVRD_IN_LO_RX_PLL_EN); - pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); - - usleep_range(2000, 3000); - - pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); - tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | - PHY_RX_OVRD_IN_LO_RX_PLL_EN); - pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); -} - -/* Added for PCI abort handling */ -static int imx6q_pcie_abort_handler(unsigned long addr, - unsigned int fsr, struct pt_regs *regs) -{ - unsigned long pc = instruction_pointer(regs); - unsigned long instr = *(unsigned long *)pc; - int reg = (instr >> 12) & 15; - - /* - * If the instruction being executed was a read, - * make it look like it read all-ones. - */ - if ((instr & 0x0c100000) == 0x04100000) { - unsigned long val; - - if (instr & 0x00400000) - val = 255; - else - val = -1; - - regs->uregs[reg] = val; - regs->ARM_pc += 4; - return 0; - } - - if ((instr & 0x0e100090) == 0x00100090) { - regs->uregs[reg] = -1; - regs->ARM_pc += 4; - return 0; - } - - return 1; -} - -static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) -{ - struct device *dev = imx6_pcie->pci->dev; - - switch (imx6_pcie->variant) { - case IMX7D: - reset_control_assert(imx6_pcie->pciephy_reset); - reset_control_assert(imx6_pcie->apps_reset); - break; - case IMX6SX: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6SX_GPR12_PCIE_TEST_POWERDOWN, - IMX6SX_GPR12_PCIE_TEST_POWERDOWN); - /* Force PCIe PHY reset */ - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, - IMX6SX_GPR5_PCIE_BTNRST_RESET, - IMX6SX_GPR5_PCIE_BTNRST_RESET); - break; - case IMX6QP: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_SW_RST, - IMX6Q_GPR1_PCIE_SW_RST); - break; - case IMX6Q: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); - break; - } - - if (imx6_pcie->vpcie && regulator_is_enabled(imx6_pcie->vpcie) > 0) { - int ret = regulator_disable(imx6_pcie->vpcie); - - if (ret) - dev_err(dev, "failed to disable vpcie regulator: %d\n", - ret); - } -} - -static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) -{ - struct dw_pcie *pci = imx6_pcie->pci; - struct device *dev = pci->dev; - int ret = 0; - - switch (imx6_pcie->variant) { - case IMX6SX: - ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi); - if (ret) { - dev_err(dev, "unable to enable pcie_axi clock\n"); - break; - } - - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0); - break; - case IMX6QP: /* FALLTHROUGH */ - case IMX6Q: - /* power up core phy and enable ref clock */ - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18); - /* - * the async reset input need ref clock to sync internally, - * when the ref clock comes after reset, internal synced - * reset time is too short, cannot meet the requirement. - * add one ~10us delay here. - */ - udelay(10); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); - break; - case IMX7D: - break; - } - - return ret; -} - -static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) -{ - u32 val; - unsigned int retries; - struct device *dev = imx6_pcie->pci->dev; - - for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) { - regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR22, &val); - - if (val & IMX7D_GPR22_PCIE_PHY_PLL_LOCKED) - return; - - usleep_range(PHY_PLL_LOCK_WAIT_USLEEP_MIN, - PHY_PLL_LOCK_WAIT_USLEEP_MAX); - } - - dev_err(dev, "PCIe PLL lock timeout\n"); -} - -static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) -{ - struct dw_pcie *pci = imx6_pcie->pci; - struct device *dev = pci->dev; - int ret; - - if (imx6_pcie->vpcie && !regulator_is_enabled(imx6_pcie->vpcie)) { - ret = regulator_enable(imx6_pcie->vpcie); - if (ret) { - dev_err(dev, "failed to enable vpcie regulator: %d\n", - ret); - return; - } - } - - ret = clk_prepare_enable(imx6_pcie->pcie_phy); - if (ret) { - dev_err(dev, "unable to enable pcie_phy clock\n"); - goto err_pcie_phy; - } - - ret = clk_prepare_enable(imx6_pcie->pcie_bus); - if (ret) { - dev_err(dev, "unable to enable pcie_bus clock\n"); - goto err_pcie_bus; - } - - ret = clk_prepare_enable(imx6_pcie->pcie); - if (ret) { - dev_err(dev, "unable to enable pcie clock\n"); - goto err_pcie; - } - - ret = imx6_pcie_enable_ref_clk(imx6_pcie); - if (ret) { - dev_err(dev, "unable to enable pcie ref clock\n"); - goto err_ref_clk; - } - - /* allow the clocks to stabilize */ - usleep_range(200, 500); - - /* Some boards don't have PCIe reset GPIO. */ - if (gpio_is_valid(imx6_pcie->reset_gpio)) { - gpio_set_value_cansleep(imx6_pcie->reset_gpio, - imx6_pcie->gpio_active_high); - msleep(100); - gpio_set_value_cansleep(imx6_pcie->reset_gpio, - !imx6_pcie->gpio_active_high); - } - - switch (imx6_pcie->variant) { - case IMX7D: - reset_control_deassert(imx6_pcie->pciephy_reset); - imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); - break; - case IMX6SX: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, - IMX6SX_GPR5_PCIE_BTNRST_RESET, 0); - break; - case IMX6QP: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_SW_RST, 0); - - usleep_range(200, 500); - break; - case IMX6Q: /* Nothing to do */ - break; - } - - return; - -err_ref_clk: - clk_disable_unprepare(imx6_pcie->pcie); -err_pcie: - clk_disable_unprepare(imx6_pcie->pcie_bus); -err_pcie_bus: - clk_disable_unprepare(imx6_pcie->pcie_phy); -err_pcie_phy: - if (imx6_pcie->vpcie && regulator_is_enabled(imx6_pcie->vpcie) > 0) { - ret = regulator_disable(imx6_pcie->vpcie); - if (ret) - dev_err(dev, "failed to disable vpcie regulator: %d\n", - ret); - } -} - -static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) -{ - switch (imx6_pcie->variant) { - case IMX7D: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); - break; - case IMX6SX: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6SX_GPR12_PCIE_RX_EQ_MASK, - IMX6SX_GPR12_PCIE_RX_EQ_2); - /* FALLTHROUGH */ - default: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); - - /* configure constant input signal to the pcie ctrl and phy */ - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_LOS_LEVEL, 9 << 4); - - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN1, - imx6_pcie->tx_deemph_gen1 << 0); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, - imx6_pcie->tx_deemph_gen2_3p5db << 6); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, - imx6_pcie->tx_deemph_gen2_6db << 12); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_SWING_FULL, - imx6_pcie->tx_swing_full << 18); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_SWING_LOW, - imx6_pcie->tx_swing_low << 25); - break; - } - - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12); -} - -static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) -{ - struct dw_pcie *pci = imx6_pcie->pci; - struct device *dev = pci->dev; - - /* check if the link is up or not */ - if (!dw_pcie_wait_for_link(pci)) - return 0; - - dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", - dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), - dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); - return -ETIMEDOUT; -} - -static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) -{ - struct dw_pcie *pci = imx6_pcie->pci; - struct device *dev = pci->dev; - u32 tmp; - unsigned int retries; - - for (retries = 0; retries < 200; retries++) { - tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - /* Test if the speed change finished. */ - if (!(tmp & PORT_LOGIC_SPEED_CHANGE)) - return 0; - usleep_range(100, 1000); - } - - dev_err(dev, "Speed change timeout\n"); - return -EINVAL; -} - -static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) -{ - struct dw_pcie *pci = imx6_pcie->pci; - struct device *dev = pci->dev; - u32 tmp; - int ret; - - /* - * Force Gen1 operation when starting the link. In case the link is - * started in Gen2 mode, there is a possibility the devices on the - * bus will not be detected at all. This happens with PCIe switches. - */ - tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); - tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; - tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; - dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); - - /* Start LTSSM. */ - if (imx6_pcie->variant == IMX7D) - reset_control_deassert(imx6_pcie->apps_reset); - else - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); - - ret = imx6_pcie_wait_for_link(imx6_pcie); - if (ret) - goto err_reset_phy; - - if (imx6_pcie->link_gen == 2) { - /* Allow Gen2 mode after the link is up. */ - tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); - tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; - tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; - dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); - - /* - * Start Directed Speed Change so the best possible - * speed both link partners support can be negotiated. - */ - tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - tmp |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); - - if (imx6_pcie->variant != IMX7D) { - /* - * On i.MX7, DIRECT_SPEED_CHANGE behaves differently - * from i.MX6 family when no link speed transition - * occurs and we go Gen1 -> yep, Gen1. The difference - * is that, in such case, it will not be cleared by HW - * which will cause the following code to report false - * failure. - */ - - ret = imx6_pcie_wait_for_speed_change(imx6_pcie); - if (ret) { - dev_err(dev, "Failed to bring link up!\n"); - goto err_reset_phy; - } - } - - /* Make sure link training is finished as well! */ - ret = imx6_pcie_wait_for_link(imx6_pcie); - if (ret) { - dev_err(dev, "Failed to bring link up!\n"); - goto err_reset_phy; - } - } else { - dev_info(dev, "Link: Gen2 disabled\n"); - } - - tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR); - dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf); - return 0; - -err_reset_phy: - dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n", - dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), - dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); - imx6_pcie_reset_phy(imx6_pcie); - return ret; -} - -static int imx6_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); - - imx6_pcie_assert_core_reset(imx6_pcie); - imx6_pcie_init_phy(imx6_pcie); - imx6_pcie_deassert_core_reset(imx6_pcie); - dw_pcie_setup_rc(pp); - imx6_pcie_establish_link(imx6_pcie); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - dw_pcie_msi_init(pp); - - return 0; -} - -static int imx6_pcie_link_up(struct dw_pcie *pci) -{ - return dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1) & - PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; -} - -static const struct dw_pcie_host_ops imx6_pcie_host_ops = { - .host_init = imx6_pcie_host_init, -}; - -static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, - struct platform_device *pdev) -{ - struct dw_pcie *pci = imx6_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = &pdev->dev; - int ret; - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->msi_irq = platform_get_irq_byname(pdev, "msi"); - if (pp->msi_irq <= 0) { - dev_err(dev, "failed to get MSI irq\n"); - return -ENODEV; - } - } - - pp->root_bus_nr = -1; - pp->ops = &imx6_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .link_up = imx6_pcie_link_up, -}; - -static int imx6_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_pcie *pci; - struct imx6_pcie *imx6_pcie; - struct resource *dbi_base; - struct device_node *node = dev->of_node; - int ret; - - imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL); - if (!imx6_pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - imx6_pcie->pci = pci; - imx6_pcie->variant = - (enum imx6_pcie_variants)of_device_get_match_data(dev); - - dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pci->dbi_base = devm_ioremap_resource(dev, dbi_base); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - - /* Fetch GPIOs */ - imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0); - imx6_pcie->gpio_active_high = of_property_read_bool(node, - "reset-gpio-active-high"); - if (gpio_is_valid(imx6_pcie->reset_gpio)) { - ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio, - imx6_pcie->gpio_active_high ? - GPIOF_OUT_INIT_HIGH : - GPIOF_OUT_INIT_LOW, - "PCIe reset"); - if (ret) { - dev_err(dev, "unable to get reset gpio\n"); - return ret; - } - } else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) { - return imx6_pcie->reset_gpio; - } - - /* Fetch clocks */ - imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy"); - if (IS_ERR(imx6_pcie->pcie_phy)) { - dev_err(dev, "pcie_phy clock source missing or invalid\n"); - return PTR_ERR(imx6_pcie->pcie_phy); - } - - imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus"); - if (IS_ERR(imx6_pcie->pcie_bus)) { - dev_err(dev, "pcie_bus clock source missing or invalid\n"); - return PTR_ERR(imx6_pcie->pcie_bus); - } - - imx6_pcie->pcie = devm_clk_get(dev, "pcie"); - if (IS_ERR(imx6_pcie->pcie)) { - dev_err(dev, "pcie clock source missing or invalid\n"); - return PTR_ERR(imx6_pcie->pcie); - } - - switch (imx6_pcie->variant) { - case IMX6SX: - imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, - "pcie_inbound_axi"); - if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { - dev_err(dev, "pcie_inbound_axi clock missing or invalid\n"); - return PTR_ERR(imx6_pcie->pcie_inbound_axi); - } - break; - case IMX7D: - imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev, - "pciephy"); - if (IS_ERR(imx6_pcie->pciephy_reset)) { - dev_err(dev, "Failed to get PCIEPHY reset control\n"); - return PTR_ERR(imx6_pcie->pciephy_reset); - } - - imx6_pcie->apps_reset = devm_reset_control_get_exclusive(dev, - "apps"); - if (IS_ERR(imx6_pcie->apps_reset)) { - dev_err(dev, "Failed to get PCIE APPS reset control\n"); - return PTR_ERR(imx6_pcie->apps_reset); - } - break; - default: - break; - } - - /* Grab GPR config register range */ - imx6_pcie->iomuxc_gpr = - syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); - if (IS_ERR(imx6_pcie->iomuxc_gpr)) { - dev_err(dev, "unable to find iomuxc registers\n"); - return PTR_ERR(imx6_pcie->iomuxc_gpr); - } - - /* Grab PCIe PHY Tx Settings */ - if (of_property_read_u32(node, "fsl,tx-deemph-gen1", - &imx6_pcie->tx_deemph_gen1)) - imx6_pcie->tx_deemph_gen1 = 0; - - if (of_property_read_u32(node, "fsl,tx-deemph-gen2-3p5db", - &imx6_pcie->tx_deemph_gen2_3p5db)) - imx6_pcie->tx_deemph_gen2_3p5db = 0; - - if (of_property_read_u32(node, "fsl,tx-deemph-gen2-6db", - &imx6_pcie->tx_deemph_gen2_6db)) - imx6_pcie->tx_deemph_gen2_6db = 20; - - if (of_property_read_u32(node, "fsl,tx-swing-full", - &imx6_pcie->tx_swing_full)) - imx6_pcie->tx_swing_full = 127; - - if (of_property_read_u32(node, "fsl,tx-swing-low", - &imx6_pcie->tx_swing_low)) - imx6_pcie->tx_swing_low = 127; - - /* Limit link speed */ - ret = of_property_read_u32(node, "fsl,max-link-speed", - &imx6_pcie->link_gen); - if (ret) - imx6_pcie->link_gen = 1; - - imx6_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie"); - if (IS_ERR(imx6_pcie->vpcie)) { - if (PTR_ERR(imx6_pcie->vpcie) == -EPROBE_DEFER) - return -EPROBE_DEFER; - imx6_pcie->vpcie = NULL; - } - - platform_set_drvdata(pdev, imx6_pcie); - - ret = imx6_add_pcie_port(imx6_pcie, pdev); - if (ret < 0) - return ret; - - return 0; -} - -static void imx6_pcie_shutdown(struct platform_device *pdev) -{ - struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev); - - /* bring down link, so bootloader gets clean state in case of reboot */ - imx6_pcie_assert_core_reset(imx6_pcie); -} - -static const struct of_device_id imx6_pcie_of_match[] = { - { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, }, - { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, }, - { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, - { .compatible = "fsl,imx7d-pcie", .data = (void *)IMX7D, }, - {}, -}; - -static struct platform_driver imx6_pcie_driver = { - .driver = { - .name = "imx6q-pcie", - .of_match_table = imx6_pcie_of_match, - .suppress_bind_attrs = true, - }, - .probe = imx6_pcie_probe, - .shutdown = imx6_pcie_shutdown, -}; - -static int __init imx6_pcie_init(void) -{ - /* - * Since probe() can be deferred we need to make sure that - * hook_fault_code is not called after __init memory is freed - * by kernel and since imx6q_pcie_abort_handler() is a no-op, - * we can install the handler here without risking it - * accessing some uninitialized driver state. - */ - hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0, - "external abort on non-linefetch"); - - return platform_driver_register(&imx6_pcie_driver); -} -device_initcall(imx6_pcie_init); diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c deleted file mode 100644 index 0682213328e9..000000000000 --- a/drivers/pci/dwc/pci-keystone-dw.c +++ /dev/null @@ -1,484 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * DesignWare application register space functions for Keystone PCI controller - * - * Copyright (C) 2013-2014 Texas Instruments., Ltd. - * http://www.ti.com - * - * Author: Murali Karicheri - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" -#include "pci-keystone.h" - -/* Application register defines */ -#define LTSSM_EN_VAL 1 -#define LTSSM_STATE_MASK 0x1f -#define LTSSM_STATE_L0 0x11 -#define DBI_CS2_EN_VAL 0x20 -#define OB_XLAT_EN_VAL 2 - -/* Application registers */ -#define CMD_STATUS 0x004 -#define CFG_SETUP 0x008 -#define OB_SIZE 0x030 -#define CFG_PCIM_WIN_SZ_IDX 3 -#define CFG_PCIM_WIN_CNT 32 -#define SPACE0_REMOTE_CFG_OFFSET 0x1000 -#define OB_OFFSET_INDEX(n) (0x200 + (8 * n)) -#define OB_OFFSET_HI(n) (0x204 + (8 * n)) - -/* IRQ register defines */ -#define IRQ_EOI 0x050 -#define IRQ_STATUS 0x184 -#define IRQ_ENABLE_SET 0x188 -#define IRQ_ENABLE_CLR 0x18c - -#define MSI_IRQ 0x054 -#define MSI0_IRQ_STATUS 0x104 -#define MSI0_IRQ_ENABLE_SET 0x108 -#define MSI0_IRQ_ENABLE_CLR 0x10c -#define IRQ_STATUS 0x184 -#define MSI_IRQ_OFFSET 4 - -/* Error IRQ bits */ -#define ERR_AER BIT(5) /* ECRC error */ -#define ERR_AXI BIT(4) /* AXI tag lookup fatal error */ -#define ERR_CORR BIT(3) /* Correctable error */ -#define ERR_NONFATAL BIT(2) /* Non-fatal error */ -#define ERR_FATAL BIT(1) /* Fatal error */ -#define ERR_SYS BIT(0) /* System (fatal, non-fatal, or correctable) */ -#define ERR_IRQ_ALL (ERR_AER | ERR_AXI | ERR_CORR | \ - ERR_NONFATAL | ERR_FATAL | ERR_SYS) -#define ERR_FATAL_IRQ (ERR_FATAL | ERR_AXI) -#define ERR_IRQ_STATUS_RAW 0x1c0 -#define ERR_IRQ_STATUS 0x1c4 -#define ERR_IRQ_ENABLE_SET 0x1c8 -#define ERR_IRQ_ENABLE_CLR 0x1cc - -/* Config space registers */ -#define DEBUG0 0x728 - -#define to_keystone_pcie(x) dev_get_drvdata((x)->dev) - -static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset, - u32 *bit_pos) -{ - *reg_offset = offset % 8; - *bit_pos = offset >> 3; -} - -phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - - return ks_pcie->app.start + MSI_IRQ; -} - -static u32 ks_dw_app_readl(struct keystone_pcie *ks_pcie, u32 offset) -{ - return readl(ks_pcie->va_app_base + offset); -} - -static void ks_dw_app_writel(struct keystone_pcie *ks_pcie, u32 offset, u32 val) -{ - writel(val, ks_pcie->va_app_base + offset); -} - -void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset) -{ - struct dw_pcie *pci = ks_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = pci->dev; - u32 pending, vector; - int src, virq; - - pending = ks_dw_app_readl(ks_pcie, MSI0_IRQ_STATUS + (offset << 4)); - - /* - * MSI0 status bit 0-3 shows vectors 0, 8, 16, 24, MSI1 status bit - * shows 1, 9, 17, 25 and so forth - */ - for (src = 0; src < 4; src++) { - if (BIT(src) & pending) { - vector = offset + (src << 3); - virq = irq_linear_revmap(pp->irq_domain, vector); - dev_dbg(dev, "irq: bit %d, vector %d, virq %d\n", - src, vector, virq); - generic_handle_irq(virq); - } - } -} - -void ks_dw_pcie_msi_irq_ack(int irq, struct pcie_port *pp) -{ - u32 reg_offset, bit_pos; - struct keystone_pcie *ks_pcie; - struct dw_pcie *pci; - - pci = to_dw_pcie_from_pp(pp); - ks_pcie = to_keystone_pcie(pci); - update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); - - ks_dw_app_writel(ks_pcie, MSI0_IRQ_STATUS + (reg_offset << 4), - BIT(bit_pos)); - ks_dw_app_writel(ks_pcie, IRQ_EOI, reg_offset + MSI_IRQ_OFFSET); -} - -void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) -{ - u32 reg_offset, bit_pos; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - - update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); - ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_SET + (reg_offset << 4), - BIT(bit_pos)); -} - -void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) -{ - u32 reg_offset, bit_pos; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - - update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); - ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_CLR + (reg_offset << 4), - BIT(bit_pos)); -} - -int ks_dw_pcie_msi_host_init(struct pcie_port *pp) -{ - return dw_pcie_allocate_domains(pp); -} - -void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie) -{ - int i; - - for (i = 0; i < PCI_NUM_INTX; i++) - ks_dw_app_writel(ks_pcie, IRQ_ENABLE_SET + (i << 4), 0x1); -} - -void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset) -{ - struct dw_pcie *pci = ks_pcie->pci; - struct device *dev = pci->dev; - u32 pending; - int virq; - - pending = ks_dw_app_readl(ks_pcie, IRQ_STATUS + (offset << 4)); - - if (BIT(0) & pending) { - virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, offset); - dev_dbg(dev, ": irq: irq_offset %d, virq %d\n", offset, virq); - generic_handle_irq(virq); - } - - /* EOI the INTx interrupt */ - ks_dw_app_writel(ks_pcie, IRQ_EOI, offset); -} - -void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie) -{ - ks_dw_app_writel(ks_pcie, ERR_IRQ_ENABLE_SET, ERR_IRQ_ALL); -} - -irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie) -{ - u32 status; - - status = ks_dw_app_readl(ks_pcie, ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL; - if (!status) - return IRQ_NONE; - - if (status & ERR_FATAL_IRQ) - dev_err(ks_pcie->pci->dev, "fatal error (status %#010x)\n", - status); - - /* Ack the IRQ; status bits are RW1C */ - ks_dw_app_writel(ks_pcie, ERR_IRQ_STATUS, status); - return IRQ_HANDLED; -} - -static void ks_dw_pcie_ack_legacy_irq(struct irq_data *d) -{ -} - -static void ks_dw_pcie_mask_legacy_irq(struct irq_data *d) -{ -} - -static void ks_dw_pcie_unmask_legacy_irq(struct irq_data *d) -{ -} - -static struct irq_chip ks_dw_pcie_legacy_irq_chip = { - .name = "Keystone-PCI-Legacy-IRQ", - .irq_ack = ks_dw_pcie_ack_legacy_irq, - .irq_mask = ks_dw_pcie_mask_legacy_irq, - .irq_unmask = ks_dw_pcie_unmask_legacy_irq, -}; - -static int ks_dw_pcie_init_legacy_irq_map(struct irq_domain *d, - unsigned int irq, irq_hw_number_t hw_irq) -{ - irq_set_chip_and_handler(irq, &ks_dw_pcie_legacy_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, d->host_data); - - return 0; -} - -static const struct irq_domain_ops ks_dw_pcie_legacy_irq_domain_ops = { - .map = ks_dw_pcie_init_legacy_irq_map, - .xlate = irq_domain_xlate_onetwocell, -}; - -/** - * ks_dw_pcie_set_dbi_mode() - Set DBI mode to access overlaid BAR mask - * registers - * - * Since modification of dbi_cs2 involves different clock domain, read the - * status back to ensure the transition is complete. - */ -static void ks_dw_pcie_set_dbi_mode(struct keystone_pcie *ks_pcie) -{ - u32 val; - - val = ks_dw_app_readl(ks_pcie, CMD_STATUS); - ks_dw_app_writel(ks_pcie, CMD_STATUS, DBI_CS2_EN_VAL | val); - - do { - val = ks_dw_app_readl(ks_pcie, CMD_STATUS); - } while (!(val & DBI_CS2_EN_VAL)); -} - -/** - * ks_dw_pcie_clear_dbi_mode() - Disable DBI mode - * - * Since modification of dbi_cs2 involves different clock domain, read the - * status back to ensure the transition is complete. - */ -static void ks_dw_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie) -{ - u32 val; - - val = ks_dw_app_readl(ks_pcie, CMD_STATUS); - ks_dw_app_writel(ks_pcie, CMD_STATUS, ~DBI_CS2_EN_VAL & val); - - do { - val = ks_dw_app_readl(ks_pcie, CMD_STATUS); - } while (val & DBI_CS2_EN_VAL); -} - -void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) -{ - struct dw_pcie *pci = ks_pcie->pci; - struct pcie_port *pp = &pci->pp; - u32 start = pp->mem->start, end = pp->mem->end; - int i, tr_size; - u32 val; - - /* Disable BARs for inbound access */ - ks_dw_pcie_set_dbi_mode(ks_pcie); - dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); - dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0); - ks_dw_pcie_clear_dbi_mode(ks_pcie); - - /* Set outbound translation size per window division */ - ks_dw_app_writel(ks_pcie, OB_SIZE, CFG_PCIM_WIN_SZ_IDX & 0x7); - - tr_size = (1 << (CFG_PCIM_WIN_SZ_IDX & 0x7)) * SZ_1M; - - /* Using Direct 1:1 mapping of RC <-> PCI memory space */ - for (i = 0; (i < CFG_PCIM_WIN_CNT) && (start < end); i++) { - ks_dw_app_writel(ks_pcie, OB_OFFSET_INDEX(i), start | 1); - ks_dw_app_writel(ks_pcie, OB_OFFSET_HI(i), 0); - start += tr_size; - } - - /* Enable OB translation */ - val = ks_dw_app_readl(ks_pcie, CMD_STATUS); - ks_dw_app_writel(ks_pcie, CMD_STATUS, OB_XLAT_EN_VAL | val); -} - -/** - * ks_pcie_cfg_setup() - Set up configuration space address for a device - * - * @ks_pcie: ptr to keystone_pcie structure - * @bus: Bus number the device is residing on - * @devfn: device, function number info - * - * Forms and returns the address of configuration space mapped in PCIESS - * address space 0. Also configures CFG_SETUP for remote configuration space - * access. - * - * The address space has two regions to access configuration - local and remote. - * We access local region for bus 0 (as RC is attached on bus 0) and remote - * region for others with TYPE 1 access when bus > 1. As for device on bus = 1, - * we will do TYPE 0 access as it will be on our secondary bus (logical). - * CFG_SETUP is needed only for remote configuration access. - */ -static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus, - unsigned int devfn) -{ - u8 device = PCI_SLOT(devfn), function = PCI_FUNC(devfn); - struct dw_pcie *pci = ks_pcie->pci; - struct pcie_port *pp = &pci->pp; - u32 regval; - - if (bus == 0) - return pci->dbi_base; - - regval = (bus << 16) | (device << 8) | function; - - /* - * Since Bus#1 will be a virtual bus, we need to have TYPE0 - * access only. - * TYPE 1 - */ - if (bus != 1) - regval |= BIT(24); - - ks_dw_app_writel(ks_pcie, CFG_SETUP, regval); - return pp->va_cfg0_base; -} - -int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 *val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - u8 bus_num = bus->number; - void __iomem *addr; - - addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); - - return dw_pcie_read(addr + where, size, val); -} - -int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - u8 bus_num = bus->number; - void __iomem *addr; - - addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); - - return dw_pcie_write(addr + where, size, val); -} - -/** - * ks_dw_pcie_v3_65_scan_bus() - keystone scan_bus post initialization - * - * This sets BAR0 to enable inbound access for MSI_IRQ register - */ -void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - - /* Configure and set up BAR0 */ - ks_dw_pcie_set_dbi_mode(ks_pcie); - - /* Enable BAR0 */ - dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 1); - dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, SZ_4K - 1); - - ks_dw_pcie_clear_dbi_mode(ks_pcie); - - /* - * For BAR0, just setting bus address for inbound writes (MSI) should - * be sufficient. Use physical address to avoid any conflicts. - */ - dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, ks_pcie->app.start); -} - -/** - * ks_dw_pcie_link_up() - Check if link up - */ -int ks_dw_pcie_link_up(struct dw_pcie *pci) -{ - u32 val; - - val = dw_pcie_readl_dbi(pci, DEBUG0); - return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0; -} - -void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie) -{ - u32 val; - - /* Disable Link training */ - val = ks_dw_app_readl(ks_pcie, CMD_STATUS); - val &= ~LTSSM_EN_VAL; - ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); - - /* Initiate Link Training */ - val = ks_dw_app_readl(ks_pcie, CMD_STATUS); - ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); -} - -/** - * ks_dw_pcie_host_init() - initialize host for v3_65 dw hardware - * - * Ioremap the register resources, initialize legacy irq domain - * and call dw_pcie_v3_65_host_init() API to initialize the Keystone - * PCI host controller. - */ -int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, - struct device_node *msi_intc_np) -{ - struct dw_pcie *pci = ks_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = pci->dev; - struct platform_device *pdev = to_platform_device(dev); - struct resource *res; - - /* Index 0 is the config reg. space address */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - - /* - * We set these same and is used in pcie rd/wr_other_conf - * functions - */ - pp->va_cfg0_base = pci->dbi_base + SPACE0_REMOTE_CFG_OFFSET; - pp->va_cfg1_base = pp->va_cfg0_base; - - /* Index 1 is the application reg. space address */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - ks_pcie->va_app_base = devm_ioremap_resource(dev, res); - if (IS_ERR(ks_pcie->va_app_base)) - return PTR_ERR(ks_pcie->va_app_base); - - ks_pcie->app = *res; - - /* Create legacy IRQ domain */ - ks_pcie->legacy_irq_domain = - irq_domain_add_linear(ks_pcie->legacy_intc_np, - PCI_NUM_INTX, - &ks_dw_pcie_legacy_irq_domain_ops, - NULL); - if (!ks_pcie->legacy_irq_domain) { - dev_err(dev, "Failed to add irq domain for legacy irqs\n"); - return -EINVAL; - } - - return dw_pcie_host_init(pp); -} diff --git a/drivers/pci/dwc/pci-keystone.c b/drivers/pci/dwc/pci-keystone.c deleted file mode 100644 index 3722a5f31e5e..000000000000 --- a/drivers/pci/dwc/pci-keystone.c +++ /dev/null @@ -1,457 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for Texas Instruments Keystone SoCs - * - * Copyright (C) 2013-2014 Texas Instruments., Ltd. - * http://www.ti.com - * - * Author: Murali Karicheri - * Implementation based on pci-exynos.c and pcie-designware.c - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" -#include "pci-keystone.h" - -#define DRIVER_NAME "keystone-pcie" - -/* DEV_STAT_CTRL */ -#define PCIE_CAP_BASE 0x70 - -/* PCIE controller device IDs */ -#define PCIE_RC_K2HK 0xb008 -#define PCIE_RC_K2E 0xb009 -#define PCIE_RC_K2L 0xb00a - -#define to_keystone_pcie(x) dev_get_drvdata((x)->dev) - -static void quirk_limit_mrrs(struct pci_dev *dev) -{ - struct pci_bus *bus = dev->bus; - struct pci_dev *bridge = bus->self; - static const struct pci_device_id rc_pci_devids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2HK), - .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, - { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2E), - .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, - { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2L), - .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, }, - { 0, }, - }; - - if (pci_is_root_bus(bus)) - return; - - /* look for the host bridge */ - while (!pci_is_root_bus(bus)) { - bridge = bus->self; - bus = bus->parent; - } - - if (bridge) { - /* - * Keystone PCI controller has a h/w limitation of - * 256 bytes maximum read request size. It can't handle - * anything higher than this. So force this limit on - * all downstream devices. - */ - if (pci_match_id(rc_pci_devids, bridge)) { - if (pcie_get_readrq(dev) > 256) { - dev_info(&dev->dev, "limiting MRRS to 256\n"); - pcie_set_readrq(dev, 256); - } - } - } -} -DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs); - -static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) -{ - struct dw_pcie *pci = ks_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = pci->dev; - unsigned int retries; - - dw_pcie_setup_rc(pp); - - if (dw_pcie_link_up(pci)) { - dev_info(dev, "Link already up\n"); - return 0; - } - - /* check if the link is up or not */ - for (retries = 0; retries < 5; retries++) { - ks_dw_pcie_initiate_link_train(ks_pcie); - if (!dw_pcie_wait_for_link(pci)) - return 0; - } - - dev_err(dev, "phy link never came up\n"); - return -ETIMEDOUT; -} - -static void ks_pcie_msi_irq_handler(struct irq_desc *desc) -{ - unsigned int irq = irq_desc_get_irq(desc); - struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); - u32 offset = irq - ks_pcie->msi_host_irqs[0]; - struct dw_pcie *pci = ks_pcie->pci; - struct device *dev = pci->dev; - struct irq_chip *chip = irq_desc_get_chip(desc); - - dev_dbg(dev, "%s, irq %d\n", __func__, irq); - - /* - * The chained irq handler installation would have replaced normal - * interrupt driver handler so we need to take care of mask/unmask and - * ack operation. - */ - chained_irq_enter(chip, desc); - ks_dw_pcie_handle_msi_irq(ks_pcie, offset); - chained_irq_exit(chip, desc); -} - -/** - * ks_pcie_legacy_irq_handler() - Handle legacy interrupt - * @irq: IRQ line for legacy interrupts - * @desc: Pointer to irq descriptor - * - * Traverse through pending legacy interrupts and invoke handler for each. Also - * takes care of interrupt controller level mask/ack operation. - */ -static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) -{ - unsigned int irq = irq_desc_get_irq(desc); - struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); - struct dw_pcie *pci = ks_pcie->pci; - struct device *dev = pci->dev; - u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0]; - struct irq_chip *chip = irq_desc_get_chip(desc); - - dev_dbg(dev, ": Handling legacy irq %d\n", irq); - - /* - * The chained irq handler installation would have replaced normal - * interrupt driver handler so we need to take care of mask/unmask and - * ack operation. - */ - chained_irq_enter(chip, desc); - ks_dw_pcie_handle_legacy_irq(ks_pcie, irq_offset); - chained_irq_exit(chip, desc); -} - -static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie, - char *controller, int *num_irqs) -{ - int temp, max_host_irqs, legacy = 1, *host_irqs; - struct device *dev = ks_pcie->pci->dev; - struct device_node *np_pcie = dev->of_node, **np_temp; - - if (!strcmp(controller, "msi-interrupt-controller")) - legacy = 0; - - if (legacy) { - np_temp = &ks_pcie->legacy_intc_np; - max_host_irqs = PCI_NUM_INTX; - host_irqs = &ks_pcie->legacy_host_irqs[0]; - } else { - np_temp = &ks_pcie->msi_intc_np; - max_host_irqs = MAX_MSI_HOST_IRQS; - host_irqs = &ks_pcie->msi_host_irqs[0]; - } - - /* interrupt controller is in a child node */ - *np_temp = of_get_child_by_name(np_pcie, controller); - if (!(*np_temp)) { - dev_err(dev, "Node for %s is absent\n", controller); - return -EINVAL; - } - - temp = of_irq_count(*np_temp); - if (!temp) { - dev_err(dev, "No IRQ entries in %s\n", controller); - of_node_put(*np_temp); - return -EINVAL; - } - - if (temp > max_host_irqs) - dev_warn(dev, "Too many %s interrupts defined %u\n", - (legacy ? "legacy" : "MSI"), temp); - - /* - * support upto max_host_irqs. In dt from index 0 to 3 (legacy) or 0 to - * 7 (MSI) - */ - for (temp = 0; temp < max_host_irqs; temp++) { - host_irqs[temp] = irq_of_parse_and_map(*np_temp, temp); - if (!host_irqs[temp]) - break; - } - - of_node_put(*np_temp); - - if (temp) { - *num_irqs = temp; - return 0; - } - - return -EINVAL; -} - -static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie) -{ - int i; - - /* Legacy IRQ */ - for (i = 0; i < ks_pcie->num_legacy_host_irqs; i++) { - irq_set_chained_handler_and_data(ks_pcie->legacy_host_irqs[i], - ks_pcie_legacy_irq_handler, - ks_pcie); - } - ks_dw_pcie_enable_legacy_irqs(ks_pcie); - - /* MSI IRQ */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - for (i = 0; i < ks_pcie->num_msi_host_irqs; i++) { - irq_set_chained_handler_and_data(ks_pcie->msi_host_irqs[i], - ks_pcie_msi_irq_handler, - ks_pcie); - } - } - - if (ks_pcie->error_irq > 0) - ks_dw_pcie_enable_error_irq(ks_pcie); -} - -/* - * When a PCI device does not exist during config cycles, keystone host gets a - * bus error instead of returning 0xffffffff. This handler always returns 0 - * for this kind of faults. - */ -static int keystone_pcie_fault(unsigned long addr, unsigned int fsr, - struct pt_regs *regs) -{ - unsigned long instr = *(unsigned long *) instruction_pointer(regs); - - if ((instr & 0x0e100090) == 0x00100090) { - int reg = (instr >> 12) & 15; - - regs->uregs[reg] = -1; - regs->ARM_pc += 4; - } - - return 0; -} - -static int __init ks_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - u32 val; - - ks_pcie_establish_link(ks_pcie); - ks_dw_pcie_setup_rc_app_regs(ks_pcie); - ks_pcie_setup_interrupts(ks_pcie); - writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8), - pci->dbi_base + PCI_IO_BASE); - - /* update the Vendor ID */ - writew(ks_pcie->device_id, pci->dbi_base + PCI_DEVICE_ID); - - /* update the DEV_STAT_CTRL to publish right mrrs */ - val = readl(pci->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL); - val &= ~PCI_EXP_DEVCTL_READRQ; - /* set the mrrs to 256 bytes */ - val |= BIT(12); - writel(val, pci->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL); - - /* - * PCIe access errors that result into OCP errors are caught by ARM as - * "External aborts" - */ - hook_fault_code(17, keystone_pcie_fault, SIGBUS, 0, - "Asynchronous external abort"); - - return 0; -} - -static const struct dw_pcie_host_ops keystone_pcie_host_ops = { - .rd_other_conf = ks_dw_pcie_rd_other_conf, - .wr_other_conf = ks_dw_pcie_wr_other_conf, - .host_init = ks_pcie_host_init, - .msi_set_irq = ks_dw_pcie_msi_set_irq, - .msi_clear_irq = ks_dw_pcie_msi_clear_irq, - .get_msi_addr = ks_dw_pcie_get_msi_addr, - .msi_host_init = ks_dw_pcie_msi_host_init, - .msi_irq_ack = ks_dw_pcie_msi_irq_ack, - .scan_bus = ks_dw_pcie_v3_65_scan_bus, -}; - -static irqreturn_t pcie_err_irq_handler(int irq, void *priv) -{ - struct keystone_pcie *ks_pcie = priv; - - return ks_dw_pcie_handle_error_irq(ks_pcie); -} - -static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie, - struct platform_device *pdev) -{ - struct dw_pcie *pci = ks_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = &pdev->dev; - int ret; - - ret = ks_pcie_get_irq_controller_info(ks_pcie, - "legacy-interrupt-controller", - &ks_pcie->num_legacy_host_irqs); - if (ret) - return ret; - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - ret = ks_pcie_get_irq_controller_info(ks_pcie, - "msi-interrupt-controller", - &ks_pcie->num_msi_host_irqs); - if (ret) - return ret; - } - - /* - * Index 0 is the platform interrupt for error interrupt - * from RC. This is optional. - */ - ks_pcie->error_irq = irq_of_parse_and_map(ks_pcie->np, 0); - if (ks_pcie->error_irq <= 0) - dev_info(dev, "no error IRQ defined\n"); - else { - ret = request_irq(ks_pcie->error_irq, pcie_err_irq_handler, - IRQF_SHARED, "pcie-error-irq", ks_pcie); - if (ret < 0) { - dev_err(dev, "failed to request error IRQ %d\n", - ks_pcie->error_irq); - return ret; - } - } - - pp->root_bus_nr = -1; - pp->ops = &keystone_pcie_host_ops; - ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static const struct of_device_id ks_pcie_of_match[] = { - { - .type = "pci", - .compatible = "ti,keystone-pcie", - }, - { }, -}; - -static const struct dw_pcie_ops dw_pcie_ops = { - .link_up = ks_dw_pcie_link_up, -}; - -static int __exit ks_pcie_remove(struct platform_device *pdev) -{ - struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev); - - clk_disable_unprepare(ks_pcie->clk); - - return 0; -} - -static int __init ks_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_pcie *pci; - struct keystone_pcie *ks_pcie; - struct resource *res; - void __iomem *reg_p; - struct phy *phy; - int ret; - - ks_pcie = devm_kzalloc(dev, sizeof(*ks_pcie), GFP_KERNEL); - if (!ks_pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - ks_pcie->pci = pci; - - /* initialize SerDes Phy if present */ - phy = devm_phy_get(dev, "pcie-phy"); - if (PTR_ERR_OR_ZERO(phy) == -EPROBE_DEFER) - return PTR_ERR(phy); - - if (!IS_ERR_OR_NULL(phy)) { - ret = phy_init(phy); - if (ret < 0) - return ret; - } - - /* index 2 is to read PCI DEVICE_ID */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - reg_p = devm_ioremap_resource(dev, res); - if (IS_ERR(reg_p)) - return PTR_ERR(reg_p); - ks_pcie->device_id = readl(reg_p) >> 16; - devm_iounmap(dev, reg_p); - devm_release_mem_region(dev, res->start, resource_size(res)); - - ks_pcie->np = dev->of_node; - platform_set_drvdata(pdev, ks_pcie); - ks_pcie->clk = devm_clk_get(dev, "pcie"); - if (IS_ERR(ks_pcie->clk)) { - dev_err(dev, "Failed to get pcie rc clock\n"); - return PTR_ERR(ks_pcie->clk); - } - ret = clk_prepare_enable(ks_pcie->clk); - if (ret) - return ret; - - platform_set_drvdata(pdev, ks_pcie); - - ret = ks_add_pcie_port(ks_pcie, pdev); - if (ret < 0) - goto fail_clk; - - return 0; -fail_clk: - clk_disable_unprepare(ks_pcie->clk); - - return ret; -} - -static struct platform_driver ks_pcie_driver __refdata = { - .probe = ks_pcie_probe, - .remove = __exit_p(ks_pcie_remove), - .driver = { - .name = "keystone-pcie", - .of_match_table = of_match_ptr(ks_pcie_of_match), - }, -}; -builtin_platform_driver(ks_pcie_driver); diff --git a/drivers/pci/dwc/pci-keystone.h b/drivers/pci/dwc/pci-keystone.h deleted file mode 100644 index 8a13da391543..000000000000 --- a/drivers/pci/dwc/pci-keystone.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Keystone PCI Controller's common includes - * - * Copyright (C) 2013-2014 Texas Instruments., Ltd. - * http://www.ti.com - * - * Author: Murali Karicheri - */ - -#define MAX_MSI_HOST_IRQS 8 - -struct keystone_pcie { - struct dw_pcie *pci; - struct clk *clk; - /* PCI Device ID */ - u32 device_id; - int num_legacy_host_irqs; - int legacy_host_irqs[PCI_NUM_INTX]; - struct device_node *legacy_intc_np; - - int num_msi_host_irqs; - int msi_host_irqs[MAX_MSI_HOST_IRQS]; - struct device_node *msi_intc_np; - struct irq_domain *legacy_irq_domain; - struct device_node *np; - - int error_irq; - - /* Application register space */ - void __iomem *va_app_base; /* DT 1st resource */ - struct resource app; -}; - -/* Keystone DW specific MSI controller APIs/definitions */ -void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset); -phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp); - -/* Keystone specific PCI controller APIs */ -void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie); -void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset); -void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie); -irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie); -int ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, - struct device_node *msi_intc_np); -int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 val); -int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 *val); -void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie); -void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie); -void ks_dw_pcie_msi_irq_ack(int i, struct pcie_port *pp); -void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); -void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); -void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); -int ks_dw_pcie_msi_host_init(struct pcie_port *pp); -int ks_dw_pcie_link_up(struct dw_pcie *pci); diff --git a/drivers/pci/dwc/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c deleted file mode 100644 index 3724d3ef7008..000000000000 --- a/drivers/pci/dwc/pci-layerscape.c +++ /dev/null @@ -1,341 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for Freescale Layerscape SoCs - * - * Copyright (C) 2014 Freescale Semiconductor. - * - * Author: Minghuan Lian - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -/* PEX1/2 Misc Ports Status Register */ -#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4) -#define LTSSM_STATE_SHIFT 20 -#define LTSSM_STATE_MASK 0x3f -#define LTSSM_PCIE_L0 0x11 /* L0 state */ - -/* PEX Internal Configuration Registers */ -#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ -#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */ -#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */ - -#define PCIE_IATU_NUM 6 - -struct ls_pcie_drvdata { - u32 lut_offset; - u32 ltssm_shift; - u32 lut_dbg; - const struct dw_pcie_host_ops *ops; - const struct dw_pcie_ops *dw_pcie_ops; -}; - -struct ls_pcie { - struct dw_pcie *pci; - void __iomem *lut; - struct regmap *scfg; - const struct ls_pcie_drvdata *drvdata; - int index; -}; - -#define to_ls_pcie(x) dev_get_drvdata((x)->dev) - -static bool ls_pcie_is_bridge(struct ls_pcie *pcie) -{ - struct dw_pcie *pci = pcie->pci; - u32 header_type; - - header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE); - header_type &= 0x7f; - - return header_type == PCI_HEADER_TYPE_BRIDGE; -} - -/* Clear multi-function bit */ -static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) -{ - struct dw_pcie *pci = pcie->pci; - - iowrite8(PCI_HEADER_TYPE_BRIDGE, pci->dbi_base + PCI_HEADER_TYPE); -} - -/* Drop MSG TLP except for Vendor MSG */ -static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) -{ - u32 val; - struct dw_pcie *pci = pcie->pci; - - val = ioread32(pci->dbi_base + PCIE_STRFMR1); - val &= 0xDFFFFFFF; - iowrite32(val, pci->dbi_base + PCIE_STRFMR1); -} - -static void ls_pcie_disable_outbound_atus(struct ls_pcie *pcie) -{ - int i; - - for (i = 0; i < PCIE_IATU_NUM; i++) - dw_pcie_disable_atu(pcie->pci, DW_PCIE_REGION_OUTBOUND, i); -} - -static int ls1021_pcie_link_up(struct dw_pcie *pci) -{ - u32 state; - struct ls_pcie *pcie = to_ls_pcie(pci); - - if (!pcie->scfg) - return 0; - - regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); - state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; - - if (state < LTSSM_PCIE_L0) - return 0; - - return 1; -} - -static int ls_pcie_link_up(struct dw_pcie *pci) -{ - struct ls_pcie *pcie = to_ls_pcie(pci); - u32 state; - - state = (ioread32(pcie->lut + pcie->drvdata->lut_dbg) >> - pcie->drvdata->ltssm_shift) & - LTSSM_STATE_MASK; - - if (state < LTSSM_PCIE_L0) - return 0; - - return 1; -} - -/* Forward error response of outbound non-posted requests */ -static void ls_pcie_fix_error_response(struct ls_pcie *pcie) -{ - struct dw_pcie *pci = pcie->pci; - - iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR); -} - -static int ls_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); - - /* - * Disable outbound windows configured by the bootloader to avoid - * one transaction hitting multiple outbound windows. - * dw_pcie_setup_rc() will reconfigure the outbound windows. - */ - ls_pcie_disable_outbound_atus(pcie); - ls_pcie_fix_error_response(pcie); - - dw_pcie_dbi_ro_wr_en(pci); - ls_pcie_clear_multifunction(pcie); - dw_pcie_dbi_ro_wr_dis(pci); - - ls_pcie_drop_msg_tlp(pcie); - - dw_pcie_setup_rc(pp); - - return 0; -} - -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 *dev = pci->dev; - u32 index[2]; - int ret; - - pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, - "fsl,pcie-scfg"); - if (IS_ERR(pcie->scfg)) { - ret = PTR_ERR(pcie->scfg); - dev_err(dev, "No syscfg phandle specified\n"); - pcie->scfg = NULL; - return ret; - } - - if (of_property_read_u32_array(dev->of_node, - "fsl,pcie-scfg", index, 2)) { - pcie->scfg = NULL; - return -EINVAL; - } - pcie->index = index[1]; - - return ls_pcie_host_init(pp); -} - -static int ls_pcie_msi_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct device *dev = pci->dev; - struct device_node *np = dev->of_node; - struct device_node *msi_node; - - /* - * The MSI domain is set by the generic of_msi_configure(). This - * .msi_host_init() function keeps us from doing the default MSI - * domain setup in dw_pcie_host_init() and also enforces the - * requirement that "msi-parent" exists. - */ - msi_node = of_parse_phandle(np, "msi-parent", 0); - if (!msi_node) { - dev_err(dev, "failed to find msi-parent\n"); - return -EINVAL; - } - - return 0; -} - -static const struct dw_pcie_host_ops ls1021_pcie_host_ops = { - .host_init = ls1021_pcie_host_init, - .msi_host_init = ls_pcie_msi_host_init, -}; - -static const struct dw_pcie_host_ops ls_pcie_host_ops = { - .host_init = ls_pcie_host_init, - .msi_host_init = ls_pcie_msi_host_init, -}; - -static const struct dw_pcie_ops dw_ls1021_pcie_ops = { - .link_up = ls1021_pcie_link_up, -}; - -static const struct dw_pcie_ops dw_ls_pcie_ops = { - .link_up = ls_pcie_link_up, -}; - -static struct ls_pcie_drvdata ls1021_drvdata = { - .ops = &ls1021_pcie_host_ops, - .dw_pcie_ops = &dw_ls1021_pcie_ops, -}; - -static struct ls_pcie_drvdata ls1043_drvdata = { - .lut_offset = 0x10000, - .ltssm_shift = 24, - .lut_dbg = 0x7fc, - .ops = &ls_pcie_host_ops, - .dw_pcie_ops = &dw_ls_pcie_ops, -}; - -static struct ls_pcie_drvdata ls1046_drvdata = { - .lut_offset = 0x80000, - .ltssm_shift = 24, - .lut_dbg = 0x407fc, - .ops = &ls_pcie_host_ops, - .dw_pcie_ops = &dw_ls_pcie_ops, -}; - -static struct ls_pcie_drvdata ls2080_drvdata = { - .lut_offset = 0x80000, - .ltssm_shift = 0, - .lut_dbg = 0x7fc, - .ops = &ls_pcie_host_ops, - .dw_pcie_ops = &dw_ls_pcie_ops, -}; - -static struct ls_pcie_drvdata ls2088_drvdata = { - .lut_offset = 0x80000, - .ltssm_shift = 0, - .lut_dbg = 0x407fc, - .ops = &ls_pcie_host_ops, - .dw_pcie_ops = &dw_ls_pcie_ops, -}; - -static const struct of_device_id ls_pcie_of_match[] = { - { .compatible = "fsl,ls1012a-pcie", .data = &ls1046_drvdata }, - { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata }, - { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata }, - { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata }, - { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata }, - { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata }, - { .compatible = "fsl,ls2088a-pcie", .data = &ls2088_drvdata }, - { .compatible = "fsl,ls1088a-pcie", .data = &ls2088_drvdata }, - { }, -}; - -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 *dev = pci->dev; - int ret; - - pp->ops = pcie->drvdata->ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static int __init ls_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_pcie *pci; - struct ls_pcie *pcie; - struct resource *dbi_base; - int ret; - - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); - if (!pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pcie->drvdata = of_device_get_match_data(dev); - - pci->dev = dev; - pci->ops = pcie->drvdata->dw_pcie_ops; - - pcie->pci = pci; - - dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - - pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset; - - if (!ls_pcie_is_bridge(pcie)) - return -ENODEV; - - platform_set_drvdata(pdev, pcie); - - ret = ls_add_pcie_port(pcie); - if (ret < 0) - return ret; - - return 0; -} - -static struct platform_driver ls_pcie_driver = { - .driver = { - .name = "layerscape-pcie", - .of_match_table = ls_pcie_of_match, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver_probe(ls_pcie_driver, ls_pcie_probe); diff --git a/drivers/pci/dwc/pcie-armada8k.c b/drivers/pci/dwc/pcie-armada8k.c deleted file mode 100644 index 072fd7ecc29f..000000000000 --- a/drivers/pci/dwc/pcie-armada8k.c +++ /dev/null @@ -1,282 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for Marvell Armada-8K SoCs - * - * Armada-8K PCIe Glue Layer Source Code - * - * Copyright (C) 2016 Marvell Technology Group Ltd. - * - * Author: Yehuda Yitshak - * Author: Shadi Ammouri - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -struct armada8k_pcie { - struct dw_pcie *pci; - struct clk *clk; - struct clk *clk_reg; -}; - -#define PCIE_VENDOR_REGS_OFFSET 0x8000 - -#define PCIE_GLOBAL_CONTROL_REG (PCIE_VENDOR_REGS_OFFSET + 0x0) -#define PCIE_APP_LTSSM_EN BIT(2) -#define PCIE_DEVICE_TYPE_SHIFT 4 -#define PCIE_DEVICE_TYPE_MASK 0xF -#define PCIE_DEVICE_TYPE_RC 0x4 /* Root complex */ - -#define PCIE_GLOBAL_STATUS_REG (PCIE_VENDOR_REGS_OFFSET + 0x8) -#define PCIE_GLB_STS_RDLH_LINK_UP BIT(1) -#define PCIE_GLB_STS_PHY_LINK_UP BIT(9) - -#define PCIE_GLOBAL_INT_CAUSE1_REG (PCIE_VENDOR_REGS_OFFSET + 0x1C) -#define PCIE_GLOBAL_INT_MASK1_REG (PCIE_VENDOR_REGS_OFFSET + 0x20) -#define PCIE_INT_A_ASSERT_MASK BIT(9) -#define PCIE_INT_B_ASSERT_MASK BIT(10) -#define PCIE_INT_C_ASSERT_MASK BIT(11) -#define PCIE_INT_D_ASSERT_MASK BIT(12) - -#define PCIE_ARCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x50) -#define PCIE_AWCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x54) -#define PCIE_ARUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x5C) -#define PCIE_AWUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x60) -/* - * AR/AW Cache defauls: Normal memory, Write-Back, Read / Write - * allocate - */ -#define ARCACHE_DEFAULT_VALUE 0x3511 -#define AWCACHE_DEFAULT_VALUE 0x5311 - -#define DOMAIN_OUTER_SHAREABLE 0x2 -#define AX_USER_DOMAIN_MASK 0x3 -#define AX_USER_DOMAIN_SHIFT 4 - -#define to_armada8k_pcie(x) dev_get_drvdata((x)->dev) - -static int armada8k_pcie_link_up(struct dw_pcie *pci) -{ - u32 reg; - u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP; - - reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_STATUS_REG); - - if ((reg & mask) == mask) - return 1; - - dev_dbg(pci->dev, "No link detected (Global-Status: 0x%08x).\n", reg); - return 0; -} - -static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie) -{ - struct dw_pcie *pci = pcie->pci; - u32 reg; - - if (!dw_pcie_link_up(pci)) { - /* Disable LTSSM state machine to enable configuration */ - reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); - reg &= ~(PCIE_APP_LTSSM_EN); - dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); - } - - /* Set the device to root complex mode */ - reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); - reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT); - reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT; - dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); - - /* Set the PCIe master AxCache attributes */ - dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE); - dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE); - - /* Set the PCIe master AxDomain attributes */ - reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG); - reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); - reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; - dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg); - - reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG); - reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); - reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; - dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg); - - /* Enable INT A-D interrupts */ - reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG); - reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK | - PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK; - dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg); - - if (!dw_pcie_link_up(pci)) { - /* Configuration done. Start LTSSM */ - reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); - reg |= PCIE_APP_LTSSM_EN; - dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); - } - - /* Wait until the link becomes active again */ - if (dw_pcie_wait_for_link(pci)) - dev_err(pci->dev, "Link not up after reconfiguration\n"); -} - -static int armada8k_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct armada8k_pcie *pcie = to_armada8k_pcie(pci); - - dw_pcie_setup_rc(pp); - armada8k_pcie_establish_link(pcie); - - return 0; -} - -static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) -{ - struct armada8k_pcie *pcie = arg; - struct dw_pcie *pci = pcie->pci; - u32 val; - - /* - * Interrupts are directly handled by the device driver of the - * PCI device. However, they are also latched into the PCIe - * controller, so we simply discard them. - */ - val = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG); - dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG, val); - - return IRQ_HANDLED; -} - -static const struct dw_pcie_host_ops armada8k_pcie_host_ops = { - .host_init = armada8k_pcie_host_init, -}; - -static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, - struct platform_device *pdev) -{ - struct dw_pcie *pci = pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = &pdev->dev; - int ret; - - pp->root_bus_nr = -1; - pp->ops = &armada8k_pcie_host_ops; - - pp->irq = platform_get_irq(pdev, 0); - if (pp->irq < 0) { - dev_err(dev, "failed to get irq for port\n"); - return pp->irq; - } - - ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler, - IRQF_SHARED, "armada8k-pcie", pcie); - if (ret) { - dev_err(dev, "failed to request irq %d\n", pp->irq); - return ret; - } - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host: %d\n", ret); - return ret; - } - - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .link_up = armada8k_pcie_link_up, -}; - -static int armada8k_pcie_probe(struct platform_device *pdev) -{ - struct dw_pcie *pci; - struct armada8k_pcie *pcie; - struct device *dev = &pdev->dev; - struct resource *base; - int ret; - - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); - if (!pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - pcie->pci = pci; - - pcie->clk = devm_clk_get(dev, NULL); - if (IS_ERR(pcie->clk)) - return PTR_ERR(pcie->clk); - - ret = clk_prepare_enable(pcie->clk); - if (ret) - return ret; - - pcie->clk_reg = devm_clk_get(dev, "reg"); - if (pcie->clk_reg == ERR_PTR(-EPROBE_DEFER)) { - ret = -EPROBE_DEFER; - goto fail; - } - if (!IS_ERR(pcie->clk_reg)) { - ret = clk_prepare_enable(pcie->clk_reg); - if (ret) - goto fail_clkreg; - } - - /* Get the dw-pcie unit configuration/control registers base. */ - base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, base); - if (IS_ERR(pci->dbi_base)) { - dev_err(dev, "couldn't remap regs base %p\n", base); - ret = PTR_ERR(pci->dbi_base); - goto fail_clkreg; - } - - platform_set_drvdata(pdev, pcie); - - ret = armada8k_add_pcie_port(pcie, pdev); - if (ret) - goto fail_clkreg; - - return 0; - -fail_clkreg: - clk_disable_unprepare(pcie->clk_reg); -fail: - clk_disable_unprepare(pcie->clk); - - return ret; -} - -static const struct of_device_id armada8k_pcie_of_match[] = { - { .compatible = "marvell,armada8k-pcie", }, - {}, -}; - -static struct platform_driver armada8k_pcie_driver = { - .probe = armada8k_pcie_probe, - .driver = { - .name = "armada8k-pcie", - .of_match_table = of_match_ptr(armada8k_pcie_of_match), - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(armada8k_pcie_driver); diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c deleted file mode 100644 index 321b56cfd5d0..000000000000 --- a/drivers/pci/dwc/pcie-artpec6.c +++ /dev/null @@ -1,618 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for Axis ARTPEC-6 SoC - * - * Author: Niklas Cassel - * - * Based on work done by Phil Edworthy - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -#define to_artpec6_pcie(x) dev_get_drvdata((x)->dev) - -enum artpec_pcie_variants { - ARTPEC6, - ARTPEC7, -}; - -struct artpec6_pcie { - struct dw_pcie *pci; - struct regmap *regmap; /* DT axis,syscon-pcie */ - void __iomem *phy_base; /* DT phy */ - enum artpec_pcie_variants variant; - enum dw_pcie_device_mode mode; -}; - -struct artpec_pcie_of_data { - enum artpec_pcie_variants variant; - enum dw_pcie_device_mode mode; -}; - -static const struct of_device_id artpec6_pcie_of_match[]; - -/* PCIe Port Logic registers (memory-mapped) */ -#define PL_OFFSET 0x700 - -#define ACK_F_ASPM_CTRL_OFF (PL_OFFSET + 0xc) -#define ACK_N_FTS_MASK GENMASK(15, 8) -#define ACK_N_FTS(x) (((x) << 8) & ACK_N_FTS_MASK) - -#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0) -#define FAST_TRAINING_SEQ(x) (((x) << 0) & FAST_TRAINING_SEQ_MASK) - -/* ARTPEC-6 specific registers */ -#define PCIECFG 0x18 -#define PCIECFG_DBG_OEN BIT(24) -#define PCIECFG_CORE_RESET_REQ BIT(21) -#define PCIECFG_LTSSM_ENABLE BIT(20) -#define PCIECFG_DEVICE_TYPE_MASK GENMASK(19, 16) -#define PCIECFG_CLKREQ_B BIT(11) -#define PCIECFG_REFCLK_ENABLE BIT(10) -#define PCIECFG_PLL_ENABLE BIT(9) -#define PCIECFG_PCLK_ENABLE BIT(8) -#define PCIECFG_RISRCREN BIT(4) -#define PCIECFG_MODE_TX_DRV_EN BIT(3) -#define PCIECFG_CISRREN BIT(2) -#define PCIECFG_MACRO_ENABLE BIT(0) -/* ARTPEC-7 specific fields */ -#define PCIECFG_REFCLKSEL BIT(23) -#define PCIECFG_NOC_RESET BIT(3) - -#define PCIESTAT 0x1c -/* ARTPEC-7 specific fields */ -#define PCIESTAT_EXTREFCLK BIT(3) - -#define NOCCFG 0x40 -#define NOCCFG_ENABLE_CLK_PCIE BIT(4) -#define NOCCFG_POWER_PCIE_IDLEACK BIT(3) -#define NOCCFG_POWER_PCIE_IDLE BIT(2) -#define NOCCFG_POWER_PCIE_IDLEREQ BIT(1) - -#define PHY_STATUS 0x118 -#define PHY_COSPLLLOCK BIT(0) - -#define PHY_TX_ASIC_OUT 0x4040 -#define PHY_TX_ASIC_OUT_TX_ACK BIT(0) - -#define PHY_RX_ASIC_OUT 0x405c -#define PHY_RX_ASIC_OUT_ACK BIT(0) - -static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset) -{ - u32 val; - - regmap_read(artpec6_pcie->regmap, offset, &val); - return val; -} - -static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u32 val) -{ - regmap_write(artpec6_pcie->regmap, offset, val); -} - -static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) -{ - struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); - struct pcie_port *pp = &pci->pp; - struct dw_pcie_ep *ep = &pci->ep; - - switch (artpec6_pcie->mode) { - case DW_PCIE_RC_TYPE: - return pci_addr - pp->cfg0_base; - case DW_PCIE_EP_TYPE: - return pci_addr - ep->phys_base; - default: - dev_err(pci->dev, "UNKNOWN device type\n"); - } - return pci_addr; -} - -static int artpec6_pcie_establish_link(struct dw_pcie *pci) -{ - struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); - u32 val; - - val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val |= PCIECFG_LTSSM_ENABLE; - artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - - return 0; -} - -static void artpec6_pcie_stop_link(struct dw_pcie *pci) -{ - struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); - u32 val; - - val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val &= ~PCIECFG_LTSSM_ENABLE; - artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup, - .start_link = artpec6_pcie_establish_link, - .stop_link = artpec6_pcie_stop_link, -}; - -static void artpec6_pcie_wait_for_phy_a6(struct artpec6_pcie *artpec6_pcie) -{ - struct dw_pcie *pci = artpec6_pcie->pci; - struct device *dev = pci->dev; - u32 val; - unsigned int retries; - - retries = 50; - do { - usleep_range(1000, 2000); - val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); - retries--; - } while (retries && - (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE))); - if (!retries) - dev_err(dev, "PCIe clock manager did not leave idle state\n"); - - retries = 50; - do { - usleep_range(1000, 2000); - val = readl(artpec6_pcie->phy_base + PHY_STATUS); - retries--; - } while (retries && !(val & PHY_COSPLLLOCK)); - if (!retries) - dev_err(dev, "PHY PLL did not lock\n"); -} - -static void artpec6_pcie_wait_for_phy_a7(struct artpec6_pcie *artpec6_pcie) -{ - struct dw_pcie *pci = artpec6_pcie->pci; - struct device *dev = pci->dev; - u32 val; - u16 phy_status_tx, phy_status_rx; - unsigned int retries; - - retries = 50; - do { - usleep_range(1000, 2000); - val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); - retries--; - } while (retries && - (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE))); - if (!retries) - dev_err(dev, "PCIe clock manager did not leave idle state\n"); - - retries = 50; - do { - usleep_range(1000, 2000); - phy_status_tx = readw(artpec6_pcie->phy_base + PHY_TX_ASIC_OUT); - phy_status_rx = readw(artpec6_pcie->phy_base + PHY_RX_ASIC_OUT); - retries--; - } while (retries && ((phy_status_tx & PHY_TX_ASIC_OUT_TX_ACK) || - (phy_status_rx & PHY_RX_ASIC_OUT_ACK))); - if (!retries) - dev_err(dev, "PHY did not enter Pn state\n"); -} - -static void artpec6_pcie_wait_for_phy(struct artpec6_pcie *artpec6_pcie) -{ - switch (artpec6_pcie->variant) { - case ARTPEC6: - artpec6_pcie_wait_for_phy_a6(artpec6_pcie); - break; - case ARTPEC7: - artpec6_pcie_wait_for_phy_a7(artpec6_pcie); - break; - } -} - -static void artpec6_pcie_init_phy_a6(struct artpec6_pcie *artpec6_pcie) -{ - u32 val; - - val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */ - PCIECFG_MODE_TX_DRV_EN | - PCIECFG_CISRREN | /* Reference clock term. 100 Ohm */ - PCIECFG_MACRO_ENABLE; - val |= PCIECFG_REFCLK_ENABLE; - val &= ~PCIECFG_DBG_OEN; - val &= ~PCIECFG_CLKREQ_B; - artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - usleep_range(5000, 6000); - - val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); - val |= NOCCFG_ENABLE_CLK_PCIE; - artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); - usleep_range(20, 30); - - val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val |= PCIECFG_PCLK_ENABLE | PCIECFG_PLL_ENABLE; - artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - usleep_range(6000, 7000); - - val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); - val &= ~NOCCFG_POWER_PCIE_IDLEREQ; - artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); -} - -static void artpec6_pcie_init_phy_a7(struct artpec6_pcie *artpec6_pcie) -{ - struct dw_pcie *pci = artpec6_pcie->pci; - u32 val; - bool extrefclk; - - /* Check if external reference clock is connected */ - val = artpec6_pcie_readl(artpec6_pcie, PCIESTAT); - extrefclk = !!(val & PCIESTAT_EXTREFCLK); - dev_dbg(pci->dev, "Using reference clock: %s\n", - extrefclk ? "external" : "internal"); - - val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */ - PCIECFG_PCLK_ENABLE; - if (extrefclk) - val |= PCIECFG_REFCLKSEL; - else - val &= ~PCIECFG_REFCLKSEL; - artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - usleep_range(10, 20); - - val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); - val |= NOCCFG_ENABLE_CLK_PCIE; - artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); - usleep_range(20, 30); - - val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); - val &= ~NOCCFG_POWER_PCIE_IDLEREQ; - artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); -} - -static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie) -{ - switch (artpec6_pcie->variant) { - case ARTPEC6: - artpec6_pcie_init_phy_a6(artpec6_pcie); - break; - case ARTPEC7: - artpec6_pcie_init_phy_a7(artpec6_pcie); - break; - } -} - -static void artpec6_pcie_set_nfts(struct artpec6_pcie *artpec6_pcie) -{ - struct dw_pcie *pci = artpec6_pcie->pci; - u32 val; - - if (artpec6_pcie->variant != ARTPEC7) - return; - - /* - * Increase the N_FTS (Number of Fast Training Sequences) - * to be transmitted when transitioning from L0s to L0. - */ - val = dw_pcie_readl_dbi(pci, ACK_F_ASPM_CTRL_OFF); - val &= ~ACK_N_FTS_MASK; - val |= ACK_N_FTS(180); - dw_pcie_writel_dbi(pci, ACK_F_ASPM_CTRL_OFF, val); - - /* - * Set the Number of Fast Training Sequences that the core - * advertises as its N_FTS during Gen2 or Gen3 link training. - */ - val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - val &= ~FAST_TRAINING_SEQ_MASK; - val |= FAST_TRAINING_SEQ(180); - dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); -} - -static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie) -{ - u32 val; - - val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - switch (artpec6_pcie->variant) { - case ARTPEC6: - val |= PCIECFG_CORE_RESET_REQ; - break; - case ARTPEC7: - val &= ~PCIECFG_NOC_RESET; - break; - } - artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); -} - -static void artpec6_pcie_deassert_core_reset(struct artpec6_pcie *artpec6_pcie) -{ - u32 val; - - val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - switch (artpec6_pcie->variant) { - case ARTPEC6: - val &= ~PCIECFG_CORE_RESET_REQ; - break; - case ARTPEC7: - val |= PCIECFG_NOC_RESET; - break; - } - artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - usleep_range(100, 200); -} - -static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie) -{ - struct dw_pcie *pci = artpec6_pcie->pci; - struct pcie_port *pp = &pci->pp; - - if (IS_ENABLED(CONFIG_PCI_MSI)) - dw_pcie_msi_init(pp); -} - -static int artpec6_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); - - artpec6_pcie_assert_core_reset(artpec6_pcie); - artpec6_pcie_init_phy(artpec6_pcie); - artpec6_pcie_deassert_core_reset(artpec6_pcie); - artpec6_pcie_wait_for_phy(artpec6_pcie); - artpec6_pcie_set_nfts(artpec6_pcie); - dw_pcie_setup_rc(pp); - artpec6_pcie_establish_link(pci); - dw_pcie_wait_for_link(pci); - artpec6_pcie_enable_interrupts(artpec6_pcie); - - return 0; -} - -static const struct dw_pcie_host_ops artpec6_pcie_host_ops = { - .host_init = artpec6_pcie_host_init, -}; - -static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, - struct platform_device *pdev) -{ - struct dw_pcie *pci = artpec6_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = pci->dev; - int ret; - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->msi_irq = platform_get_irq_byname(pdev, "msi"); - if (pp->msi_irq < 0) { - dev_err(dev, "failed to get MSI irq\n"); - return pp->msi_irq; - } - } - - pp->root_bus_nr = -1; - pp->ops = &artpec6_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); - enum pci_barno bar; - - artpec6_pcie_assert_core_reset(artpec6_pcie); - artpec6_pcie_init_phy(artpec6_pcie); - artpec6_pcie_deassert_core_reset(artpec6_pcie); - artpec6_pcie_wait_for_phy(artpec6_pcie); - artpec6_pcie_set_nfts(artpec6_pcie); - - for (bar = BAR_0; bar <= BAR_5; bar++) - dw_pcie_ep_reset_bar(pci, bar); -} - -static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - switch (type) { - case PCI_EPC_IRQ_LEGACY: - dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); - return -EINVAL; - case PCI_EPC_IRQ_MSI: - return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - default: - dev_err(pci->dev, "UNKNOWN IRQ type\n"); - } - - return 0; -} - -static struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = artpec6_pcie_ep_init, - .raise_irq = artpec6_pcie_raise_irq, -}; - -static int artpec6_add_pcie_ep(struct artpec6_pcie *artpec6_pcie, - struct platform_device *pdev) -{ - int ret; - struct dw_pcie_ep *ep; - struct resource *res; - struct device *dev = &pdev->dev; - struct dw_pcie *pci = artpec6_pcie->pci; - - ep = &pci->ep; - ep->ops = &pcie_ep_ops; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); - pci->dbi_base2 = devm_ioremap_resource(dev, res); - if (IS_ERR(pci->dbi_base2)) - return PTR_ERR(pci->dbi_base2); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); - if (!res) - return -EINVAL; - - ep->phys_base = res->start; - ep->addr_size = resource_size(res); - - ret = dw_pcie_ep_init(ep); - if (ret) { - dev_err(dev, "failed to initialize endpoint\n"); - return ret; - } - - return 0; -} - -static int artpec6_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_pcie *pci; - struct artpec6_pcie *artpec6_pcie; - struct resource *dbi_base; - struct resource *phy_base; - int ret; - const struct of_device_id *match; - const struct artpec_pcie_of_data *data; - enum artpec_pcie_variants variant; - enum dw_pcie_device_mode mode; - - match = of_match_device(artpec6_pcie_of_match, dev); - if (!match) - return -EINVAL; - - data = (struct artpec_pcie_of_data *)match->data; - variant = (enum artpec_pcie_variants)data->variant; - mode = (enum dw_pcie_device_mode)data->mode; - - artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL); - if (!artpec6_pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - artpec6_pcie->pci = pci; - artpec6_pcie->variant = variant; - artpec6_pcie->mode = mode; - - dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pci->dbi_base = devm_ioremap_resource(dev, dbi_base); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - - phy_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - artpec6_pcie->phy_base = devm_ioremap_resource(dev, phy_base); - if (IS_ERR(artpec6_pcie->phy_base)) - return PTR_ERR(artpec6_pcie->phy_base); - - artpec6_pcie->regmap = - syscon_regmap_lookup_by_phandle(dev->of_node, - "axis,syscon-pcie"); - if (IS_ERR(artpec6_pcie->regmap)) - return PTR_ERR(artpec6_pcie->regmap); - - platform_set_drvdata(pdev, artpec6_pcie); - - switch (artpec6_pcie->mode) { - case DW_PCIE_RC_TYPE: - if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_HOST)) - return -ENODEV; - - ret = artpec6_add_pcie_port(artpec6_pcie, pdev); - if (ret < 0) - return ret; - break; - case DW_PCIE_EP_TYPE: { - u32 val; - - if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_EP)) - return -ENODEV; - - val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); - val &= ~PCIECFG_DEVICE_TYPE_MASK; - artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - ret = artpec6_add_pcie_ep(artpec6_pcie, pdev); - if (ret < 0) - return ret; - break; - } - default: - dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode); - } - - return 0; -} - -static const struct artpec_pcie_of_data artpec6_pcie_rc_of_data = { - .variant = ARTPEC6, - .mode = DW_PCIE_RC_TYPE, -}; - -static const struct artpec_pcie_of_data artpec6_pcie_ep_of_data = { - .variant = ARTPEC6, - .mode = DW_PCIE_EP_TYPE, -}; - -static const struct artpec_pcie_of_data artpec7_pcie_rc_of_data = { - .variant = ARTPEC7, - .mode = DW_PCIE_RC_TYPE, -}; - -static const struct artpec_pcie_of_data artpec7_pcie_ep_of_data = { - .variant = ARTPEC7, - .mode = DW_PCIE_EP_TYPE, -}; - -static const struct of_device_id artpec6_pcie_of_match[] = { - { - .compatible = "axis,artpec6-pcie", - .data = &artpec6_pcie_rc_of_data, - }, - { - .compatible = "axis,artpec6-pcie-ep", - .data = &artpec6_pcie_ep_of_data, - }, - { - .compatible = "axis,artpec7-pcie", - .data = &artpec7_pcie_rc_of_data, - }, - { - .compatible = "axis,artpec7-pcie-ep", - .data = &artpec7_pcie_ep_of_data, - }, - {}, -}; - -static struct platform_driver artpec6_pcie_driver = { - .probe = artpec6_pcie_probe, - .driver = { - .name = "artpec6-pcie", - .of_match_table = artpec6_pcie_of_match, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(artpec6_pcie_driver); diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c deleted file mode 100644 index 1eec4415a77f..000000000000 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ /dev/null @@ -1,422 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/** - * Synopsys DesignWare PCIe Endpoint controller driver - * - * Copyright (C) 2017 Texas Instruments - * Author: Kishon Vijay Abraham I - */ - -#include - -#include "pcie-designware.h" -#include -#include - -void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) -{ - struct pci_epc *epc = ep->epc; - - pci_epc_linkup(epc); -} - -static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, - int flags) -{ - u32 reg; - - reg = PCI_BASE_ADDRESS_0 + (4 * bar); - dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi2(pci, reg, 0x0); - dw_pcie_writel_dbi(pci, reg, 0x0); - if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { - dw_pcie_writel_dbi2(pci, reg + 4, 0x0); - dw_pcie_writel_dbi(pci, reg + 4, 0x0); - } - dw_pcie_dbi_ro_wr_dis(pci); -} - -void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) -{ - __dw_pcie_ep_reset_bar(pci, bar, 0); -} - -static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, - struct pci_epf_header *hdr) -{ - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, hdr->vendorid); - dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, hdr->deviceid); - dw_pcie_writeb_dbi(pci, PCI_REVISION_ID, hdr->revid); - dw_pcie_writeb_dbi(pci, PCI_CLASS_PROG, hdr->progif_code); - dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, - hdr->subclass_code | hdr->baseclass_code << 8); - dw_pcie_writeb_dbi(pci, PCI_CACHE_LINE_SIZE, - hdr->cache_line_size); - dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_VENDOR_ID, - hdr->subsys_vendor_id); - dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_ID, hdr->subsys_id); - dw_pcie_writeb_dbi(pci, PCI_INTERRUPT_PIN, - hdr->interrupt_pin); - dw_pcie_dbi_ro_wr_dis(pci); - - return 0; -} - -static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar, - dma_addr_t cpu_addr, - enum dw_pcie_as_type as_type) -{ - int ret; - u32 free_win; - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - free_win = find_first_zero_bit(ep->ib_window_map, ep->num_ib_windows); - if (free_win >= ep->num_ib_windows) { - dev_err(pci->dev, "No free inbound window\n"); - return -EINVAL; - } - - ret = dw_pcie_prog_inbound_atu(pci, free_win, bar, cpu_addr, - as_type); - if (ret < 0) { - dev_err(pci->dev, "Failed to program IB window\n"); - return ret; - } - - ep->bar_to_atu[bar] = free_win; - set_bit(free_win, ep->ib_window_map); - - return 0; -} - -static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, - u64 pci_addr, size_t size) -{ - u32 free_win; - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows); - if (free_win >= ep->num_ob_windows) { - dev_err(pci->dev, "No free outbound window\n"); - return -EINVAL; - } - - dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM, - phys_addr, pci_addr, size); - - set_bit(free_win, ep->ob_window_map); - ep->outbound_addr[free_win] = phys_addr; - - return 0; -} - -static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, - struct pci_epf_bar *epf_bar) -{ - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar = epf_bar->barno; - u32 atu_index = ep->bar_to_atu[bar]; - - __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags); - - dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); - clear_bit(atu_index, ep->ib_window_map); -} - -static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, - struct pci_epf_bar *epf_bar) -{ - int ret; - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar = epf_bar->barno; - size_t size = epf_bar->size; - int flags = epf_bar->flags; - enum dw_pcie_as_type as_type; - u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); - - if (!(flags & PCI_BASE_ADDRESS_SPACE)) - as_type = DW_PCIE_AS_MEM; - else - as_type = DW_PCIE_AS_IO; - - ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type); - if (ret) - return ret; - - dw_pcie_dbi_ro_wr_en(pci); - - dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); - dw_pcie_writel_dbi(pci, reg, flags); - - if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { - dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1)); - dw_pcie_writel_dbi(pci, reg + 4, 0); - } - - dw_pcie_dbi_ro_wr_dis(pci); - - return 0; -} - -static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr, - u32 *atu_index) -{ - u32 index; - - for (index = 0; index < ep->num_ob_windows; index++) { - if (ep->outbound_addr[index] != addr) - continue; - *atu_index = index; - return 0; - } - - return -EINVAL; -} - -static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, - phys_addr_t addr) -{ - int ret; - u32 atu_index; - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - ret = dw_pcie_find_index(ep, addr, &atu_index); - if (ret < 0) - return; - - dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND); - clear_bit(atu_index, ep->ob_window_map); -} - -static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, - phys_addr_t addr, - u64 pci_addr, size_t size) -{ - int ret; - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - ret = dw_pcie_ep_outbound_atu(ep, addr, pci_addr, size); - if (ret) { - dev_err(pci->dev, "Failed to enable address\n"); - return ret; - } - - return 0; -} - -static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no) -{ - int val; - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); - if (!(val & MSI_CAP_MSI_EN_MASK)) - return -EINVAL; - - val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; - return val; -} - -static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) -{ - int val; - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); - val &= ~MSI_CAP_MMC_MASK; - val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK; - dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val); - dw_pcie_dbi_ro_wr_dis(pci); - - return 0; -} - -static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num) -{ - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - - if (!ep->ops->raise_irq) - return -EINVAL; - - return ep->ops->raise_irq(ep, func_no, type, interrupt_num); -} - -static void dw_pcie_ep_stop(struct pci_epc *epc) -{ - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - if (!pci->ops->stop_link) - return; - - pci->ops->stop_link(pci); -} - -static int dw_pcie_ep_start(struct pci_epc *epc) -{ - struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - if (!pci->ops->start_link) - return -EINVAL; - - return pci->ops->start_link(pci); -} - -static const struct pci_epc_ops epc_ops = { - .write_header = dw_pcie_ep_write_header, - .set_bar = dw_pcie_ep_set_bar, - .clear_bar = dw_pcie_ep_clear_bar, - .map_addr = dw_pcie_ep_map_addr, - .unmap_addr = dw_pcie_ep_unmap_addr, - .set_msi = dw_pcie_ep_set_msi, - .get_msi = dw_pcie_ep_get_msi, - .raise_irq = dw_pcie_ep_raise_irq, - .start = dw_pcie_ep_start, - .stop = dw_pcie_ep_stop, -}; - -int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, - u8 interrupt_num) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct pci_epc *epc = ep->epc; - u16 msg_ctrl, msg_data; - u32 msg_addr_lower, msg_addr_upper; - u64 msg_addr; - bool has_upper; - int ret; - - /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ - msg_ctrl = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); - has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); - msg_addr_lower = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32); - if (has_upper) { - msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32); - msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64); - } else { - msg_addr_upper = 0; - msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_32); - } - msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; - ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, - epc->mem->page_size); - if (ret) - return ret; - - writel(msg_data | (interrupt_num - 1), ep->msi_mem); - - dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys); - - return 0; -} - -void dw_pcie_ep_exit(struct dw_pcie_ep *ep) -{ - struct pci_epc *epc = ep->epc; - - pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem, - epc->mem->page_size); - - pci_epc_mem_exit(epc); -} - -int dw_pcie_ep_init(struct dw_pcie_ep *ep) -{ - int ret; - void *addr; - struct pci_epc *epc; - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct device *dev = pci->dev; - struct device_node *np = dev->of_node; - - if (!pci->dbi_base || !pci->dbi_base2) { - dev_err(dev, "dbi_base/dbi_base2 is not populated\n"); - return -EINVAL; - } - - ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows); - if (ret < 0) { - dev_err(dev, "Unable to read *num-ib-windows* property\n"); - return ret; - } - if (ep->num_ib_windows > MAX_IATU_IN) { - dev_err(dev, "Invalid *num-ib-windows*\n"); - return -EINVAL; - } - - ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows); - if (ret < 0) { - dev_err(dev, "Unable to read *num-ob-windows* property\n"); - return ret; - } - if (ep->num_ob_windows > MAX_IATU_OUT) { - dev_err(dev, "Invalid *num-ob-windows*\n"); - return -EINVAL; - } - - ep->ib_window_map = devm_kzalloc(dev, sizeof(long) * - BITS_TO_LONGS(ep->num_ib_windows), - GFP_KERNEL); - if (!ep->ib_window_map) - return -ENOMEM; - - ep->ob_window_map = devm_kzalloc(dev, sizeof(long) * - BITS_TO_LONGS(ep->num_ob_windows), - GFP_KERNEL); - if (!ep->ob_window_map) - return -ENOMEM; - - addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows, - GFP_KERNEL); - if (!addr) - return -ENOMEM; - ep->outbound_addr = addr; - - if (ep->ops->ep_init) - ep->ops->ep_init(ep); - - epc = devm_pci_epc_create(dev, &epc_ops); - if (IS_ERR(epc)) { - dev_err(dev, "Failed to create epc device\n"); - return PTR_ERR(epc); - } - - ret = of_property_read_u8(np, "max-functions", &epc->max_functions); - if (ret < 0) - epc->max_functions = 1; - - ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size, - ep->page_size); - if (ret < 0) { - dev_err(dev, "Failed to initialize address space\n"); - return ret; - } - - ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, - epc->mem->page_size); - if (!ep->msi_mem) { - dev_err(dev, "Failed to reserve memory for MSI\n"); - return -ENOMEM; - } - - epc->features = EPC_FEATURE_NO_LINKUP_NOTIFIER; - EPC_FEATURE_SET_BAR(epc->features, BAR_0); - - ep->epc = epc; - epc_set_drvdata(epc, ep); - dw_pcie_setup(pci); - - return 0; -} diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c deleted file mode 100644 index cba1432e395d..000000000000 --- a/drivers/pci/dwc/pcie-designware-host.c +++ /dev/null @@ -1,722 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Synopsys DesignWare PCIe host controller driver - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Jingoo Han - */ - -#include -#include -#include -#include -#include -#include - -#include "../pci.h" -#include "pcie-designware.h" - -static struct pci_ops dw_pcie_ops; - -static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) -{ - struct dw_pcie *pci; - - if (pp->ops->rd_own_conf) - return pp->ops->rd_own_conf(pp, where, size, val); - - pci = to_dw_pcie_from_pp(pp); - return dw_pcie_read(pci->dbi_base + where, size, val); -} - -static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) -{ - struct dw_pcie *pci; - - if (pp->ops->wr_own_conf) - return pp->ops->wr_own_conf(pp, where, size, val); - - pci = to_dw_pcie_from_pp(pp); - return dw_pcie_write(pci->dbi_base + where, size, val); -} - -static void dw_msi_ack_irq(struct irq_data *d) -{ - irq_chip_ack_parent(d); -} - -static void dw_msi_mask_irq(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void dw_msi_unmask_irq(struct irq_data *d) -{ - pci_msi_unmask_irq(d); - irq_chip_unmask_parent(d); -} - -static struct irq_chip dw_pcie_msi_irq_chip = { - .name = "PCI-MSI", - .irq_ack = dw_msi_ack_irq, - .irq_mask = dw_msi_mask_irq, - .irq_unmask = dw_msi_unmask_irq, -}; - -static struct msi_domain_info dw_pcie_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), - .chip = &dw_pcie_msi_irq_chip, -}; - -/* MSI int handler */ -irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) -{ - int i, pos, irq; - u32 val, num_ctrls; - irqreturn_t ret = IRQ_NONE; - - num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; - - for (i = 0; i < num_ctrls; i++) { - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + - (i * MSI_REG_CTRL_BLOCK_SIZE), - 4, &val); - if (!val) - continue; - - ret = IRQ_HANDLED; - pos = 0; - while ((pos = find_next_bit((unsigned long *) &val, - MAX_MSI_IRQS_PER_CTRL, - pos)) != MAX_MSI_IRQS_PER_CTRL) { - irq = irq_find_mapping(pp->irq_domain, - (i * MAX_MSI_IRQS_PER_CTRL) + - pos); - generic_handle_irq(irq); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + - (i * MSI_REG_CTRL_BLOCK_SIZE), - 4, 1 << pos); - pos++; - } - } - - return ret; -} - -/* Chained MSI interrupt service routine */ -static void dw_chained_msi_isr(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct pcie_port *pp; - - chained_irq_enter(chip, desc); - - pp = irq_desc_get_handler_data(desc); - dw_handle_msi_irq(pp); - - chained_irq_exit(chip, desc); -} - -static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct pcie_port *pp = irq_data_get_irq_chip_data(data); - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - u64 msi_target; - - if (pp->ops->get_msi_addr) - msi_target = pp->ops->get_msi_addr(pp); - else - msi_target = (u64)pp->msi_data; - - msg->address_lo = lower_32_bits(msi_target); - msg->address_hi = upper_32_bits(msi_target); - - if (pp->ops->get_msi_data) - msg->data = pp->ops->get_msi_data(pp, data->hwirq); - else - msg->data = data->hwirq; - - dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", - (int)data->hwirq, msg->address_hi, msg->address_lo); -} - -static int dw_pci_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) -{ - return -EINVAL; -} - -static void dw_pci_bottom_mask(struct irq_data *data) -{ - struct pcie_port *pp = irq_data_get_irq_chip_data(data); - unsigned int res, bit, ctrl; - unsigned long flags; - - raw_spin_lock_irqsave(&pp->lock, flags); - - if (pp->ops->msi_clear_irq) { - pp->ops->msi_clear_irq(pp, data->hwirq); - } else { - ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL; - res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; - bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; - - pp->irq_status[ctrl] &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, - pp->irq_status[ctrl]); - } - - raw_spin_unlock_irqrestore(&pp->lock, flags); -} - -static void dw_pci_bottom_unmask(struct irq_data *data) -{ - struct pcie_port *pp = irq_data_get_irq_chip_data(data); - unsigned int res, bit, ctrl; - unsigned long flags; - - raw_spin_lock_irqsave(&pp->lock, flags); - - if (pp->ops->msi_set_irq) { - pp->ops->msi_set_irq(pp, data->hwirq); - } else { - ctrl = data->hwirq / MAX_MSI_IRQS_PER_CTRL; - res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; - bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; - - pp->irq_status[ctrl] |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, - pp->irq_status[ctrl]); - } - - raw_spin_unlock_irqrestore(&pp->lock, flags); -} - -static void dw_pci_bottom_ack(struct irq_data *d) -{ - struct msi_desc *msi = irq_data_get_msi_desc(d); - struct pcie_port *pp; - - pp = msi_desc_to_pci_sysdata(msi); - - if (pp->ops->msi_irq_ack) - pp->ops->msi_irq_ack(d->hwirq, pp); -} - -static struct irq_chip dw_pci_msi_bottom_irq_chip = { - .name = "DWPCI-MSI", - .irq_ack = dw_pci_bottom_ack, - .irq_compose_msi_msg = dw_pci_setup_msi_msg, - .irq_set_affinity = dw_pci_msi_set_affinity, - .irq_mask = dw_pci_bottom_mask, - .irq_unmask = dw_pci_bottom_unmask, -}; - -static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs, - void *args) -{ - struct pcie_port *pp = domain->host_data; - unsigned long flags; - u32 i; - int bit; - - raw_spin_lock_irqsave(&pp->lock, flags); - - bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors, - order_base_2(nr_irqs)); - - raw_spin_unlock_irqrestore(&pp->lock, flags); - - if (bit < 0) - return -ENOSPC; - - for (i = 0; i < nr_irqs; i++) - irq_domain_set_info(domain, virq + i, bit + i, - &dw_pci_msi_bottom_irq_chip, - pp, handle_edge_irq, - NULL, NULL); - - return 0; -} - -static void dw_pcie_irq_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) -{ - struct irq_data *data = irq_domain_get_irq_data(domain, virq); - struct pcie_port *pp = irq_data_get_irq_chip_data(data); - unsigned long flags; - - raw_spin_lock_irqsave(&pp->lock, flags); - - bitmap_release_region(pp->msi_irq_in_use, data->hwirq, - order_base_2(nr_irqs)); - - raw_spin_unlock_irqrestore(&pp->lock, flags); -} - -static const struct irq_domain_ops dw_pcie_msi_domain_ops = { - .alloc = dw_pcie_irq_domain_alloc, - .free = dw_pcie_irq_domain_free, -}; - -int dw_pcie_allocate_domains(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); - - pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, - &dw_pcie_msi_domain_ops, pp); - if (!pp->irq_domain) { - dev_err(pci->dev, "Failed to create IRQ domain\n"); - return -ENOMEM; - } - - pp->msi_domain = pci_msi_create_irq_domain(fwnode, - &dw_pcie_msi_domain_info, - pp->irq_domain); - if (!pp->msi_domain) { - dev_err(pci->dev, "Failed to create MSI domain\n"); - irq_domain_remove(pp->irq_domain); - return -ENOMEM; - } - - return 0; -} - -void dw_pcie_free_msi(struct pcie_port *pp) -{ - irq_set_chained_handler(pp->msi_irq, NULL); - irq_set_handler_data(pp->msi_irq, NULL); - - irq_domain_remove(pp->msi_domain); - irq_domain_remove(pp->irq_domain); -} - -void dw_pcie_msi_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct device *dev = pci->dev; - struct page *page; - u64 msi_target; - - page = alloc_page(GFP_KERNEL); - pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, pp->msi_data)) { - dev_err(dev, "Failed to map MSI data\n"); - __free_page(page); - return; - } - msi_target = (u64)pp->msi_data; - - /* Program the msi_data */ - dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, - lower_32_bits(msi_target)); - dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, - upper_32_bits(msi_target)); -} - -int dw_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct device *dev = pci->dev; - struct device_node *np = dev->of_node; - struct platform_device *pdev = to_platform_device(dev); - struct resource_entry *win, *tmp; - struct pci_bus *bus, *child; - struct pci_host_bridge *bridge; - struct resource *cfg_res; - int ret; - - raw_spin_lock_init(&pci->pp.lock); - - cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); - if (cfg_res) { - pp->cfg0_size = resource_size(cfg_res) >> 1; - pp->cfg1_size = resource_size(cfg_res) >> 1; - pp->cfg0_base = cfg_res->start; - pp->cfg1_base = cfg_res->start + pp->cfg0_size; - } else if (!pp->va_cfg0_base) { - dev_err(dev, "Missing *config* reg space\n"); - } - - bridge = pci_alloc_host_bridge(0); - if (!bridge) - return -ENOMEM; - - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &bridge->windows, &pp->io_base); - if (ret) - return ret; - - ret = devm_request_pci_bus_resources(dev, &bridge->windows); - if (ret) - goto error; - - /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry_safe(win, tmp, &bridge->windows) { - switch (resource_type(win->res)) { - case IORESOURCE_IO: - ret = pci_remap_iospace(win->res, pp->io_base); - if (ret) { - dev_warn(dev, "Error %d: failed to map resource %pR\n", - ret, win->res); - resource_list_destroy_entry(win); - } else { - pp->io = win->res; - pp->io->name = "I/O"; - pp->io_size = resource_size(pp->io); - pp->io_bus_addr = pp->io->start - win->offset; - } - break; - case IORESOURCE_MEM: - pp->mem = win->res; - pp->mem->name = "MEM"; - pp->mem_size = resource_size(pp->mem); - pp->mem_bus_addr = pp->mem->start - win->offset; - break; - case 0: - pp->cfg = win->res; - pp->cfg0_size = resource_size(pp->cfg) >> 1; - pp->cfg1_size = resource_size(pp->cfg) >> 1; - pp->cfg0_base = pp->cfg->start; - pp->cfg1_base = pp->cfg->start + pp->cfg0_size; - break; - case IORESOURCE_BUS: - pp->busn = win->res; - break; - } - } - - if (!pci->dbi_base) { - pci->dbi_base = devm_pci_remap_cfgspace(dev, - pp->cfg->start, - resource_size(pp->cfg)); - if (!pci->dbi_base) { - dev_err(dev, "Error with ioremap\n"); - ret = -ENOMEM; - goto error; - } - } - - pp->mem_base = pp->mem->start; - - if (!pp->va_cfg0_base) { - pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, - pp->cfg0_base, pp->cfg0_size); - if (!pp->va_cfg0_base) { - dev_err(dev, "Error with ioremap in function\n"); - ret = -ENOMEM; - goto error; - } - } - - if (!pp->va_cfg1_base) { - pp->va_cfg1_base = devm_pci_remap_cfgspace(dev, - pp->cfg1_base, - pp->cfg1_size); - if (!pp->va_cfg1_base) { - dev_err(dev, "Error with ioremap\n"); - ret = -ENOMEM; - goto error; - } - } - - ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport); - if (ret) - pci->num_viewport = 2; - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - /* - * If a specific SoC driver needs to change the - * default number of vectors, it needs to implement - * the set_num_vectors callback. - */ - if (!pp->ops->set_num_vectors) { - pp->num_vectors = MSI_DEF_NUM_VECTORS; - } else { - pp->ops->set_num_vectors(pp); - - if (pp->num_vectors > MAX_MSI_IRQS || - pp->num_vectors == 0) { - dev_err(dev, - "Invalid number of vectors\n"); - goto error; - } - } - - if (!pp->ops->msi_host_init) { - ret = dw_pcie_allocate_domains(pp); - if (ret) - goto error; - - if (pp->msi_irq) - irq_set_chained_handler_and_data(pp->msi_irq, - dw_chained_msi_isr, - pp); - } else { - ret = pp->ops->msi_host_init(pp); - if (ret < 0) - goto error; - } - } - - if (pp->ops->host_init) { - ret = pp->ops->host_init(pp); - if (ret) - goto error; - } - - pp->root_bus_nr = pp->busn->start; - - bridge->dev.parent = dev; - bridge->sysdata = pp; - bridge->busnr = pp->root_bus_nr; - bridge->ops = &dw_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - ret = pci_scan_root_bus_bridge(bridge); - if (ret) - goto error; - - bus = bridge->bus; - - if (pp->ops->scan_bus) - pp->ops->scan_bus(pp); - - 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; - -error: - pci_free_host_bridge(bridge); - return ret; -} - -static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) -{ - int ret, type; - u32 busdev, cfg_size; - u64 cpu_addr; - void __iomem *va_cfg_base; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - if (pp->ops->rd_other_conf) - return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val); - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); - - if (bus->parent->number == pp->root_bus_nr) { - type = PCIE_ATU_TYPE_CFG0; - cpu_addr = pp->cfg0_base; - cfg_size = pp->cfg0_size; - va_cfg_base = pp->va_cfg0_base; - } else { - type = PCIE_ATU_TYPE_CFG1; - cpu_addr = pp->cfg1_base; - cfg_size = pp->cfg1_size; - va_cfg_base = pp->va_cfg1_base; - } - - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - type, cpu_addr, - busdev, cfg_size); - ret = dw_pcie_read(va_cfg_base + where, size, val); - if (pci->num_viewport <= 2) - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); - - return ret; -} - -static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) -{ - int ret, type; - u32 busdev, cfg_size; - u64 cpu_addr; - void __iomem *va_cfg_base; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - if (pp->ops->wr_other_conf) - return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val); - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); - - if (bus->parent->number == pp->root_bus_nr) { - type = PCIE_ATU_TYPE_CFG0; - cpu_addr = pp->cfg0_base; - cfg_size = pp->cfg0_size; - va_cfg_base = pp->va_cfg0_base; - } else { - type = PCIE_ATU_TYPE_CFG1; - cpu_addr = pp->cfg1_base; - cfg_size = pp->cfg1_size; - va_cfg_base = pp->va_cfg1_base; - } - - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - type, cpu_addr, - busdev, cfg_size); - ret = dw_pcie_write(va_cfg_base + where, size, val); - if (pci->num_viewport <= 2) - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); - - return ret; -} - -static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, - int dev) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - /* If there is no link, then there is no device */ - if (bus->number != pp->root_bus_nr) { - if (!dw_pcie_link_up(pci)) - return 0; - } - - /* Access only one slot on each root port */ - if (bus->number == pp->root_bus_nr && dev > 0) - return 0; - - return 1; -} - -static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) -{ - struct pcie_port *pp = bus->sysdata; - - if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - if (bus->number == pp->root_bus_nr) - return dw_pcie_rd_own_conf(pp, where, size, val); - - return dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); -} - -static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) -{ - struct pcie_port *pp = bus->sysdata; - - if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (bus->number == pp->root_bus_nr) - return dw_pcie_wr_own_conf(pp, where, size, val); - - return dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); -} - -static struct pci_ops dw_pcie_ops = { - .read = dw_pcie_rd_conf, - .write = dw_pcie_wr_conf, -}; - -static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) -{ - u32 val; - - val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT); - if (val == 0xffffffff) - return 1; - - return 0; -} - -void dw_pcie_setup_rc(struct pcie_port *pp) -{ - u32 val, ctrl, num_ctrls; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - dw_pcie_setup(pci); - - num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; - - /* Initialize IRQ Status array */ - for (ctrl = 0; ctrl < num_ctrls; ctrl++) - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + - (ctrl * MSI_REG_CTRL_BLOCK_SIZE), - 4, &pp->irq_status[ctrl]); - - /* Setup RC BARs */ - dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); - dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); - - /* Setup interrupt pins */ - dw_pcie_dbi_ro_wr_en(pci); - val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE); - val &= 0xffff00ff; - val |= 0x00000100; - dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val); - dw_pcie_dbi_ro_wr_dis(pci); - - /* Setup bus numbers */ - val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS); - val &= 0xff000000; - val |= 0x00ff0100; - dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val); - - /* Setup command register */ - val = dw_pcie_readl_dbi(pci, PCI_COMMAND); - val &= 0xffff0000; - val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR; - dw_pcie_writel_dbi(pci, PCI_COMMAND, val); - - /* - * If the platform provides ->rd_other_conf, it means the platform - * uses its own address translation component rather than ATU, so - * we should not program the ATU here. - */ - if (!pp->ops->rd_other_conf) { - /* Get iATU unroll support */ - pci->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pci); - dev_dbg(pci->dev, "iATU unroll: %s\n", - pci->iatu_unroll_enabled ? "enabled" : "disabled"); - - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, - PCIE_ATU_TYPE_MEM, pp->mem_base, - pp->mem_bus_addr, pp->mem_size); - if (pci->num_viewport > 2) - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2, - PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); - } - - dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); - - /* Enable write permission for the DBI read-only register */ - dw_pcie_dbi_ro_wr_en(pci); - /* Program correct class for RC */ - dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI); - /* Better disable write permission right after the update */ - dw_pcie_dbi_ro_wr_dis(pci); - - dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val); - val |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); -} diff --git a/drivers/pci/dwc/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c deleted file mode 100644 index 5937fed4c938..000000000000 --- a/drivers/pci/dwc/pcie-designware-plat.c +++ /dev/null @@ -1,259 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe RC driver for Synopsys DesignWare Core - * - * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com) - * - * Authors: Joao Pinto - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -struct dw_plat_pcie { - struct dw_pcie *pci; - struct regmap *regmap; - enum dw_pcie_device_mode mode; -}; - -struct dw_plat_pcie_of_data { - enum dw_pcie_device_mode mode; -}; - -static const struct of_device_id dw_plat_pcie_of_match[]; - -static int dw_plat_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - dw_pcie_setup_rc(pp); - dw_pcie_wait_for_link(pci); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - dw_pcie_msi_init(pp); - - return 0; -} - -static void dw_plat_set_num_vectors(struct pcie_port *pp) -{ - pp->num_vectors = MAX_MSI_IRQS; -} - -static const struct dw_pcie_host_ops dw_plat_pcie_host_ops = { - .host_init = dw_plat_pcie_host_init, - .set_num_vectors = dw_plat_set_num_vectors, -}; - -static int dw_plat_pcie_establish_link(struct dw_pcie *pci) -{ - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .start_link = dw_plat_pcie_establish_link, -}; - -static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - enum pci_barno bar; - - for (bar = BAR_0; bar <= BAR_5; bar++) - dw_pcie_ep_reset_bar(pci, bar); -} - -static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u8 interrupt_num) -{ - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - switch (type) { - case PCI_EPC_IRQ_LEGACY: - dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); - return -EINVAL; - case PCI_EPC_IRQ_MSI: - return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - default: - dev_err(pci->dev, "UNKNOWN IRQ type\n"); - } - - return 0; -} - -static struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = dw_plat_pcie_ep_init, - .raise_irq = dw_plat_pcie_ep_raise_irq, -}; - -static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie, - struct platform_device *pdev) -{ - struct dw_pcie *pci = dw_plat_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = &pdev->dev; - int ret; - - pp->irq = platform_get_irq(pdev, 1); - if (pp->irq < 0) - return pp->irq; - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->msi_irq = platform_get_irq(pdev, 0); - if (pp->msi_irq < 0) - return pp->msi_irq; - } - - pp->root_bus_nr = -1; - pp->ops = &dw_plat_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "Failed to initialize host\n"); - return ret; - } - - return 0; -} - -static int dw_plat_add_pcie_ep(struct dw_plat_pcie *dw_plat_pcie, - struct platform_device *pdev) -{ - int ret; - struct dw_pcie_ep *ep; - struct resource *res; - struct device *dev = &pdev->dev; - struct dw_pcie *pci = dw_plat_pcie->pci; - - ep = &pci->ep; - ep->ops = &pcie_ep_ops; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); - pci->dbi_base2 = devm_ioremap_resource(dev, res); - if (IS_ERR(pci->dbi_base2)) - return PTR_ERR(pci->dbi_base2); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); - if (!res) - return -EINVAL; - - ep->phys_base = res->start; - ep->addr_size = resource_size(res); - - ret = dw_pcie_ep_init(ep); - if (ret) { - dev_err(dev, "Failed to initialize endpoint\n"); - return ret; - } - return 0; -} - -static int dw_plat_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_plat_pcie *dw_plat_pcie; - struct dw_pcie *pci; - struct resource *res; /* Resource from DT */ - int ret; - const struct of_device_id *match; - const struct dw_plat_pcie_of_data *data; - enum dw_pcie_device_mode mode; - - match = of_match_device(dw_plat_pcie_of_match, dev); - if (!match) - return -EINVAL; - - data = (struct dw_plat_pcie_of_data *)match->data; - mode = (enum dw_pcie_device_mode)data->mode; - - dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL); - if (!dw_plat_pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - dw_plat_pcie->pci = pci; - dw_plat_pcie->mode = mode; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - if (!res) - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - pci->dbi_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - - platform_set_drvdata(pdev, dw_plat_pcie); - - switch (dw_plat_pcie->mode) { - case DW_PCIE_RC_TYPE: - if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_HOST)) - return -ENODEV; - - ret = dw_plat_add_pcie_port(dw_plat_pcie, pdev); - if (ret < 0) - return ret; - break; - case DW_PCIE_EP_TYPE: - if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_EP)) - return -ENODEV; - - ret = dw_plat_add_pcie_ep(dw_plat_pcie, pdev); - if (ret < 0) - return ret; - break; - default: - dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode); - } - - return 0; -} - -static const struct dw_plat_pcie_of_data dw_plat_pcie_rc_of_data = { - .mode = DW_PCIE_RC_TYPE, -}; - -static const struct dw_plat_pcie_of_data dw_plat_pcie_ep_of_data = { - .mode = DW_PCIE_EP_TYPE, -}; - -static const struct of_device_id dw_plat_pcie_of_match[] = { - { - .compatible = "snps,dw-pcie", - .data = &dw_plat_pcie_rc_of_data, - }, - { - .compatible = "snps,dw-pcie-ep", - .data = &dw_plat_pcie_ep_of_data, - }, - {}, -}; - -static struct platform_driver dw_plat_pcie_driver = { - .driver = { - .name = "dw-pcie", - .of_match_table = dw_plat_pcie_of_match, - .suppress_bind_attrs = true, - }, - .probe = dw_plat_pcie_probe, -}; -builtin_platform_driver(dw_plat_pcie_driver); diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c deleted file mode 100644 index 778c4f76a884..000000000000 --- a/drivers/pci/dwc/pcie-designware.c +++ /dev/null @@ -1,394 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Synopsys DesignWare PCIe host controller driver - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Jingoo Han - */ - -#include -#include -#include - -#include "pcie-designware.h" - -/* PCIe Port Logic registers */ -#define PLR_OFFSET 0x700 -#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) -#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4) -#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29) - -int dw_pcie_read(void __iomem *addr, int size, u32 *val) -{ - if ((uintptr_t)addr & (size - 1)) { - *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; -} - -int dw_pcie_write(void __iomem *addr, int size, u32 val) -{ - if ((uintptr_t)addr & (size - 1)) - 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; -} - -u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size) -{ - int ret; - u32 val; - - if (pci->ops->read_dbi) - return pci->ops->read_dbi(pci, base, reg, size); - - ret = dw_pcie_read(base + reg, size, &val); - if (ret) - dev_err(pci->dev, "Read DBI address failed\n"); - - return val; -} - -void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size, u32 val) -{ - int ret; - - if (pci->ops->write_dbi) { - pci->ops->write_dbi(pci, base, reg, size, val); - return; - } - - ret = dw_pcie_write(base + reg, size, val); - if (ret) - dev_err(pci->dev, "Write DBI address failed\n"); -} - -static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg) -{ - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - - return dw_pcie_readl_dbi(pci, offset + reg); -} - -static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg, - u32 val) -{ - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - - dw_pcie_writel_dbi(pci, offset + reg, val); -} - -static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index, - int type, u64 cpu_addr, - u64 pci_addr, u32 size) -{ - u32 retries, val; - - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, - lower_32_bits(cpu_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, - upper_32_bits(cpu_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT, - lower_32_bits(cpu_addr + size - 1)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, - lower_32_bits(pci_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, - upper_32_bits(pci_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, - type); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, - PCIE_ATU_ENABLE); - - /* - * Make sure ATU enable takes effect before any subsequent config - * and I/O accesses. - */ - for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_ob_unroll(pci, index, - PCIE_ATU_UNR_REGION_CTRL2); - if (val & PCIE_ATU_ENABLE) - return; - - usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); - } - dev_err(pci->dev, "Outbound iATU is not being enabled\n"); -} - -void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, - u64 cpu_addr, u64 pci_addr, u32 size) -{ - u32 retries, val; - - if (pci->ops->cpu_addr_fixup) - cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr); - - if (pci->iatu_unroll_enabled) { - dw_pcie_prog_outbound_atu_unroll(pci, index, type, cpu_addr, - pci_addr, size); - return; - } - - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, - PCIE_ATU_REGION_OUTBOUND | index); - dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, - lower_32_bits(cpu_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, - upper_32_bits(cpu_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, - lower_32_bits(cpu_addr + size - 1)); - dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, - lower_32_bits(pci_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, - upper_32_bits(pci_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE); - - /* - * Make sure ATU enable takes effect before any subsequent config - * and I/O accesses. - */ - for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); - if (val & PCIE_ATU_ENABLE) - return; - - usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); - } - dev_err(pci->dev, "Outbound iATU is not being enabled\n"); -} - -static u32 dw_pcie_readl_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg) -{ - u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); - - return dw_pcie_readl_dbi(pci, offset + reg); -} - -static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg, - u32 val) -{ - u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); - - dw_pcie_writel_dbi(pci, offset + reg, val); -} - -static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index, - int bar, u64 cpu_addr, - enum dw_pcie_as_type as_type) -{ - int type; - u32 retries, val; - - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, - lower_32_bits(cpu_addr)); - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, - upper_32_bits(cpu_addr)); - - switch (as_type) { - case DW_PCIE_AS_MEM: - type = PCIE_ATU_TYPE_MEM; - break; - case DW_PCIE_AS_IO: - type = PCIE_ATU_TYPE_IO; - break; - default: - return -EINVAL; - } - - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, type); - dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, - PCIE_ATU_ENABLE | - PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); - - /* - * Make sure ATU enable takes effect before any subsequent config - * and I/O accesses. - */ - for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_ib_unroll(pci, index, - PCIE_ATU_UNR_REGION_CTRL2); - if (val & PCIE_ATU_ENABLE) - return 0; - - usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); - } - dev_err(pci->dev, "Inbound iATU is not being enabled\n"); - - return -EBUSY; -} - -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar, - u64 cpu_addr, enum dw_pcie_as_type as_type) -{ - int type; - u32 retries, val; - - if (pci->iatu_unroll_enabled) - return dw_pcie_prog_inbound_atu_unroll(pci, index, bar, - cpu_addr, as_type); - - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | - index); - dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr)); - dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr)); - - switch (as_type) { - case DW_PCIE_AS_MEM: - type = PCIE_ATU_TYPE_MEM; - break; - case DW_PCIE_AS_IO: - type = PCIE_ATU_TYPE_IO; - break; - default: - return -EINVAL; - } - - dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE - | PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); - - /* - * Make sure ATU enable takes effect before any subsequent config - * and I/O accesses. - */ - for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); - if (val & PCIE_ATU_ENABLE) - return 0; - - usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); - } - dev_err(pci->dev, "Inbound iATU is not being enabled\n"); - - return -EBUSY; -} - -void dw_pcie_disable_atu(struct dw_pcie *pci, int index, - enum dw_pcie_region_type type) -{ - int region; - - switch (type) { - case DW_PCIE_REGION_INBOUND: - region = PCIE_ATU_REGION_INBOUND; - break; - case DW_PCIE_REGION_OUTBOUND: - region = PCIE_ATU_REGION_OUTBOUND; - break; - default: - return; - } - - dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index); - dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~PCIE_ATU_ENABLE); -} - -int dw_pcie_wait_for_link(struct dw_pcie *pci) -{ - int retries; - - /* Check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (dw_pcie_link_up(pci)) { - dev_info(pci->dev, "Link up\n"); - return 0; - } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); - } - - dev_err(pci->dev, "Phy link never came up\n"); - - return -ETIMEDOUT; -} - -int dw_pcie_link_up(struct dw_pcie *pci) -{ - u32 val; - - if (pci->ops->link_up) - return pci->ops->link_up(pci); - - val = readl(pci->dbi_base + PCIE_PHY_DEBUG_R1); - return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) && - (!(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING))); -} - -void dw_pcie_setup(struct dw_pcie *pci) -{ - int ret; - u32 val; - u32 lanes; - struct device *dev = pci->dev; - struct device_node *np = dev->of_node; - - ret = of_property_read_u32(np, "num-lanes", &lanes); - if (ret) - lanes = 0; - - /* Set the number of lanes */ - val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); - val &= ~PORT_LINK_MODE_MASK; - switch (lanes) { - case 1: - val |= PORT_LINK_MODE_1_LANES; - break; - case 2: - val |= PORT_LINK_MODE_2_LANES; - break; - case 4: - val |= PORT_LINK_MODE_4_LANES; - break; - case 8: - val |= PORT_LINK_MODE_8_LANES; - break; - default: - dev_err(pci->dev, "num-lanes %u: invalid value\n", lanes); - return; - } - dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); - - /* Set link width speed control register */ - val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - val &= ~PORT_LOGIC_LINK_WIDTH_MASK; - switch (lanes) { - case 1: - val |= PORT_LOGIC_LINK_WIDTH_1_LANES; - break; - case 2: - val |= PORT_LOGIC_LINK_WIDTH_2_LANES; - break; - case 4: - val |= PORT_LOGIC_LINK_WIDTH_4_LANES; - break; - case 8: - val |= PORT_LOGIC_LINK_WIDTH_8_LANES; - break; - } - dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); -} diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h deleted file mode 100644 index bee4e2535a61..000000000000 --- a/drivers/pci/dwc/pcie-designware.h +++ /dev/null @@ -1,387 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Synopsys DesignWare PCIe host controller driver - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Jingoo Han - */ - -#ifndef _PCIE_DESIGNWARE_H -#define _PCIE_DESIGNWARE_H - -#include -#include -#include -#include - -#include -#include - -/* Parameters for the waiting for link up routine */ -#define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_USLEEP_MIN 90000 -#define LINK_WAIT_USLEEP_MAX 100000 - -/* Parameters for the waiting for iATU enabled routine */ -#define LINK_WAIT_MAX_IATU_RETRIES 5 -#define LINK_WAIT_IATU_MIN 9000 -#define LINK_WAIT_IATU_MAX 10000 - -/* Synopsys-specific PCIe configuration registers */ -#define PCIE_PORT_LINK_CONTROL 0x710 -#define PORT_LINK_MODE_MASK (0x3f << 16) -#define PORT_LINK_MODE_1_LANES (0x1 << 16) -#define PORT_LINK_MODE_2_LANES (0x3 << 16) -#define PORT_LINK_MODE_4_LANES (0x7 << 16) -#define PORT_LINK_MODE_8_LANES (0xf << 16) - -#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C -#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) -#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8) -#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) -#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) -#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) -#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8) - -#define PCIE_MSI_ADDR_LO 0x820 -#define PCIE_MSI_ADDR_HI 0x824 -#define PCIE_MSI_INTR0_ENABLE 0x828 -#define PCIE_MSI_INTR0_MASK 0x82C -#define PCIE_MSI_INTR0_STATUS 0x830 - -#define PCIE_ATU_VIEWPORT 0x900 -#define PCIE_ATU_REGION_INBOUND (0x1 << 31) -#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) -#define PCIE_ATU_REGION_INDEX2 (0x2 << 0) -#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) -#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) -#define PCIE_ATU_CR1 0x904 -#define PCIE_ATU_TYPE_MEM (0x0 << 0) -#define PCIE_ATU_TYPE_IO (0x2 << 0) -#define PCIE_ATU_TYPE_CFG0 (0x4 << 0) -#define PCIE_ATU_TYPE_CFG1 (0x5 << 0) -#define PCIE_ATU_CR2 0x908 -#define PCIE_ATU_ENABLE (0x1 << 31) -#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) -#define PCIE_ATU_LOWER_BASE 0x90C -#define PCIE_ATU_UPPER_BASE 0x910 -#define PCIE_ATU_LIMIT 0x914 -#define PCIE_ATU_LOWER_TARGET 0x918 -#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) -#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) -#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) -#define PCIE_ATU_UPPER_TARGET 0x91C - -#define PCIE_MISC_CONTROL_1_OFF 0x8BC -#define PCIE_DBI_RO_WR_EN (0x1 << 0) - -/* - * iATU Unroll-specific register definitions - * From 4.80 core version the address translation will be made by unroll - */ -#define PCIE_ATU_UNR_REGION_CTRL1 0x00 -#define PCIE_ATU_UNR_REGION_CTRL2 0x04 -#define PCIE_ATU_UNR_LOWER_BASE 0x08 -#define PCIE_ATU_UNR_UPPER_BASE 0x0C -#define PCIE_ATU_UNR_LIMIT 0x10 -#define PCIE_ATU_UNR_LOWER_TARGET 0x14 -#define PCIE_ATU_UNR_UPPER_TARGET 0x18 - -/* Register address builder */ -#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \ - ((0x3 << 20) | ((region) << 9)) - -#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \ - ((0x3 << 20) | ((region) << 9) | (0x1 << 8)) - -#define MSI_MESSAGE_CONTROL 0x52 -#define MSI_CAP_MMC_SHIFT 1 -#define MSI_CAP_MMC_MASK (7 << MSI_CAP_MMC_SHIFT) -#define MSI_CAP_MME_SHIFT 4 -#define MSI_CAP_MSI_EN_MASK 0x1 -#define MSI_CAP_MME_MASK (7 << MSI_CAP_MME_SHIFT) -#define MSI_MESSAGE_ADDR_L32 0x54 -#define MSI_MESSAGE_ADDR_U32 0x58 -#define MSI_MESSAGE_DATA_32 0x58 -#define MSI_MESSAGE_DATA_64 0x5C - -#define MAX_MSI_IRQS 256 -#define MAX_MSI_IRQS_PER_CTRL 32 -#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL) -#define MSI_REG_CTRL_BLOCK_SIZE 12 -#define MSI_DEF_NUM_VECTORS 32 - -/* Maximum number of inbound/outbound iATUs */ -#define MAX_IATU_IN 256 -#define MAX_IATU_OUT 256 - -struct pcie_port; -struct dw_pcie; -struct dw_pcie_ep; - -enum dw_pcie_region_type { - DW_PCIE_REGION_UNKNOWN, - DW_PCIE_REGION_INBOUND, - DW_PCIE_REGION_OUTBOUND, -}; - -enum dw_pcie_device_mode { - DW_PCIE_UNKNOWN_TYPE, - DW_PCIE_EP_TYPE, - DW_PCIE_LEG_EP_TYPE, - DW_PCIE_RC_TYPE, -}; - -struct dw_pcie_host_ops { - int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); - int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); - int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 *val); - int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 val); - int (*host_init)(struct pcie_port *pp); - void (*msi_set_irq)(struct pcie_port *pp, int irq); - void (*msi_clear_irq)(struct pcie_port *pp, int irq); - phys_addr_t (*get_msi_addr)(struct pcie_port *pp); - u32 (*get_msi_data)(struct pcie_port *pp, int pos); - void (*scan_bus)(struct pcie_port *pp); - void (*set_num_vectors)(struct pcie_port *pp); - int (*msi_host_init)(struct pcie_port *pp); - void (*msi_irq_ack)(int irq, struct pcie_port *pp); -}; - -struct pcie_port { - u8 root_bus_nr; - u64 cfg0_base; - void __iomem *va_cfg0_base; - u32 cfg0_size; - u64 cfg1_base; - void __iomem *va_cfg1_base; - u32 cfg1_size; - resource_size_t io_base; - phys_addr_t io_bus_addr; - u32 io_size; - u64 mem_base; - phys_addr_t mem_bus_addr; - u32 mem_size; - struct resource *cfg; - struct resource *io; - struct resource *mem; - struct resource *busn; - int irq; - const struct dw_pcie_host_ops *ops; - int msi_irq; - struct irq_domain *irq_domain; - struct irq_domain *msi_domain; - dma_addr_t msi_data; - u32 num_vectors; - u32 irq_status[MAX_MSI_CTRLS]; - raw_spinlock_t lock; - DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); -}; - -enum dw_pcie_as_type { - DW_PCIE_AS_UNKNOWN, - DW_PCIE_AS_MEM, - DW_PCIE_AS_IO, -}; - -struct dw_pcie_ep_ops { - void (*ep_init)(struct dw_pcie_ep *ep); - int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u8 interrupt_num); -}; - -struct dw_pcie_ep { - struct pci_epc *epc; - struct dw_pcie_ep_ops *ops; - phys_addr_t phys_base; - size_t addr_size; - size_t page_size; - u8 bar_to_atu[6]; - phys_addr_t *outbound_addr; - unsigned long *ib_window_map; - unsigned long *ob_window_map; - u32 num_ib_windows; - u32 num_ob_windows; - void __iomem *msi_mem; - phys_addr_t msi_mem_phys; -}; - -struct dw_pcie_ops { - u64 (*cpu_addr_fixup)(struct dw_pcie *pcie, u64 cpu_addr); - u32 (*read_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, - size_t size); - void (*write_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, - size_t size, u32 val); - int (*link_up)(struct dw_pcie *pcie); - int (*start_link)(struct dw_pcie *pcie); - void (*stop_link)(struct dw_pcie *pcie); -}; - -struct dw_pcie { - struct device *dev; - void __iomem *dbi_base; - void __iomem *dbi_base2; - u32 num_viewport; - u8 iatu_unroll_enabled; - struct pcie_port pp; - struct dw_pcie_ep ep; - const struct dw_pcie_ops *ops; -}; - -#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) - -#define to_dw_pcie_from_ep(endpoint) \ - container_of((endpoint), struct dw_pcie, ep) - -int dw_pcie_read(void __iomem *addr, int size, u32 *val); -int dw_pcie_write(void __iomem *addr, int size, u32 val); - -u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size); -void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size, u32 val); -int dw_pcie_link_up(struct dw_pcie *pci); -int dw_pcie_wait_for_link(struct dw_pcie *pci); -void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, - int type, u64 cpu_addr, u64 pci_addr, - u32 size); -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar, - u64 cpu_addr, enum dw_pcie_as_type as_type); -void dw_pcie_disable_atu(struct dw_pcie *pci, int index, - enum dw_pcie_region_type type); -void dw_pcie_setup(struct dw_pcie *pci); - -static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) -{ - __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x4, val); -} - -static inline u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) -{ - return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x4); -} - -static inline void dw_pcie_writew_dbi(struct dw_pcie *pci, u32 reg, u16 val) -{ - __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x2, val); -} - -static inline u16 dw_pcie_readw_dbi(struct dw_pcie *pci, u32 reg) -{ - return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x2); -} - -static inline void dw_pcie_writeb_dbi(struct dw_pcie *pci, u32 reg, u8 val) -{ - __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x1, val); -} - -static inline u8 dw_pcie_readb_dbi(struct dw_pcie *pci, u32 reg) -{ - return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x1); -} - -static inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, u32 reg, u32 val) -{ - __dw_pcie_write_dbi(pci, pci->dbi_base2, reg, 0x4, val); -} - -static inline u32 dw_pcie_readl_dbi2(struct dw_pcie *pci, u32 reg) -{ - return __dw_pcie_read_dbi(pci, pci->dbi_base2, reg, 0x4); -} - -static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci) -{ - u32 reg; - u32 val; - - reg = PCIE_MISC_CONTROL_1_OFF; - val = dw_pcie_readl_dbi(pci, reg); - val |= PCIE_DBI_RO_WR_EN; - dw_pcie_writel_dbi(pci, reg, val); -} - -static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci) -{ - u32 reg; - u32 val; - - reg = PCIE_MISC_CONTROL_1_OFF; - val = dw_pcie_readl_dbi(pci, reg); - val &= ~PCIE_DBI_RO_WR_EN; - dw_pcie_writel_dbi(pci, reg, val); -} - -#ifdef CONFIG_PCIE_DW_HOST -irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); -void dw_pcie_msi_init(struct pcie_port *pp); -void dw_pcie_free_msi(struct pcie_port *pp); -void dw_pcie_setup_rc(struct pcie_port *pp); -int dw_pcie_host_init(struct pcie_port *pp); -int dw_pcie_allocate_domains(struct pcie_port *pp); -#else -static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) -{ - return IRQ_NONE; -} - -static inline void dw_pcie_msi_init(struct pcie_port *pp) -{ -} - -static inline void dw_pcie_free_msi(struct pcie_port *pp) -{ -} - -static inline void dw_pcie_setup_rc(struct pcie_port *pp) -{ -} - -static inline int dw_pcie_host_init(struct pcie_port *pp) -{ - return 0; -} - -static inline int dw_pcie_allocate_domains(struct pcie_port *pp) -{ - return 0; -} -#endif - -#ifdef CONFIG_PCIE_DW_EP -void dw_pcie_ep_linkup(struct dw_pcie_ep *ep); -int dw_pcie_ep_init(struct dw_pcie_ep *ep); -void dw_pcie_ep_exit(struct dw_pcie_ep *ep); -int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, - u8 interrupt_num); -void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); -#else -static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) -{ -} - -static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep) -{ - return 0; -} - -static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep) -{ -} - -static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, - u8 interrupt_num) -{ - return 0; -} - -static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) -{ -} -#endif -#endif /* _PCIE_DESIGNWARE_H */ diff --git a/drivers/pci/dwc/pcie-hisi.c b/drivers/pci/dwc/pcie-hisi.c deleted file mode 100644 index 2658aaebb993..000000000000 --- a/drivers/pci/dwc/pcie-hisi.c +++ /dev/null @@ -1,398 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for HiSilicon SoCs - * - * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com - * - * Authors: Zhou Wang - * Dacai Zhu - * Gabriele Paoloni - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../pci.h" - -#if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) - -static int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) -{ - struct pci_config_window *cfg = bus->sysdata; - int dev = PCI_SLOT(devfn); - - if (bus->number == cfg->busr.start) { - /* access only one slot on each root port */ - if (dev > 0) - return PCIBIOS_DEVICE_NOT_FOUND; - else - return pci_generic_config_read32(bus, devfn, where, - size, val); - } - - return pci_generic_config_read(bus, devfn, where, size, val); -} - -static int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) -{ - struct pci_config_window *cfg = bus->sysdata; - int dev = PCI_SLOT(devfn); - - if (bus->number == cfg->busr.start) { - /* access only one slot on each root port */ - if (dev > 0) - return PCIBIOS_DEVICE_NOT_FOUND; - else - return pci_generic_config_write32(bus, devfn, where, - size, val); - } - - return pci_generic_config_write(bus, devfn, where, size, val); -} - -static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, - int where) -{ - struct pci_config_window *cfg = bus->sysdata; - void __iomem *reg_base = cfg->priv; - - if (bus->number == cfg->busr.start) - return reg_base + where; - else - return pci_ecam_map_bus(bus, devfn, where); -} - -#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) - -static int hisi_pcie_init(struct pci_config_window *cfg) -{ - struct device *dev = cfg->parent; - struct acpi_device *adev = to_acpi_device(dev); - struct acpi_pci_root *root = acpi_driver_data(adev); - struct resource *res; - void __iomem *reg_base; - int ret; - - /* - * Retrieve RC base and size from a HISI0081 device with _UID - * matching our segment. - */ - res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); - if (!res) - return -ENOMEM; - - ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res); - if (ret) { - dev_err(dev, "can't get rc base address\n"); - return -ENOMEM; - } - - reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); - if (!reg_base) - return -ENOMEM; - - cfg->priv = reg_base; - return 0; -} - -struct pci_ecam_ops hisi_pcie_ops = { - .bus_shift = 20, - .init = hisi_pcie_init, - .pci_ops = { - .map_bus = hisi_pcie_map_bus, - .read = hisi_pcie_rd_conf, - .write = hisi_pcie_wr_conf, - } -}; - -#endif - -#ifdef CONFIG_PCI_HISI - -#include "pcie-designware.h" - -#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 -#define PCIE_HIP06_CTRL_OFF 0x1000 -#define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c) -#define PCIE_LTSSM_LINKUP_STATE 0x11 -#define PCIE_LTSSM_STATE_MASK 0x3F - -#define to_hisi_pcie(x) dev_get_drvdata((x)->dev) - -struct hisi_pcie; - -struct pcie_soc_ops { - int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie); -}; - -struct hisi_pcie { - struct dw_pcie *pci; - struct regmap *subctrl; - u32 port_id; - const struct pcie_soc_ops *soc_ops; -}; - -/* HipXX PCIe host only supports 32-bit config access */ -static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size, - u32 *val) -{ - u32 reg; - u32 reg_val; - void *walker = ®_val; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - walker += (where & 0x3); - reg = where & ~0x3; - reg_val = dw_pcie_readl_dbi(pci, reg); - - if (size == 1) - *val = *(u8 __force *) walker; - else if (size == 2) - *val = *(u16 __force *) walker; - else if (size == 4) - *val = reg_val; - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - return PCIBIOS_SUCCESSFUL; -} - -/* HipXX PCIe host only supports 32-bit config access */ -static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, - u32 val) -{ - u32 reg_val; - u32 reg; - void *walker = ®_val; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - walker += (where & 0x3); - reg = where & ~0x3; - if (size == 4) - dw_pcie_writel_dbi(pci, reg, val); - else if (size == 2) { - reg_val = dw_pcie_readl_dbi(pci, reg); - *(u16 __force *) walker = val; - dw_pcie_writel_dbi(pci, reg, reg_val); - } else if (size == 1) { - reg_val = dw_pcie_readl_dbi(pci, reg); - *(u8 __force *) walker = val; - dw_pcie_writel_dbi(pci, reg, reg_val); - } else - return PCIBIOS_BAD_REGISTER_NUMBER; - - return PCIBIOS_SUCCESSFUL; -} - -static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie) -{ - u32 val; - - regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG + - 0x100 * hisi_pcie->port_id, &val); - - return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); -} - -static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie) -{ - struct dw_pcie *pci = hisi_pcie->pci; - u32 val; - - val = dw_pcie_readl_dbi(pci, PCIE_SYS_STATE4); - - return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); -} - -static int hisi_pcie_link_up(struct dw_pcie *pci) -{ - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pci); - - return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie); -} - -static const struct dw_pcie_host_ops hisi_pcie_host_ops = { - .rd_own_conf = hisi_pcie_cfg_read, - .wr_own_conf = hisi_pcie_cfg_write, -}; - -static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie, - struct platform_device *pdev) -{ - struct dw_pcie *pci = hisi_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = &pdev->dev; - int ret; - u32 port_id; - - if (of_property_read_u32(dev->of_node, "port-id", &port_id)) { - dev_err(dev, "failed to read port-id\n"); - return -EINVAL; - } - if (port_id > 3) { - dev_err(dev, "Invalid port-id: %d\n", port_id); - return -EINVAL; - } - hisi_pcie->port_id = port_id; - - pp->ops = &hisi_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .link_up = hisi_pcie_link_up, -}; - -static int hisi_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_pcie *pci; - struct hisi_pcie *hisi_pcie; - struct resource *reg; - int ret; - - hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL); - if (!hisi_pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - hisi_pcie->pci = pci; - - hisi_pcie->soc_ops = of_device_get_match_data(dev); - - hisi_pcie->subctrl = - syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); - if (IS_ERR(hisi_pcie->subctrl)) { - dev_err(dev, "cannot get subctrl base\n"); - return PTR_ERR(hisi_pcie->subctrl); - } - - reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, reg); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - platform_set_drvdata(pdev, hisi_pcie); - - ret = hisi_add_pcie_port(hisi_pcie, pdev); - if (ret) - return ret; - - return 0; -} - -static struct pcie_soc_ops hip05_ops = { - &hisi_pcie_link_up_hip05 -}; - -static struct pcie_soc_ops hip06_ops = { - &hisi_pcie_link_up_hip06 -}; - -static const struct of_device_id hisi_pcie_of_match[] = { - { - .compatible = "hisilicon,hip05-pcie", - .data = (void *) &hip05_ops, - }, - { - .compatible = "hisilicon,hip06-pcie", - .data = (void *) &hip06_ops, - }, - {}, -}; - -static struct platform_driver hisi_pcie_driver = { - .probe = hisi_pcie_probe, - .driver = { - .name = "hisi-pcie", - .of_match_table = hisi_pcie_of_match, - .suppress_bind_attrs = true, - }, -}; -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; - struct platform_device *pdev = to_platform_device(dev); - struct resource *res; - void __iomem *reg_base; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(dev, "missing \"reg[1]\"property\n"); - return -EINVAL; - } - - reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); - if (!reg_base) - return -ENOMEM; - - cfg->priv = reg_base; - return 0; -} - -struct pci_ecam_ops hisi_pcie_platform_ops = { - .bus_shift = 20, - .init = hisi_pcie_platform_init, - .pci_ops = { - .map_bus = hisi_pcie_map_bus, - .read = hisi_pcie_rd_conf, - .write = hisi_pcie_wr_conf, - } -}; - -static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { - { - .compatible = "hisilicon,hip06-pcie-ecam", - .data = (void *) &hisi_pcie_platform_ops, - }, - { - .compatible = "hisilicon,hip07-pcie-ecam", - .data = (void *) &hisi_pcie_platform_ops, - }, - {}, -}; - -static struct platform_driver hisi_pcie_almost_ecam_driver = { - .probe = hisi_pcie_almost_ecam_probe, - .driver = { - .name = "hisi-pcie-almost-ecam", - .of_match_table = hisi_pcie_almost_ecam_of_match, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(hisi_pcie_almost_ecam_driver); - -#endif -#endif diff --git a/drivers/pci/dwc/pcie-histb.c b/drivers/pci/dwc/pcie-histb.c deleted file mode 100644 index 3611d6ce9a92..000000000000 --- a/drivers/pci/dwc/pcie-histb.c +++ /dev/null @@ -1,472 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for HiSilicon STB SoCs - * - * Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com - * - * Authors: Ruqiang Ju - * Jianguo Sun - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -#define to_histb_pcie(x) dev_get_drvdata((x)->dev) - -#define PCIE_SYS_CTRL0 0x0000 -#define PCIE_SYS_CTRL1 0x0004 -#define PCIE_SYS_CTRL7 0x001C -#define PCIE_SYS_CTRL13 0x0034 -#define PCIE_SYS_CTRL15 0x003C -#define PCIE_SYS_CTRL16 0x0040 -#define PCIE_SYS_CTRL17 0x0044 - -#define PCIE_SYS_STAT0 0x0100 -#define PCIE_SYS_STAT4 0x0110 - -#define PCIE_RDLH_LINK_UP BIT(5) -#define PCIE_XMLH_LINK_UP BIT(15) -#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21) -#define PCIE_APP_LTSSM_ENABLE BIT(11) - -#define PCIE_DEVICE_TYPE_MASK GENMASK(31, 28) -#define PCIE_WM_EP 0 -#define PCIE_WM_LEGACY BIT(1) -#define PCIE_WM_RC BIT(30) - -#define PCIE_LTSSM_STATE_MASK GENMASK(5, 0) -#define PCIE_LTSSM_STATE_ACTIVE 0x11 - -struct histb_pcie { - struct dw_pcie *pci; - struct clk *aux_clk; - struct clk *pipe_clk; - struct clk *sys_clk; - struct clk *bus_clk; - struct phy *phy; - struct reset_control *soft_reset; - struct reset_control *sys_reset; - struct reset_control *bus_reset; - void __iomem *ctrl; - int reset_gpio; - struct regulator *vpcie; -}; - -static u32 histb_pcie_readl(struct histb_pcie *histb_pcie, u32 reg) -{ - return readl(histb_pcie->ctrl + reg); -} - -static void histb_pcie_writel(struct histb_pcie *histb_pcie, u32 reg, u32 val) -{ - writel(val, histb_pcie->ctrl + reg); -} - -static void histb_pcie_dbi_w_mode(struct pcie_port *pp, bool enable) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct histb_pcie *hipcie = to_histb_pcie(pci); - u32 val; - - val = histb_pcie_readl(hipcie, PCIE_SYS_CTRL0); - if (enable) - val |= PCIE_ELBI_SLV_DBI_ENABLE; - else - val &= ~PCIE_ELBI_SLV_DBI_ENABLE; - histb_pcie_writel(hipcie, PCIE_SYS_CTRL0, val); -} - -static void histb_pcie_dbi_r_mode(struct pcie_port *pp, bool enable) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct histb_pcie *hipcie = to_histb_pcie(pci); - u32 val; - - val = histb_pcie_readl(hipcie, PCIE_SYS_CTRL1); - if (enable) - val |= PCIE_ELBI_SLV_DBI_ENABLE; - else - val &= ~PCIE_ELBI_SLV_DBI_ENABLE; - histb_pcie_writel(hipcie, PCIE_SYS_CTRL1, val); -} - -static u32 histb_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, - u32 reg, size_t size) -{ - u32 val; - - histb_pcie_dbi_r_mode(&pci->pp, true); - dw_pcie_read(base + reg, size, &val); - histb_pcie_dbi_r_mode(&pci->pp, false); - - return val; -} - -static void histb_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, - u32 reg, size_t size, u32 val) -{ - histb_pcie_dbi_w_mode(&pci->pp, true); - dw_pcie_write(base + reg, size, val); - histb_pcie_dbi_w_mode(&pci->pp, false); -} - -static int histb_pcie_rd_own_conf(struct pcie_port *pp, int where, - int size, u32 *val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - int ret; - - histb_pcie_dbi_r_mode(pp, true); - ret = dw_pcie_read(pci->dbi_base + where, size, val); - histb_pcie_dbi_r_mode(pp, false); - - return ret; -} - -static int histb_pcie_wr_own_conf(struct pcie_port *pp, int where, - int size, u32 val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - int ret; - - histb_pcie_dbi_w_mode(pp, true); - ret = dw_pcie_write(pci->dbi_base + where, size, val); - histb_pcie_dbi_w_mode(pp, false); - - return ret; -} - -static int histb_pcie_link_up(struct dw_pcie *pci) -{ - struct histb_pcie *hipcie = to_histb_pcie(pci); - u32 regval; - u32 status; - - regval = histb_pcie_readl(hipcie, PCIE_SYS_STAT0); - status = histb_pcie_readl(hipcie, PCIE_SYS_STAT4); - status &= PCIE_LTSSM_STATE_MASK; - if ((regval & PCIE_XMLH_LINK_UP) && (regval & PCIE_RDLH_LINK_UP) && - (status == PCIE_LTSSM_STATE_ACTIVE)) - return 1; - - return 0; -} - -static int histb_pcie_establish_link(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct histb_pcie *hipcie = to_histb_pcie(pci); - u32 regval; - - if (dw_pcie_link_up(pci)) { - dev_info(pci->dev, "Link already up\n"); - return 0; - } - - /* PCIe RC work mode */ - regval = histb_pcie_readl(hipcie, PCIE_SYS_CTRL0); - regval &= ~PCIE_DEVICE_TYPE_MASK; - regval |= PCIE_WM_RC; - histb_pcie_writel(hipcie, PCIE_SYS_CTRL0, regval); - - /* setup root complex */ - dw_pcie_setup_rc(pp); - - /* assert LTSSM enable */ - regval = histb_pcie_readl(hipcie, PCIE_SYS_CTRL7); - regval |= PCIE_APP_LTSSM_ENABLE; - histb_pcie_writel(hipcie, PCIE_SYS_CTRL7, regval); - - return dw_pcie_wait_for_link(pci); -} - -static int histb_pcie_host_init(struct pcie_port *pp) -{ - histb_pcie_establish_link(pp); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - dw_pcie_msi_init(pp); - - return 0; -} - -static struct dw_pcie_host_ops histb_pcie_host_ops = { - .rd_own_conf = histb_pcie_rd_own_conf, - .wr_own_conf = histb_pcie_wr_own_conf, - .host_init = histb_pcie_host_init, -}; - -static void histb_pcie_host_disable(struct histb_pcie *hipcie) -{ - reset_control_assert(hipcie->soft_reset); - reset_control_assert(hipcie->sys_reset); - reset_control_assert(hipcie->bus_reset); - - clk_disable_unprepare(hipcie->aux_clk); - clk_disable_unprepare(hipcie->pipe_clk); - clk_disable_unprepare(hipcie->sys_clk); - clk_disable_unprepare(hipcie->bus_clk); - - if (gpio_is_valid(hipcie->reset_gpio)) - gpio_set_value_cansleep(hipcie->reset_gpio, 0); - - if (hipcie->vpcie) - regulator_disable(hipcie->vpcie); -} - -static int histb_pcie_host_enable(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct histb_pcie *hipcie = to_histb_pcie(pci); - struct device *dev = pci->dev; - int ret; - - /* power on PCIe device if have */ - if (hipcie->vpcie) { - ret = regulator_enable(hipcie->vpcie); - if (ret) { - dev_err(dev, "failed to enable regulator: %d\n", ret); - return ret; - } - } - - if (gpio_is_valid(hipcie->reset_gpio)) - gpio_set_value_cansleep(hipcie->reset_gpio, 1); - - ret = clk_prepare_enable(hipcie->bus_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable bus clk\n"); - goto err_bus_clk; - } - - ret = clk_prepare_enable(hipcie->sys_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable sys clk\n"); - goto err_sys_clk; - } - - ret = clk_prepare_enable(hipcie->pipe_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable pipe clk\n"); - goto err_pipe_clk; - } - - ret = clk_prepare_enable(hipcie->aux_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable aux clk\n"); - goto err_aux_clk; - } - - reset_control_assert(hipcie->soft_reset); - reset_control_deassert(hipcie->soft_reset); - - reset_control_assert(hipcie->sys_reset); - reset_control_deassert(hipcie->sys_reset); - - reset_control_assert(hipcie->bus_reset); - reset_control_deassert(hipcie->bus_reset); - - return 0; - -err_aux_clk: - clk_disable_unprepare(hipcie->pipe_clk); -err_pipe_clk: - clk_disable_unprepare(hipcie->sys_clk); -err_sys_clk: - clk_disable_unprepare(hipcie->bus_clk); -err_bus_clk: - if (hipcie->vpcie) - regulator_disable(hipcie->vpcie); - - return ret; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .read_dbi = histb_pcie_read_dbi, - .write_dbi = histb_pcie_write_dbi, - .link_up = histb_pcie_link_up, -}; - -static int histb_pcie_probe(struct platform_device *pdev) -{ - struct histb_pcie *hipcie; - struct dw_pcie *pci; - struct pcie_port *pp; - struct resource *res; - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; - enum of_gpio_flags of_flags; - unsigned long flag = GPIOF_DIR_OUT; - int ret; - - hipcie = devm_kzalloc(dev, sizeof(*hipcie), GFP_KERNEL); - if (!hipcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - hipcie->pci = pci; - pp = &pci->pp; - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); - hipcie->ctrl = devm_ioremap_resource(dev, res); - if (IS_ERR(hipcie->ctrl)) { - dev_err(dev, "cannot get control reg base\n"); - return PTR_ERR(hipcie->ctrl); - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc-dbi"); - pci->dbi_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pci->dbi_base)) { - dev_err(dev, "cannot get rc-dbi base\n"); - return PTR_ERR(pci->dbi_base); - } - - hipcie->vpcie = devm_regulator_get_optional(dev, "vpcie"); - if (IS_ERR(hipcie->vpcie)) { - if (PTR_ERR(hipcie->vpcie) == -EPROBE_DEFER) - return -EPROBE_DEFER; - hipcie->vpcie = NULL; - } - - hipcie->reset_gpio = of_get_named_gpio_flags(np, - "reset-gpios", 0, &of_flags); - if (of_flags & OF_GPIO_ACTIVE_LOW) - flag |= GPIOF_ACTIVE_LOW; - if (gpio_is_valid(hipcie->reset_gpio)) { - ret = devm_gpio_request_one(dev, hipcie->reset_gpio, - flag, "PCIe device power control"); - if (ret) { - dev_err(dev, "unable to request gpio\n"); - return ret; - } - } - - hipcie->aux_clk = devm_clk_get(dev, "aux"); - if (IS_ERR(hipcie->aux_clk)) { - dev_err(dev, "Failed to get PCIe aux clk\n"); - return PTR_ERR(hipcie->aux_clk); - } - - hipcie->pipe_clk = devm_clk_get(dev, "pipe"); - if (IS_ERR(hipcie->pipe_clk)) { - dev_err(dev, "Failed to get PCIe pipe clk\n"); - return PTR_ERR(hipcie->pipe_clk); - } - - hipcie->sys_clk = devm_clk_get(dev, "sys"); - if (IS_ERR(hipcie->sys_clk)) { - dev_err(dev, "Failed to get PCIEe sys clk\n"); - return PTR_ERR(hipcie->sys_clk); - } - - hipcie->bus_clk = devm_clk_get(dev, "bus"); - if (IS_ERR(hipcie->bus_clk)) { - dev_err(dev, "Failed to get PCIe bus clk\n"); - return PTR_ERR(hipcie->bus_clk); - } - - hipcie->soft_reset = devm_reset_control_get(dev, "soft"); - if (IS_ERR(hipcie->soft_reset)) { - dev_err(dev, "couldn't get soft reset\n"); - return PTR_ERR(hipcie->soft_reset); - } - - hipcie->sys_reset = devm_reset_control_get(dev, "sys"); - if (IS_ERR(hipcie->sys_reset)) { - dev_err(dev, "couldn't get sys reset\n"); - return PTR_ERR(hipcie->sys_reset); - } - - hipcie->bus_reset = devm_reset_control_get(dev, "bus"); - if (IS_ERR(hipcie->bus_reset)) { - dev_err(dev, "couldn't get bus reset\n"); - return PTR_ERR(hipcie->bus_reset); - } - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->msi_irq = platform_get_irq_byname(pdev, "msi"); - if (pp->msi_irq < 0) { - dev_err(dev, "Failed to get MSI IRQ\n"); - return pp->msi_irq; - } - } - - hipcie->phy = devm_phy_get(dev, "phy"); - if (IS_ERR(hipcie->phy)) { - dev_info(dev, "no pcie-phy found\n"); - hipcie->phy = NULL; - /* fall through here! - * if no pcie-phy found, phy init - * should be done under boot! - */ - } else { - phy_init(hipcie->phy); - } - - pp->root_bus_nr = -1; - pp->ops = &histb_pcie_host_ops; - - platform_set_drvdata(pdev, hipcie); - - ret = histb_pcie_host_enable(pp); - if (ret) { - dev_err(dev, "failed to enable host\n"); - return ret; - } - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static int histb_pcie_remove(struct platform_device *pdev) -{ - struct histb_pcie *hipcie = platform_get_drvdata(pdev); - - histb_pcie_host_disable(hipcie); - - if (hipcie->phy) - phy_exit(hipcie->phy); - - return 0; -} - -static const struct of_device_id histb_pcie_of_match[] = { - { .compatible = "hisilicon,hi3798cv200-pcie", }, - {}, -}; -MODULE_DEVICE_TABLE(of, histb_pcie_of_match); - -static struct platform_driver histb_pcie_platform_driver = { - .probe = histb_pcie_probe, - .remove = histb_pcie_remove, - .driver = { - .name = "histb-pcie", - .of_match_table = histb_pcie_of_match, - }, -}; -module_platform_driver(histb_pcie_platform_driver); - -MODULE_DESCRIPTION("HiSilicon STB PCIe host controller driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/dwc/pcie-kirin.c b/drivers/pci/dwc/pcie-kirin.c deleted file mode 100644 index d2970a009eb5..000000000000 --- a/drivers/pci/dwc/pcie-kirin.c +++ /dev/null @@ -1,515 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for Kirin Phone SoCs - * - * Copyright (C) 2017 Hilisicon Electronics Co., Ltd. - * http://www.huawei.com - * - * Author: Xiaowei Song - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pcie-designware.h" - -#define to_kirin_pcie(x) dev_get_drvdata((x)->dev) - -#define REF_CLK_FREQ 100000000 - -/* PCIe ELBI registers */ -#define SOC_PCIECTRL_CTRL0_ADDR 0x000 -#define SOC_PCIECTRL_CTRL1_ADDR 0x004 -#define SOC_PCIEPHY_CTRL2_ADDR 0x008 -#define SOC_PCIEPHY_CTRL3_ADDR 0x00c -#define PCIE_ELBI_SLV_DBI_ENABLE (0x1 << 21) - -/* info located in APB */ -#define PCIE_APP_LTSSM_ENABLE 0x01c -#define PCIE_APB_PHY_CTRL0 0x0 -#define PCIE_APB_PHY_CTRL1 0x4 -#define PCIE_APB_PHY_STATUS0 0x400 -#define PCIE_LINKUP_ENABLE (0x8020) -#define PCIE_LTSSM_ENABLE_BIT (0x1 << 11) -#define PIPE_CLK_STABLE (0x1 << 19) -#define PHY_REF_PAD_BIT (0x1 << 8) -#define PHY_PWR_DOWN_BIT (0x1 << 22) -#define PHY_RST_ACK_BIT (0x1 << 16) - -/* info located in sysctrl */ -#define SCTRL_PCIE_CMOS_OFFSET 0x60 -#define SCTRL_PCIE_CMOS_BIT 0x10 -#define SCTRL_PCIE_ISO_OFFSET 0x44 -#define SCTRL_PCIE_ISO_BIT 0x30 -#define SCTRL_PCIE_HPCLK_OFFSET 0x190 -#define SCTRL_PCIE_HPCLK_BIT 0x184000 -#define SCTRL_PCIE_OE_OFFSET 0x14a -#define PCIE_DEBOUNCE_PARAM 0xF0F400 -#define PCIE_OE_BYPASS (0x3 << 28) - -/* peri_crg ctrl */ -#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88 -#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000 - -/* Time for delay */ -#define REF_2_PERST_MIN 20000 -#define REF_2_PERST_MAX 25000 -#define PERST_2_ACCESS_MIN 10000 -#define PERST_2_ACCESS_MAX 12000 -#define LINK_WAIT_MIN 900 -#define LINK_WAIT_MAX 1000 -#define PIPE_CLK_WAIT_MIN 550 -#define PIPE_CLK_WAIT_MAX 600 -#define TIME_CMOS_MIN 100 -#define TIME_CMOS_MAX 105 -#define TIME_PHY_PD_MIN 10 -#define TIME_PHY_PD_MAX 11 - -struct kirin_pcie { - struct dw_pcie *pci; - void __iomem *apb_base; - void __iomem *phy_base; - struct regmap *crgctrl; - struct regmap *sysctrl; - struct clk *apb_sys_clk; - struct clk *apb_phy_clk; - struct clk *phy_ref_clk; - struct clk *pcie_aclk; - struct clk *pcie_aux_clk; - int gpio_id_reset; -}; - -/* Registers in PCIeCTRL */ -static inline void kirin_apb_ctrl_writel(struct kirin_pcie *kirin_pcie, - u32 val, u32 reg) -{ - writel(val, kirin_pcie->apb_base + reg); -} - -static inline u32 kirin_apb_ctrl_readl(struct kirin_pcie *kirin_pcie, u32 reg) -{ - return readl(kirin_pcie->apb_base + reg); -} - -/* Registers in PCIePHY */ -static inline void kirin_apb_phy_writel(struct kirin_pcie *kirin_pcie, - u32 val, u32 reg) -{ - writel(val, kirin_pcie->phy_base + reg); -} - -static inline u32 kirin_apb_phy_readl(struct kirin_pcie *kirin_pcie, u32 reg) -{ - return readl(kirin_pcie->phy_base + reg); -} - -static long kirin_pcie_get_clk(struct kirin_pcie *kirin_pcie, - struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - - kirin_pcie->phy_ref_clk = devm_clk_get(dev, "pcie_phy_ref"); - if (IS_ERR(kirin_pcie->phy_ref_clk)) - return PTR_ERR(kirin_pcie->phy_ref_clk); - - kirin_pcie->pcie_aux_clk = devm_clk_get(dev, "pcie_aux"); - if (IS_ERR(kirin_pcie->pcie_aux_clk)) - return PTR_ERR(kirin_pcie->pcie_aux_clk); - - kirin_pcie->apb_phy_clk = devm_clk_get(dev, "pcie_apb_phy"); - if (IS_ERR(kirin_pcie->apb_phy_clk)) - return PTR_ERR(kirin_pcie->apb_phy_clk); - - kirin_pcie->apb_sys_clk = devm_clk_get(dev, "pcie_apb_sys"); - if (IS_ERR(kirin_pcie->apb_sys_clk)) - return PTR_ERR(kirin_pcie->apb_sys_clk); - - kirin_pcie->pcie_aclk = devm_clk_get(dev, "pcie_aclk"); - if (IS_ERR(kirin_pcie->pcie_aclk)) - return PTR_ERR(kirin_pcie->pcie_aclk); - - return 0; -} - -static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie, - struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *apb; - struct resource *phy; - struct resource *dbi; - - apb = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb"); - kirin_pcie->apb_base = devm_ioremap_resource(dev, apb); - if (IS_ERR(kirin_pcie->apb_base)) - return PTR_ERR(kirin_pcie->apb_base); - - phy = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - kirin_pcie->phy_base = devm_ioremap_resource(dev, phy); - if (IS_ERR(kirin_pcie->phy_base)) - return PTR_ERR(kirin_pcie->phy_base); - - dbi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - kirin_pcie->pci->dbi_base = devm_ioremap_resource(dev, dbi); - if (IS_ERR(kirin_pcie->pci->dbi_base)) - return PTR_ERR(kirin_pcie->pci->dbi_base); - - kirin_pcie->crgctrl = - syscon_regmap_lookup_by_compatible("hisilicon,hi3660-crgctrl"); - if (IS_ERR(kirin_pcie->crgctrl)) - return PTR_ERR(kirin_pcie->crgctrl); - - kirin_pcie->sysctrl = - syscon_regmap_lookup_by_compatible("hisilicon,hi3660-sctrl"); - if (IS_ERR(kirin_pcie->sysctrl)) - return PTR_ERR(kirin_pcie->sysctrl); - - return 0; -} - -static int kirin_pcie_phy_init(struct kirin_pcie *kirin_pcie) -{ - struct device *dev = kirin_pcie->pci->dev; - u32 reg_val; - - reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_CTRL1); - reg_val &= ~PHY_REF_PAD_BIT; - kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL1); - - reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_CTRL0); - reg_val &= ~PHY_PWR_DOWN_BIT; - kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL0); - usleep_range(TIME_PHY_PD_MIN, TIME_PHY_PD_MAX); - - reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_CTRL1); - reg_val &= ~PHY_RST_ACK_BIT; - kirin_apb_phy_writel(kirin_pcie, reg_val, PCIE_APB_PHY_CTRL1); - - usleep_range(PIPE_CLK_WAIT_MIN, PIPE_CLK_WAIT_MAX); - reg_val = kirin_apb_phy_readl(kirin_pcie, PCIE_APB_PHY_STATUS0); - if (reg_val & PIPE_CLK_STABLE) { - dev_err(dev, "PIPE clk is not stable\n"); - return -EINVAL; - } - - return 0; -} - -static void kirin_pcie_oe_enable(struct kirin_pcie *kirin_pcie) -{ - u32 val; - - regmap_read(kirin_pcie->sysctrl, SCTRL_PCIE_OE_OFFSET, &val); - val |= PCIE_DEBOUNCE_PARAM; - val &= ~PCIE_OE_BYPASS; - regmap_write(kirin_pcie->sysctrl, SCTRL_PCIE_OE_OFFSET, val); -} - -static int kirin_pcie_clk_ctrl(struct kirin_pcie *kirin_pcie, bool enable) -{ - int ret = 0; - - if (!enable) - goto close_clk; - - ret = clk_set_rate(kirin_pcie->phy_ref_clk, REF_CLK_FREQ); - if (ret) - return ret; - - ret = clk_prepare_enable(kirin_pcie->phy_ref_clk); - if (ret) - return ret; - - ret = clk_prepare_enable(kirin_pcie->apb_sys_clk); - if (ret) - goto apb_sys_fail; - - ret = clk_prepare_enable(kirin_pcie->apb_phy_clk); - if (ret) - goto apb_phy_fail; - - ret = clk_prepare_enable(kirin_pcie->pcie_aclk); - if (ret) - goto aclk_fail; - - ret = clk_prepare_enable(kirin_pcie->pcie_aux_clk); - if (ret) - goto aux_clk_fail; - - return 0; - -close_clk: - clk_disable_unprepare(kirin_pcie->pcie_aux_clk); -aux_clk_fail: - clk_disable_unprepare(kirin_pcie->pcie_aclk); -aclk_fail: - clk_disable_unprepare(kirin_pcie->apb_phy_clk); -apb_phy_fail: - clk_disable_unprepare(kirin_pcie->apb_sys_clk); -apb_sys_fail: - clk_disable_unprepare(kirin_pcie->phy_ref_clk); - - return ret; -} - -static int kirin_pcie_power_on(struct kirin_pcie *kirin_pcie) -{ - int ret; - - /* Power supply for Host */ - regmap_write(kirin_pcie->sysctrl, - SCTRL_PCIE_CMOS_OFFSET, SCTRL_PCIE_CMOS_BIT); - usleep_range(TIME_CMOS_MIN, TIME_CMOS_MAX); - kirin_pcie_oe_enable(kirin_pcie); - - ret = kirin_pcie_clk_ctrl(kirin_pcie, true); - if (ret) - return ret; - - /* ISO disable, PCIeCtrl, PHY assert and clk gate clear */ - regmap_write(kirin_pcie->sysctrl, - SCTRL_PCIE_ISO_OFFSET, SCTRL_PCIE_ISO_BIT); - regmap_write(kirin_pcie->crgctrl, - CRGCTRL_PCIE_ASSERT_OFFSET, CRGCTRL_PCIE_ASSERT_BIT); - regmap_write(kirin_pcie->sysctrl, - SCTRL_PCIE_HPCLK_OFFSET, SCTRL_PCIE_HPCLK_BIT); - - ret = kirin_pcie_phy_init(kirin_pcie); - if (ret) - goto close_clk; - - /* perst assert Endpoint */ - if (!gpio_request(kirin_pcie->gpio_id_reset, "pcie_perst")) { - usleep_range(REF_2_PERST_MIN, REF_2_PERST_MAX); - ret = gpio_direction_output(kirin_pcie->gpio_id_reset, 1); - if (ret) - goto close_clk; - usleep_range(PERST_2_ACCESS_MIN, PERST_2_ACCESS_MAX); - - return 0; - } - -close_clk: - kirin_pcie_clk_ctrl(kirin_pcie, false); - return ret; -} - -static void kirin_pcie_sideband_dbi_w_mode(struct kirin_pcie *kirin_pcie, - bool on) -{ - u32 val; - - val = kirin_apb_ctrl_readl(kirin_pcie, SOC_PCIECTRL_CTRL0_ADDR); - if (on) - val = val | PCIE_ELBI_SLV_DBI_ENABLE; - else - val = val & ~PCIE_ELBI_SLV_DBI_ENABLE; - - kirin_apb_ctrl_writel(kirin_pcie, val, SOC_PCIECTRL_CTRL0_ADDR); -} - -static void kirin_pcie_sideband_dbi_r_mode(struct kirin_pcie *kirin_pcie, - bool on) -{ - u32 val; - - val = kirin_apb_ctrl_readl(kirin_pcie, SOC_PCIECTRL_CTRL1_ADDR); - if (on) - val = val | PCIE_ELBI_SLV_DBI_ENABLE; - else - val = val & ~PCIE_ELBI_SLV_DBI_ENABLE; - - kirin_apb_ctrl_writel(kirin_pcie, val, SOC_PCIECTRL_CTRL1_ADDR); -} - -static int kirin_pcie_rd_own_conf(struct pcie_port *pp, - int where, int size, u32 *val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); - int ret; - - kirin_pcie_sideband_dbi_r_mode(kirin_pcie, true); - ret = dw_pcie_read(pci->dbi_base + where, size, val); - kirin_pcie_sideband_dbi_r_mode(kirin_pcie, false); - - return ret; -} - -static int kirin_pcie_wr_own_conf(struct pcie_port *pp, - int where, int size, u32 val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); - int ret; - - kirin_pcie_sideband_dbi_w_mode(kirin_pcie, true); - ret = dw_pcie_write(pci->dbi_base + where, size, val); - kirin_pcie_sideband_dbi_w_mode(kirin_pcie, false); - - return ret; -} - -static u32 kirin_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, - u32 reg, size_t size) -{ - struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); - u32 ret; - - kirin_pcie_sideband_dbi_r_mode(kirin_pcie, true); - dw_pcie_read(base + reg, size, &ret); - kirin_pcie_sideband_dbi_r_mode(kirin_pcie, false); - - return ret; -} - -static void kirin_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, - u32 reg, size_t size, u32 val) -{ - struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); - - kirin_pcie_sideband_dbi_w_mode(kirin_pcie, true); - dw_pcie_write(base + reg, size, val); - kirin_pcie_sideband_dbi_w_mode(kirin_pcie, false); -} - -static int kirin_pcie_link_up(struct dw_pcie *pci) -{ - struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); - u32 val = kirin_apb_ctrl_readl(kirin_pcie, PCIE_APB_PHY_STATUS0); - - if ((val & PCIE_LINKUP_ENABLE) == PCIE_LINKUP_ENABLE) - return 1; - - return 0; -} - -static int kirin_pcie_establish_link(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct kirin_pcie *kirin_pcie = to_kirin_pcie(pci); - struct device *dev = kirin_pcie->pci->dev; - int count = 0; - - if (kirin_pcie_link_up(pci)) - return 0; - - dw_pcie_setup_rc(pp); - - /* assert LTSSM enable */ - kirin_apb_ctrl_writel(kirin_pcie, PCIE_LTSSM_ENABLE_BIT, - PCIE_APP_LTSSM_ENABLE); - - /* check if the link is up or not */ - while (!kirin_pcie_link_up(pci)) { - usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); - count++; - if (count == 1000) { - dev_err(dev, "Link Fail\n"); - return -EINVAL; - } - } - - return 0; -} - -static int kirin_pcie_host_init(struct pcie_port *pp) -{ - kirin_pcie_establish_link(pp); - - return 0; -} - -static struct dw_pcie_ops kirin_dw_pcie_ops = { - .read_dbi = kirin_pcie_read_dbi, - .write_dbi = kirin_pcie_write_dbi, - .link_up = kirin_pcie_link_up, -}; - -static const struct dw_pcie_host_ops kirin_pcie_host_ops = { - .rd_own_conf = kirin_pcie_rd_own_conf, - .wr_own_conf = kirin_pcie_wr_own_conf, - .host_init = kirin_pcie_host_init, -}; - -static int __init kirin_add_pcie_port(struct dw_pcie *pci, - struct platform_device *pdev) -{ - pci->pp.ops = &kirin_pcie_host_ops; - - return dw_pcie_host_init(&pci->pp); -} - -static int kirin_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct kirin_pcie *kirin_pcie; - struct dw_pcie *pci; - int ret; - - if (!dev->of_node) { - dev_err(dev, "NULL node\n"); - return -EINVAL; - } - - kirin_pcie = devm_kzalloc(dev, sizeof(struct kirin_pcie), GFP_KERNEL); - if (!kirin_pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &kirin_dw_pcie_ops; - kirin_pcie->pci = pci; - - ret = kirin_pcie_get_clk(kirin_pcie, pdev); - if (ret) - return ret; - - ret = kirin_pcie_get_resource(kirin_pcie, pdev); - if (ret) - return ret; - - kirin_pcie->gpio_id_reset = of_get_named_gpio(dev->of_node, - "reset-gpios", 0); - if (kirin_pcie->gpio_id_reset < 0) - return -ENODEV; - - ret = kirin_pcie_power_on(kirin_pcie); - if (ret) - return ret; - - platform_set_drvdata(pdev, kirin_pcie); - - return kirin_add_pcie_port(pci, pdev); -} - -static const struct of_device_id kirin_pcie_match[] = { - { .compatible = "hisilicon,kirin960-pcie" }, - {}, -}; - -static struct platform_driver kirin_pcie_driver = { - .probe = kirin_pcie_probe, - .driver = { - .name = "kirin-pcie", - .of_match_table = kirin_pcie_match, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(kirin_pcie_driver); diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c deleted file mode 100644 index a1d0198081a6..000000000000 --- a/drivers/pci/dwc/pcie-qcom.c +++ /dev/null @@ -1,1299 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Qualcomm PCIe root complex driver - * - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. - * Copyright 2015 Linaro Limited. - * - * Author: Stanimir Varbanov - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -#define PCIE20_PARF_SYS_CTRL 0x00 -#define MST_WAKEUP_EN BIT(13) -#define SLV_WAKEUP_EN BIT(12) -#define MSTR_ACLK_CGC_DIS BIT(10) -#define SLV_ACLK_CGC_DIS BIT(9) -#define CORE_CLK_CGC_DIS BIT(6) -#define AUX_PWR_DET BIT(4) -#define L23_CLK_RMV_DIS BIT(2) -#define L1_CLK_RMV_DIS BIT(1) - -#define PCIE20_COMMAND_STATUS 0x04 -#define CMD_BME_VAL 0x4 -#define PCIE20_DEVICE_CONTROL2_STATUS2 0x98 -#define PCIE_CAP_CPL_TIMEOUT_DISABLE 0x10 - -#define PCIE20_PARF_PHY_CTRL 0x40 -#define PCIE20_PARF_PHY_REFCLK 0x4C -#define PCIE20_PARF_DBI_BASE_ADDR 0x168 -#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C -#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174 -#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8 -#define PCIE20_PARF_LTSSM 0x1B0 -#define PCIE20_PARF_SID_OFFSET 0x234 -#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C - -#define PCIE20_ELBI_SYS_CTRL 0x04 -#define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) - -#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 -#define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 -#define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_4K 0x5 -#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c -#define CFG_BRIDGE_SB_INIT BIT(0) - -#define PCIE20_CAP 0x70 -#define PCIE20_CAP_LINK_CAPABILITIES (PCIE20_CAP + 0xC) -#define PCIE20_CAP_ACTIVE_STATE_LINK_PM_SUPPORT (BIT(10) | BIT(11)) -#define PCIE20_CAP_LINK_1 (PCIE20_CAP + 0x14) -#define PCIE_CAP_LINK1_VAL 0x2FD7F - -#define PCIE20_PARF_Q2A_FLUSH 0x1AC - -#define PCIE20_MISC_CONTROL_1_REG 0x8BC -#define DBI_RO_WR_EN 1 - -#define PERST_DELAY_US 1000 - -#define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 -#define SLV_ADDR_SPACE_SZ 0x10000000 - -#define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 -struct qcom_pcie_resources_2_1_0 { - struct clk *iface_clk; - struct clk *core_clk; - struct clk *phy_clk; - struct reset_control *pci_reset; - struct reset_control *axi_reset; - struct reset_control *ahb_reset; - struct reset_control *por_reset; - struct reset_control *phy_reset; - struct regulator_bulk_data supplies[QCOM_PCIE_2_1_0_MAX_SUPPLY]; -}; - -struct qcom_pcie_resources_1_0_0 { - struct clk *iface; - struct clk *aux; - struct clk *master_bus; - struct clk *slave_bus; - struct reset_control *core; - struct regulator *vdda; -}; - -#define QCOM_PCIE_2_3_2_MAX_SUPPLY 2 -struct qcom_pcie_resources_2_3_2 { - struct clk *aux_clk; - struct clk *master_clk; - struct clk *slave_clk; - struct clk *cfg_clk; - struct clk *pipe_clk; - struct regulator_bulk_data supplies[QCOM_PCIE_2_3_2_MAX_SUPPLY]; -}; - -struct qcom_pcie_resources_2_4_0 { - struct clk *aux_clk; - struct clk *master_clk; - struct clk *slave_clk; - struct reset_control *axi_m_reset; - struct reset_control *axi_s_reset; - struct reset_control *pipe_reset; - struct reset_control *axi_m_vmid_reset; - struct reset_control *axi_s_xpu_reset; - struct reset_control *parf_reset; - struct reset_control *phy_reset; - struct reset_control *axi_m_sticky_reset; - struct reset_control *pipe_sticky_reset; - struct reset_control *pwr_reset; - struct reset_control *ahb_reset; - struct reset_control *phy_ahb_reset; -}; - -struct qcom_pcie_resources_2_3_3 { - struct clk *iface; - struct clk *axi_m_clk; - struct clk *axi_s_clk; - struct clk *ahb_clk; - struct clk *aux_clk; - struct reset_control *rst[7]; -}; - -union qcom_pcie_resources { - struct qcom_pcie_resources_1_0_0 v1_0_0; - struct qcom_pcie_resources_2_1_0 v2_1_0; - struct qcom_pcie_resources_2_3_2 v2_3_2; - struct qcom_pcie_resources_2_3_3 v2_3_3; - struct qcom_pcie_resources_2_4_0 v2_4_0; -}; - -struct qcom_pcie; - -struct qcom_pcie_ops { - int (*get_resources)(struct qcom_pcie *pcie); - int (*init)(struct qcom_pcie *pcie); - int (*post_init)(struct qcom_pcie *pcie); - void (*deinit)(struct qcom_pcie *pcie); - void (*post_deinit)(struct qcom_pcie *pcie); - void (*ltssm_enable)(struct qcom_pcie *pcie); -}; - -struct qcom_pcie { - struct dw_pcie *pci; - void __iomem *parf; /* DT parf */ - void __iomem *elbi; /* DT elbi */ - union qcom_pcie_resources res; - struct phy *phy; - struct gpio_desc *reset; - const struct qcom_pcie_ops *ops; -}; - -#define to_qcom_pcie(x) dev_get_drvdata((x)->dev) - -static void qcom_ep_reset_assert(struct qcom_pcie *pcie) -{ - gpiod_set_value_cansleep(pcie->reset, 1); - usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); -} - -static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) -{ - gpiod_set_value_cansleep(pcie->reset, 0); - usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); -} - -static int qcom_pcie_establish_link(struct qcom_pcie *pcie) -{ - struct dw_pcie *pci = pcie->pci; - - if (dw_pcie_link_up(pci)) - return 0; - - /* Enable Link Training state machine */ - if (pcie->ops->ltssm_enable) - pcie->ops->ltssm_enable(pcie); - - return dw_pcie_wait_for_link(pci); -} - -static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) -{ - u32 val; - - /* enable link training */ - val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL); - val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE; - writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL); -} - -static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - int ret; - - res->supplies[0].supply = "vdda"; - res->supplies[1].supply = "vdda_phy"; - res->supplies[2].supply = "vdda_refclk"; - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), - res->supplies); - if (ret) - return ret; - - res->iface_clk = devm_clk_get(dev, "iface"); - if (IS_ERR(res->iface_clk)) - return PTR_ERR(res->iface_clk); - - res->core_clk = devm_clk_get(dev, "core"); - if (IS_ERR(res->core_clk)) - return PTR_ERR(res->core_clk); - - res->phy_clk = devm_clk_get(dev, "phy"); - if (IS_ERR(res->phy_clk)) - return PTR_ERR(res->phy_clk); - - res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); - if (IS_ERR(res->pci_reset)) - return PTR_ERR(res->pci_reset); - - res->axi_reset = devm_reset_control_get_exclusive(dev, "axi"); - if (IS_ERR(res->axi_reset)) - return PTR_ERR(res->axi_reset); - - res->ahb_reset = devm_reset_control_get_exclusive(dev, "ahb"); - if (IS_ERR(res->ahb_reset)) - return PTR_ERR(res->ahb_reset); - - res->por_reset = devm_reset_control_get_exclusive(dev, "por"); - if (IS_ERR(res->por_reset)) - return PTR_ERR(res->por_reset); - - res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); - return PTR_ERR_OR_ZERO(res->phy_reset); -} - -static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; - - reset_control_assert(res->pci_reset); - reset_control_assert(res->axi_reset); - reset_control_assert(res->ahb_reset); - reset_control_assert(res->por_reset); - reset_control_assert(res->pci_reset); - clk_disable_unprepare(res->iface_clk); - clk_disable_unprepare(res->core_clk); - clk_disable_unprepare(res->phy_clk); - regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); -} - -static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - u32 val; - int ret; - - ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); - if (ret < 0) { - dev_err(dev, "cannot enable regulators\n"); - return ret; - } - - ret = reset_control_assert(res->ahb_reset); - if (ret) { - dev_err(dev, "cannot assert ahb reset\n"); - goto err_assert_ahb; - } - - ret = clk_prepare_enable(res->iface_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable iface clock\n"); - goto err_assert_ahb; - } - - ret = clk_prepare_enable(res->phy_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable phy clock\n"); - goto err_clk_phy; - } - - ret = clk_prepare_enable(res->core_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable core clock\n"); - goto err_clk_core; - } - - ret = reset_control_deassert(res->ahb_reset); - if (ret) { - dev_err(dev, "cannot deassert ahb reset\n"); - goto err_deassert_ahb; - } - - /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); - val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); - - /* enable external reference clock */ - val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); - val |= BIT(16); - writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); - - ret = reset_control_deassert(res->phy_reset); - if (ret) { - dev_err(dev, "cannot deassert phy reset\n"); - return ret; - } - - ret = reset_control_deassert(res->pci_reset); - if (ret) { - dev_err(dev, "cannot deassert pci reset\n"); - return ret; - } - - ret = reset_control_deassert(res->por_reset); - if (ret) { - dev_err(dev, "cannot deassert por reset\n"); - return ret; - } - - ret = reset_control_deassert(res->axi_reset); - if (ret) { - dev_err(dev, "cannot deassert axi reset\n"); - return ret; - } - - /* wait for clock acquisition */ - usleep_range(1000, 1500); - - - /* Set the Max TLP size to 2K, instead of using default of 4K */ - writel(CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K, - pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); - writel(CFG_BRIDGE_SB_INIT, - pci->dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); - - return 0; - -err_deassert_ahb: - clk_disable_unprepare(res->core_clk); -err_clk_core: - clk_disable_unprepare(res->phy_clk); -err_clk_phy: - clk_disable_unprepare(res->iface_clk); -err_assert_ahb: - regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); - - return ret; -} - -static int qcom_pcie_get_resources_1_0_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_1_0_0 *res = &pcie->res.v1_0_0; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - - res->vdda = devm_regulator_get(dev, "vdda"); - if (IS_ERR(res->vdda)) - return PTR_ERR(res->vdda); - - res->iface = devm_clk_get(dev, "iface"); - if (IS_ERR(res->iface)) - return PTR_ERR(res->iface); - - res->aux = devm_clk_get(dev, "aux"); - if (IS_ERR(res->aux)) - return PTR_ERR(res->aux); - - res->master_bus = devm_clk_get(dev, "master_bus"); - if (IS_ERR(res->master_bus)) - return PTR_ERR(res->master_bus); - - res->slave_bus = devm_clk_get(dev, "slave_bus"); - if (IS_ERR(res->slave_bus)) - return PTR_ERR(res->slave_bus); - - res->core = devm_reset_control_get_exclusive(dev, "core"); - return PTR_ERR_OR_ZERO(res->core); -} - -static void qcom_pcie_deinit_1_0_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_1_0_0 *res = &pcie->res.v1_0_0; - - reset_control_assert(res->core); - clk_disable_unprepare(res->slave_bus); - clk_disable_unprepare(res->master_bus); - clk_disable_unprepare(res->iface); - clk_disable_unprepare(res->aux); - regulator_disable(res->vdda); -} - -static int qcom_pcie_init_1_0_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_1_0_0 *res = &pcie->res.v1_0_0; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - int ret; - - ret = reset_control_deassert(res->core); - if (ret) { - dev_err(dev, "cannot deassert core reset\n"); - return ret; - } - - ret = clk_prepare_enable(res->aux); - if (ret) { - dev_err(dev, "cannot prepare/enable aux clock\n"); - goto err_res; - } - - ret = clk_prepare_enable(res->iface); - if (ret) { - dev_err(dev, "cannot prepare/enable iface clock\n"); - goto err_aux; - } - - ret = clk_prepare_enable(res->master_bus); - if (ret) { - dev_err(dev, "cannot prepare/enable master_bus clock\n"); - goto err_iface; - } - - ret = clk_prepare_enable(res->slave_bus); - if (ret) { - dev_err(dev, "cannot prepare/enable slave_bus clock\n"); - goto err_master; - } - - ret = regulator_enable(res->vdda); - if (ret) { - dev_err(dev, "cannot enable vdda regulator\n"); - goto err_slave; - } - - /* change DBI base address */ - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - u32 val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); - - val |= BIT(31); - writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT); - } - - return 0; -err_slave: - clk_disable_unprepare(res->slave_bus); -err_master: - clk_disable_unprepare(res->master_bus); -err_iface: - clk_disable_unprepare(res->iface); -err_aux: - clk_disable_unprepare(res->aux); -err_res: - reset_control_assert(res->core); - - return ret; -} - -static void qcom_pcie_2_3_2_ltssm_enable(struct qcom_pcie *pcie) -{ - u32 val; - - /* enable link training */ - val = readl(pcie->parf + PCIE20_PARF_LTSSM); - val |= BIT(8); - writel(val, pcie->parf + PCIE20_PARF_LTSSM); -} - -static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - int ret; - - res->supplies[0].supply = "vdda"; - res->supplies[1].supply = "vddpe-3v3"; - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), - res->supplies); - if (ret) - return ret; - - res->aux_clk = devm_clk_get(dev, "aux"); - if (IS_ERR(res->aux_clk)) - return PTR_ERR(res->aux_clk); - - res->cfg_clk = devm_clk_get(dev, "cfg"); - if (IS_ERR(res->cfg_clk)) - return PTR_ERR(res->cfg_clk); - - res->master_clk = devm_clk_get(dev, "bus_master"); - if (IS_ERR(res->master_clk)) - return PTR_ERR(res->master_clk); - - res->slave_clk = devm_clk_get(dev, "bus_slave"); - if (IS_ERR(res->slave_clk)) - return PTR_ERR(res->slave_clk); - - res->pipe_clk = devm_clk_get(dev, "pipe"); - return PTR_ERR_OR_ZERO(res->pipe_clk); -} - -static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; - - clk_disable_unprepare(res->slave_clk); - clk_disable_unprepare(res->master_clk); - clk_disable_unprepare(res->cfg_clk); - clk_disable_unprepare(res->aux_clk); - - regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); -} - -static void qcom_pcie_post_deinit_2_3_2(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; - - clk_disable_unprepare(res->pipe_clk); -} - -static int qcom_pcie_init_2_3_2(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - u32 val; - int ret; - - ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); - if (ret < 0) { - dev_err(dev, "cannot enable regulators\n"); - return ret; - } - - ret = clk_prepare_enable(res->aux_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable aux clock\n"); - goto err_aux_clk; - } - - ret = clk_prepare_enable(res->cfg_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable cfg clock\n"); - goto err_cfg_clk; - } - - ret = clk_prepare_enable(res->master_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable master clock\n"); - goto err_master_clk; - } - - ret = clk_prepare_enable(res->slave_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable slave clock\n"); - goto err_slave_clk; - } - - /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); - val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); - - /* change DBI base address */ - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); - - /* MAC PHY_POWERDOWN MUX DISABLE */ - val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); - val &= ~BIT(29); - writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); - - val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); - val |= BIT(4); - writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); - - val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); - val |= BIT(31); - writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); - - return 0; - -err_slave_clk: - clk_disable_unprepare(res->master_clk); -err_master_clk: - clk_disable_unprepare(res->cfg_clk); -err_cfg_clk: - clk_disable_unprepare(res->aux_clk); - -err_aux_clk: - regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); - - return ret; -} - -static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - int ret; - - ret = clk_prepare_enable(res->pipe_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable pipe clock\n"); - return ret; - } - - return 0; -} - -static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - - res->aux_clk = devm_clk_get(dev, "aux"); - if (IS_ERR(res->aux_clk)) - return PTR_ERR(res->aux_clk); - - res->master_clk = devm_clk_get(dev, "master_bus"); - if (IS_ERR(res->master_clk)) - return PTR_ERR(res->master_clk); - - res->slave_clk = devm_clk_get(dev, "slave_bus"); - if (IS_ERR(res->slave_clk)) - return PTR_ERR(res->slave_clk); - - res->axi_m_reset = devm_reset_control_get_exclusive(dev, "axi_m"); - if (IS_ERR(res->axi_m_reset)) - return PTR_ERR(res->axi_m_reset); - - res->axi_s_reset = devm_reset_control_get_exclusive(dev, "axi_s"); - if (IS_ERR(res->axi_s_reset)) - return PTR_ERR(res->axi_s_reset); - - res->pipe_reset = devm_reset_control_get_exclusive(dev, "pipe"); - if (IS_ERR(res->pipe_reset)) - return PTR_ERR(res->pipe_reset); - - res->axi_m_vmid_reset = devm_reset_control_get_exclusive(dev, - "axi_m_vmid"); - if (IS_ERR(res->axi_m_vmid_reset)) - return PTR_ERR(res->axi_m_vmid_reset); - - res->axi_s_xpu_reset = devm_reset_control_get_exclusive(dev, - "axi_s_xpu"); - if (IS_ERR(res->axi_s_xpu_reset)) - return PTR_ERR(res->axi_s_xpu_reset); - - res->parf_reset = devm_reset_control_get_exclusive(dev, "parf"); - if (IS_ERR(res->parf_reset)) - return PTR_ERR(res->parf_reset); - - res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); - if (IS_ERR(res->phy_reset)) - return PTR_ERR(res->phy_reset); - - res->axi_m_sticky_reset = devm_reset_control_get_exclusive(dev, - "axi_m_sticky"); - if (IS_ERR(res->axi_m_sticky_reset)) - return PTR_ERR(res->axi_m_sticky_reset); - - res->pipe_sticky_reset = devm_reset_control_get_exclusive(dev, - "pipe_sticky"); - if (IS_ERR(res->pipe_sticky_reset)) - return PTR_ERR(res->pipe_sticky_reset); - - res->pwr_reset = devm_reset_control_get_exclusive(dev, "pwr"); - if (IS_ERR(res->pwr_reset)) - return PTR_ERR(res->pwr_reset); - - res->ahb_reset = devm_reset_control_get_exclusive(dev, "ahb"); - if (IS_ERR(res->ahb_reset)) - return PTR_ERR(res->ahb_reset); - - res->phy_ahb_reset = devm_reset_control_get_exclusive(dev, "phy_ahb"); - if (IS_ERR(res->phy_ahb_reset)) - return PTR_ERR(res->phy_ahb_reset); - - return 0; -} - -static void qcom_pcie_deinit_2_4_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; - - reset_control_assert(res->axi_m_reset); - reset_control_assert(res->axi_s_reset); - reset_control_assert(res->pipe_reset); - reset_control_assert(res->pipe_sticky_reset); - reset_control_assert(res->phy_reset); - reset_control_assert(res->phy_ahb_reset); - reset_control_assert(res->axi_m_sticky_reset); - reset_control_assert(res->pwr_reset); - reset_control_assert(res->ahb_reset); - clk_disable_unprepare(res->aux_clk); - clk_disable_unprepare(res->master_clk); - clk_disable_unprepare(res->slave_clk); -} - -static int qcom_pcie_init_2_4_0(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - u32 val; - int ret; - - ret = reset_control_assert(res->axi_m_reset); - if (ret) { - dev_err(dev, "cannot assert axi master reset\n"); - return ret; - } - - ret = reset_control_assert(res->axi_s_reset); - if (ret) { - dev_err(dev, "cannot assert axi slave reset\n"); - return ret; - } - - usleep_range(10000, 12000); - - ret = reset_control_assert(res->pipe_reset); - if (ret) { - dev_err(dev, "cannot assert pipe reset\n"); - return ret; - } - - ret = reset_control_assert(res->pipe_sticky_reset); - if (ret) { - dev_err(dev, "cannot assert pipe sticky reset\n"); - return ret; - } - - ret = reset_control_assert(res->phy_reset); - if (ret) { - dev_err(dev, "cannot assert phy reset\n"); - return ret; - } - - ret = reset_control_assert(res->phy_ahb_reset); - if (ret) { - dev_err(dev, "cannot assert phy ahb reset\n"); - return ret; - } - - usleep_range(10000, 12000); - - ret = reset_control_assert(res->axi_m_sticky_reset); - if (ret) { - dev_err(dev, "cannot assert axi master sticky reset\n"); - return ret; - } - - ret = reset_control_assert(res->pwr_reset); - if (ret) { - dev_err(dev, "cannot assert power reset\n"); - return ret; - } - - ret = reset_control_assert(res->ahb_reset); - if (ret) { - dev_err(dev, "cannot assert ahb reset\n"); - return ret; - } - - usleep_range(10000, 12000); - - ret = reset_control_deassert(res->phy_ahb_reset); - if (ret) { - dev_err(dev, "cannot deassert phy ahb reset\n"); - return ret; - } - - ret = reset_control_deassert(res->phy_reset); - if (ret) { - dev_err(dev, "cannot deassert phy reset\n"); - goto err_rst_phy; - } - - ret = reset_control_deassert(res->pipe_reset); - if (ret) { - dev_err(dev, "cannot deassert pipe reset\n"); - goto err_rst_pipe; - } - - ret = reset_control_deassert(res->pipe_sticky_reset); - if (ret) { - dev_err(dev, "cannot deassert pipe sticky reset\n"); - goto err_rst_pipe_sticky; - } - - usleep_range(10000, 12000); - - ret = reset_control_deassert(res->axi_m_reset); - if (ret) { - dev_err(dev, "cannot deassert axi master reset\n"); - goto err_rst_axi_m; - } - - ret = reset_control_deassert(res->axi_m_sticky_reset); - if (ret) { - dev_err(dev, "cannot deassert axi master sticky reset\n"); - goto err_rst_axi_m_sticky; - } - - ret = reset_control_deassert(res->axi_s_reset); - if (ret) { - dev_err(dev, "cannot deassert axi slave reset\n"); - goto err_rst_axi_s; - } - - ret = reset_control_deassert(res->pwr_reset); - if (ret) { - dev_err(dev, "cannot deassert power reset\n"); - goto err_rst_pwr; - } - - ret = reset_control_deassert(res->ahb_reset); - if (ret) { - dev_err(dev, "cannot deassert ahb reset\n"); - goto err_rst_ahb; - } - - usleep_range(10000, 12000); - - ret = clk_prepare_enable(res->aux_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable iface clock\n"); - goto err_clk_aux; - } - - ret = clk_prepare_enable(res->master_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable core clock\n"); - goto err_clk_axi_m; - } - - ret = clk_prepare_enable(res->slave_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable phy clock\n"); - goto err_clk_axi_s; - } - - /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); - val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); - - /* change DBI base address */ - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); - - /* MAC PHY_POWERDOWN MUX DISABLE */ - val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL); - val &= ~BIT(29); - writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL); - - val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); - val |= BIT(4); - writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL); - - val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); - val |= BIT(31); - writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2); - - return 0; - -err_clk_axi_s: - clk_disable_unprepare(res->master_clk); -err_clk_axi_m: - clk_disable_unprepare(res->aux_clk); -err_clk_aux: - reset_control_assert(res->ahb_reset); -err_rst_ahb: - reset_control_assert(res->pwr_reset); -err_rst_pwr: - reset_control_assert(res->axi_s_reset); -err_rst_axi_s: - reset_control_assert(res->axi_m_sticky_reset); -err_rst_axi_m_sticky: - reset_control_assert(res->axi_m_reset); -err_rst_axi_m: - reset_control_assert(res->pipe_sticky_reset); -err_rst_pipe_sticky: - reset_control_assert(res->pipe_reset); -err_rst_pipe: - reset_control_assert(res->phy_reset); -err_rst_phy: - reset_control_assert(res->phy_ahb_reset); - return ret; -} - -static int qcom_pcie_get_resources_2_3_3(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - int i; - const char *rst_names[] = { "axi_m", "axi_s", "pipe", - "axi_m_sticky", "sticky", - "ahb", "sleep", }; - - res->iface = devm_clk_get(dev, "iface"); - if (IS_ERR(res->iface)) - return PTR_ERR(res->iface); - - res->axi_m_clk = devm_clk_get(dev, "axi_m"); - if (IS_ERR(res->axi_m_clk)) - return PTR_ERR(res->axi_m_clk); - - res->axi_s_clk = devm_clk_get(dev, "axi_s"); - if (IS_ERR(res->axi_s_clk)) - return PTR_ERR(res->axi_s_clk); - - res->ahb_clk = devm_clk_get(dev, "ahb"); - if (IS_ERR(res->ahb_clk)) - return PTR_ERR(res->ahb_clk); - - res->aux_clk = devm_clk_get(dev, "aux"); - if (IS_ERR(res->aux_clk)) - return PTR_ERR(res->aux_clk); - - for (i = 0; i < ARRAY_SIZE(rst_names); i++) { - res->rst[i] = devm_reset_control_get(dev, rst_names[i]); - if (IS_ERR(res->rst[i])) - return PTR_ERR(res->rst[i]); - } - - return 0; -} - -static void qcom_pcie_deinit_2_3_3(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; - - clk_disable_unprepare(res->iface); - clk_disable_unprepare(res->axi_m_clk); - clk_disable_unprepare(res->axi_s_clk); - clk_disable_unprepare(res->ahb_clk); - clk_disable_unprepare(res->aux_clk); -} - -static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie) -{ - struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; - struct dw_pcie *pci = pcie->pci; - struct device *dev = pci->dev; - int i, ret; - u32 val; - - for (i = 0; i < ARRAY_SIZE(res->rst); i++) { - ret = reset_control_assert(res->rst[i]); - if (ret) { - dev_err(dev, "reset #%d assert failed (%d)\n", i, ret); - return ret; - } - } - - usleep_range(2000, 2500); - - for (i = 0; i < ARRAY_SIZE(res->rst); i++) { - ret = reset_control_deassert(res->rst[i]); - if (ret) { - dev_err(dev, "reset #%d deassert failed (%d)\n", i, - ret); - return ret; - } - } - - /* - * Don't have a way to see if the reset has completed. - * Wait for some time. - */ - usleep_range(2000, 2500); - - ret = clk_prepare_enable(res->iface); - if (ret) { - dev_err(dev, "cannot prepare/enable core clock\n"); - goto err_clk_iface; - } - - ret = clk_prepare_enable(res->axi_m_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable core clock\n"); - goto err_clk_axi_m; - } - - ret = clk_prepare_enable(res->axi_s_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable axi slave clock\n"); - goto err_clk_axi_s; - } - - ret = clk_prepare_enable(res->ahb_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable ahb clock\n"); - goto err_clk_ahb; - } - - ret = clk_prepare_enable(res->aux_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable aux clock\n"); - goto err_clk_aux; - } - - writel(SLV_ADDR_SPACE_SZ, - pcie->parf + PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE); - - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); - val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); - - writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); - - writel(MST_WAKEUP_EN | SLV_WAKEUP_EN | MSTR_ACLK_CGC_DIS - | SLV_ACLK_CGC_DIS | CORE_CLK_CGC_DIS | - AUX_PWR_DET | L23_CLK_RMV_DIS | L1_CLK_RMV_DIS, - pcie->parf + PCIE20_PARF_SYS_CTRL); - writel(0, pcie->parf + PCIE20_PARF_Q2A_FLUSH); - - writel(CMD_BME_VAL, pci->dbi_base + PCIE20_COMMAND_STATUS); - writel(DBI_RO_WR_EN, pci->dbi_base + PCIE20_MISC_CONTROL_1_REG); - writel(PCIE_CAP_LINK1_VAL, pci->dbi_base + PCIE20_CAP_LINK_1); - - val = readl(pci->dbi_base + PCIE20_CAP_LINK_CAPABILITIES); - val &= ~PCIE20_CAP_ACTIVE_STATE_LINK_PM_SUPPORT; - writel(val, pci->dbi_base + PCIE20_CAP_LINK_CAPABILITIES); - - writel(PCIE_CAP_CPL_TIMEOUT_DISABLE, pci->dbi_base + - PCIE20_DEVICE_CONTROL2_STATUS2); - - return 0; - -err_clk_aux: - clk_disable_unprepare(res->ahb_clk); -err_clk_ahb: - clk_disable_unprepare(res->axi_s_clk); -err_clk_axi_s: - clk_disable_unprepare(res->axi_m_clk); -err_clk_axi_m: - clk_disable_unprepare(res->iface); -err_clk_iface: - /* - * Not checking for failure, will anyway return - * the original failure in 'ret'. - */ - for (i = 0; i < ARRAY_SIZE(res->rst); i++) - reset_control_assert(res->rst[i]); - - return ret; -} - -static int qcom_pcie_link_up(struct dw_pcie *pci) -{ - u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA); - - return !!(val & PCI_EXP_LNKSTA_DLLLA); -} - -static int qcom_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct qcom_pcie *pcie = to_qcom_pcie(pci); - int ret; - - pm_runtime_get_sync(pci->dev); - qcom_ep_reset_assert(pcie); - - ret = pcie->ops->init(pcie); - if (ret) - return ret; - - ret = phy_power_on(pcie->phy); - if (ret) - goto err_deinit; - - if (pcie->ops->post_init) { - ret = pcie->ops->post_init(pcie); - if (ret) - goto err_disable_phy; - } - - dw_pcie_setup_rc(pp); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - dw_pcie_msi_init(pp); - - qcom_ep_reset_deassert(pcie); - - ret = qcom_pcie_establish_link(pcie); - if (ret) - goto err; - - return 0; -err: - qcom_ep_reset_assert(pcie); - if (pcie->ops->post_deinit) - pcie->ops->post_deinit(pcie); -err_disable_phy: - phy_power_off(pcie->phy); -err_deinit: - pcie->ops->deinit(pcie); - pm_runtime_put(pci->dev); - - return ret; -} - -static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - /* the device class is not reported correctly from the register */ - if (where == PCI_CLASS_REVISION && size == 4) { - *val = readl(pci->dbi_base + PCI_CLASS_REVISION); - *val &= 0xff; /* keep revision id */ - *val |= PCI_CLASS_BRIDGE_PCI << 16; - return PCIBIOS_SUCCESSFUL; - } - - return dw_pcie_read(pci->dbi_base + where, size, val); -} - -static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { - .host_init = qcom_pcie_host_init, - .rd_own_conf = qcom_pcie_rd_own_conf, -}; - -/* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ -static const struct qcom_pcie_ops ops_2_1_0 = { - .get_resources = qcom_pcie_get_resources_2_1_0, - .init = qcom_pcie_init_2_1_0, - .deinit = qcom_pcie_deinit_2_1_0, - .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, -}; - -/* Qcom IP rev.: 1.0.0 Synopsys IP rev.: 4.11a */ -static const struct qcom_pcie_ops ops_1_0_0 = { - .get_resources = qcom_pcie_get_resources_1_0_0, - .init = qcom_pcie_init_1_0_0, - .deinit = qcom_pcie_deinit_1_0_0, - .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, -}; - -/* Qcom IP rev.: 2.3.2 Synopsys IP rev.: 4.21a */ -static const struct qcom_pcie_ops ops_2_3_2 = { - .get_resources = qcom_pcie_get_resources_2_3_2, - .init = qcom_pcie_init_2_3_2, - .post_init = qcom_pcie_post_init_2_3_2, - .deinit = qcom_pcie_deinit_2_3_2, - .post_deinit = qcom_pcie_post_deinit_2_3_2, - .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, -}; - -/* Qcom IP rev.: 2.4.0 Synopsys IP rev.: 4.20a */ -static const struct qcom_pcie_ops ops_2_4_0 = { - .get_resources = qcom_pcie_get_resources_2_4_0, - .init = qcom_pcie_init_2_4_0, - .deinit = qcom_pcie_deinit_2_4_0, - .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, -}; - -/* Qcom IP rev.: 2.3.3 Synopsys IP rev.: 4.30a */ -static const struct qcom_pcie_ops ops_2_3_3 = { - .get_resources = qcom_pcie_get_resources_2_3_3, - .init = qcom_pcie_init_2_3_3, - .deinit = qcom_pcie_deinit_2_3_3, - .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, -}; - -static const struct dw_pcie_ops dw_pcie_ops = { - .link_up = qcom_pcie_link_up, -}; - -static int qcom_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *res; - struct pcie_port *pp; - struct dw_pcie *pci; - struct qcom_pcie *pcie; - int ret; - - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); - if (!pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pm_runtime_enable(dev); - pci->dev = dev; - pci->ops = &dw_pcie_ops; - pp = &pci->pp; - - pcie->pci = pci; - - pcie->ops = of_device_get_match_data(dev); - - pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW); - if (IS_ERR(pcie->reset)) - return PTR_ERR(pcie->reset); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); - pcie->parf = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->parf)) - return PTR_ERR(pcie->parf); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); - pcie->elbi = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->elbi)) - return PTR_ERR(pcie->elbi); - - pcie->phy = devm_phy_optional_get(dev, "pciephy"); - if (IS_ERR(pcie->phy)) - return PTR_ERR(pcie->phy); - - ret = pcie->ops->get_resources(pcie); - if (ret) - return ret; - - pp->root_bus_nr = -1; - pp->ops = &qcom_pcie_dw_ops; - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->msi_irq = platform_get_irq_byname(pdev, "msi"); - if (pp->msi_irq < 0) - return pp->msi_irq; - } - - ret = phy_init(pcie->phy); - if (ret) { - pm_runtime_disable(&pdev->dev); - return ret; - } - - platform_set_drvdata(pdev, pcie); - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "cannot initialize host\n"); - pm_runtime_disable(&pdev->dev); - return ret; - } - - return 0; -} - -static const struct of_device_id qcom_pcie_match[] = { - { .compatible = "qcom,pcie-apq8084", .data = &ops_1_0_0 }, - { .compatible = "qcom,pcie-ipq8064", .data = &ops_2_1_0 }, - { .compatible = "qcom,pcie-apq8064", .data = &ops_2_1_0 }, - { .compatible = "qcom,pcie-msm8996", .data = &ops_2_3_2 }, - { .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 }, - { .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 }, - { } -}; - -static struct platform_driver qcom_pcie_driver = { - .probe = qcom_pcie_probe, - .driver = { - .name = "qcom-pcie", - .suppress_bind_attrs = true, - .of_match_table = qcom_pcie_match, - }, -}; -builtin_platform_driver(qcom_pcie_driver); diff --git a/drivers/pci/dwc/pcie-spear13xx.c b/drivers/pci/dwc/pcie-spear13xx.c deleted file mode 100644 index ecb58f7b7566..000000000000 --- a/drivers/pci/dwc/pcie-spear13xx.c +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for ST Microelectronics SPEAr13xx SoCs - * - * SPEAr13xx PCIe Glue Layer Source Code - * - * Copyright (C) 2010-2014 ST Microelectronics - * Pratyush Anand - * Mohit Kumar - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-designware.h" - -struct spear13xx_pcie { - struct dw_pcie *pci; - void __iomem *app_base; - struct phy *phy; - struct clk *clk; - bool is_gen1; -}; - -struct pcie_app_reg { - u32 app_ctrl_0; /* cr0 */ - u32 app_ctrl_1; /* cr1 */ - u32 app_status_0; /* cr2 */ - u32 app_status_1; /* cr3 */ - u32 msg_status; /* cr4 */ - u32 msg_payload; /* cr5 */ - u32 int_sts; /* cr6 */ - u32 int_clr; /* cr7 */ - u32 int_mask; /* cr8 */ - u32 mst_bmisc; /* cr9 */ - u32 phy_ctrl; /* cr10 */ - u32 phy_status; /* cr11 */ - u32 cxpl_debug_info_0; /* cr12 */ - u32 cxpl_debug_info_1; /* cr13 */ - u32 ven_msg_ctrl_0; /* cr14 */ - u32 ven_msg_ctrl_1; /* cr15 */ - u32 ven_msg_data_0; /* cr16 */ - u32 ven_msg_data_1; /* cr17 */ - u32 ven_msi_0; /* cr18 */ - u32 ven_msi_1; /* cr19 */ - u32 mst_rmisc; /* cr20 */ -}; - -/* CR0 ID */ -#define APP_LTSSM_ENABLE_ID 3 -#define DEVICE_TYPE_RC (4 << 25) -#define MISCTRL_EN_ID 30 -#define REG_TRANSLATION_ENABLE 31 - -/* CR3 ID */ -#define XMLH_LINK_UP (1 << 6) - -/* CR6 */ -#define MSI_CTRL_INT (1 << 26) - -#define EXP_CAP_ID_OFFSET 0x70 - -#define to_spear13xx_pcie(x) dev_get_drvdata((x)->dev) - -static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie) -{ - struct dw_pcie *pci = spear13xx_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; - u32 val; - u32 exp_cap_off = EXP_CAP_ID_OFFSET; - - if (dw_pcie_link_up(pci)) { - dev_err(pci->dev, "link already up\n"); - return 0; - } - - dw_pcie_setup_rc(pp); - - /* - * this controller support only 128 bytes read size, however its - * default value in capability register is 512 bytes. So force - * it to 128 here. - */ - dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val); - val &= ~PCI_EXP_DEVCTL_READRQ; - dw_pcie_write(pci->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val); - - dw_pcie_write(pci->dbi_base + PCI_VENDOR_ID, 2, 0x104A); - dw_pcie_write(pci->dbi_base + PCI_DEVICE_ID, 2, 0xCD80); - - /* - * if is_gen1 is set then handle it, so that some buggy card - * also works - */ - if (spear13xx_pcie->is_gen1) { - dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCAP, - 4, &val); - if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { - val &= ~((u32)PCI_EXP_LNKCAP_SLS); - val |= PCI_EXP_LNKCAP_SLS_2_5GB; - dw_pcie_write(pci->dbi_base + exp_cap_off + - PCI_EXP_LNKCAP, 4, val); - } - - dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2, - 2, &val); - if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { - val &= ~((u32)PCI_EXP_LNKCAP_SLS); - val |= PCI_EXP_LNKCAP_SLS_2_5GB; - dw_pcie_write(pci->dbi_base + exp_cap_off + - PCI_EXP_LNKCTL2, 2, val); - } - } - - /* enable ltssm */ - writel(DEVICE_TYPE_RC | (1 << MISCTRL_EN_ID) - | (1 << APP_LTSSM_ENABLE_ID) - | ((u32)1 << REG_TRANSLATION_ENABLE), - &app_reg->app_ctrl_0); - - return dw_pcie_wait_for_link(pci); -} - -static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg) -{ - struct spear13xx_pcie *spear13xx_pcie = arg; - struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; - struct dw_pcie *pci = spear13xx_pcie->pci; - struct pcie_port *pp = &pci->pp; - unsigned int status; - - status = readl(&app_reg->int_sts); - - if (status & MSI_CTRL_INT) { - BUG_ON(!IS_ENABLED(CONFIG_PCI_MSI)); - dw_handle_msi_irq(pp); - } - - writel(status, &app_reg->int_clr); - - return IRQ_HANDLED; -} - -static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pcie) -{ - struct dw_pcie *pci = spear13xx_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; - - /* Enable MSI interrupt */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - dw_pcie_msi_init(pp); - writel(readl(&app_reg->int_mask) | - MSI_CTRL_INT, &app_reg->int_mask); - } -} - -static int spear13xx_pcie_link_up(struct dw_pcie *pci) -{ - struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci); - struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; - - if (readl(&app_reg->app_status_1) & XMLH_LINK_UP) - return 1; - - return 0; -} - -static int spear13xx_pcie_host_init(struct pcie_port *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci); - - spear13xx_pcie_establish_link(spear13xx_pcie); - spear13xx_pcie_enable_interrupts(spear13xx_pcie); - - return 0; -} - -static const struct dw_pcie_host_ops spear13xx_pcie_host_ops = { - .host_init = spear13xx_pcie_host_init, -}; - -static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, - struct platform_device *pdev) -{ - struct dw_pcie *pci = spear13xx_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = &pdev->dev; - int ret; - - pp->irq = platform_get_irq(pdev, 0); - if (pp->irq < 0) { - dev_err(dev, "failed to get irq\n"); - return pp->irq; - } - ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "spear1340-pcie", spear13xx_pcie); - if (ret) { - dev_err(dev, "failed to request irq %d\n", pp->irq); - return ret; - } - - pp->root_bus_nr = -1; - pp->ops = &spear13xx_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .link_up = spear13xx_pcie_link_up, -}; - -static int spear13xx_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_pcie *pci; - struct spear13xx_pcie *spear13xx_pcie; - struct device_node *np = dev->of_node; - struct resource *dbi_base; - int ret; - - spear13xx_pcie = devm_kzalloc(dev, sizeof(*spear13xx_pcie), GFP_KERNEL); - if (!spear13xx_pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - spear13xx_pcie->pci = pci; - - spear13xx_pcie->phy = devm_phy_get(dev, "pcie-phy"); - if (IS_ERR(spear13xx_pcie->phy)) { - ret = PTR_ERR(spear13xx_pcie->phy); - if (ret == -EPROBE_DEFER) - dev_info(dev, "probe deferred\n"); - else - dev_err(dev, "couldn't get pcie-phy\n"); - return ret; - } - - phy_init(spear13xx_pcie->phy); - - spear13xx_pcie->clk = devm_clk_get(dev, NULL); - if (IS_ERR(spear13xx_pcie->clk)) { - dev_err(dev, "couldn't get clk for pcie\n"); - return PTR_ERR(spear13xx_pcie->clk); - } - ret = clk_prepare_enable(spear13xx_pcie->clk); - if (ret) { - dev_err(dev, "couldn't enable clk for pcie\n"); - return ret; - } - - dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); - if (IS_ERR(pci->dbi_base)) { - dev_err(dev, "couldn't remap dbi base %p\n", dbi_base); - ret = PTR_ERR(pci->dbi_base); - goto fail_clk; - } - spear13xx_pcie->app_base = pci->dbi_base + 0x2000; - - if (of_property_read_bool(np, "st,pcie-is-gen1")) - spear13xx_pcie->is_gen1 = true; - - platform_set_drvdata(pdev, spear13xx_pcie); - - ret = spear13xx_add_pcie_port(spear13xx_pcie, pdev); - if (ret < 0) - goto fail_clk; - - return 0; - -fail_clk: - clk_disable_unprepare(spear13xx_pcie->clk); - - return ret; -} - -static const struct of_device_id spear13xx_pcie_of_match[] = { - { .compatible = "st,spear1340-pcie", }, - {}, -}; - -static struct platform_driver spear13xx_pcie_driver = { - .probe = spear13xx_pcie_probe, - .driver = { - .name = "spear-pcie", - .of_match_table = of_match_ptr(spear13xx_pcie_of_match), - .suppress_bind_attrs = true, - }, -}; - -builtin_platform_driver(spear13xx_pcie_driver); diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig deleted file mode 100644 index a96e23bda664..000000000000 --- a/drivers/pci/host/Kconfig +++ /dev/null @@ -1,246 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -menu "PCI host controller drivers" - depends on PCI - -config PCI_MVEBU - bool "Marvell EBU PCIe controller" - depends on ARCH_MVEBU || ARCH_DOVE || COMPILE_TEST - depends on MVEBU_MBUS - depends on ARM - depends on OF - -config PCI_AARDVARK - bool "Aardvark PCIe controller" - depends on (ARCH_MVEBU && ARM64) || COMPILE_TEST - depends on OF - depends on PCI_MSI_IRQ_DOMAIN - help - Add support for Aardvark 64bit PCIe Host Controller. This - controller is part of the South Bridge of the Marvel Armada - 3700 SoC. - -config PCIE_XILINX_NWL - bool "NWL PCIe Core" - depends on ARCH_ZYNQMP || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN - help - Say 'Y' here if you want kernel support for Xilinx - NWL PCIe controller. The controller can act as Root Port - or End Point. The current option selection will only - support root port enabling. - -config PCI_FTPCI100 - bool "Faraday Technology FTPCI100 PCI controller" - depends on OF - default ARCH_GEMINI - -config PCI_TEGRA - bool "NVIDIA Tegra PCIe controller" - depends on ARCH_TEGRA || COMPILE_TEST - depends on PCI_MSI_IRQ_DOMAIN - help - Say Y here if you want support for the PCIe host controller found - on NVIDIA Tegra SoCs. - -config PCI_RCAR_GEN2 - bool "Renesas R-Car Gen2 Internal PCI controller" - depends on ARCH_RENESAS || COMPILE_TEST - depends on ARM - help - Say Y here if you want internal PCI support on R-Car Gen2 SoC. - There are 3 internal PCI controllers available with a single - built-in EHCI/OHCI host controller present on each one. - -config PCIE_RCAR - bool "Renesas R-Car PCIe 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. - -config PCI_HOST_COMMON - bool - select PCI_ECAM - -config PCI_HOST_GENERIC - bool "Generic PCI host controller" - depends on OF - select PCI_HOST_COMMON - select IRQ_DOMAIN - select PCI_DOMAINS - help - Say Y here if you want to support a simple generic PCI host - controller, such as the one emulated by kvmtool. - -config PCIE_XILINX - bool "Xilinx AXI PCIe host bridge support" - depends on ARCH_ZYNQ || MICROBLAZE || (MIPS && PCI_DRIVERS_GENERIC) || COMPILE_TEST - help - Say 'Y' here if you want kernel to support the Xilinx AXI PCIe - Host Bridge driver. - -config PCI_XGENE - bool "X-Gene PCIe controller" - depends on ARM64 || COMPILE_TEST - depends on OF || (ACPI && PCI_QUIRKS) - help - Say Y here if you want internal PCI support on APM X-Gene SoC. - There are 5 internal PCIe ports available. Each port is GEN3 capable - and have varied lanes from x1 to x8. - -config PCI_XGENE_MSI - bool "X-Gene v1 PCIe MSI feature" - depends on PCI_XGENE - depends on PCI_MSI_IRQ_DOMAIN - default y - help - Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC. - This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC. - -config PCI_V3_SEMI - bool "V3 Semiconductor PCI controller" - depends on OF - depends on ARM || COMPILE_TEST - default ARCH_INTEGRATOR_AP - -config PCI_VERSATILE - bool "ARM Versatile PB PCI controller" - depends on ARCH_VERSATILE - -config PCIE_IPROC - tristate - select PCI_DOMAINS - help - This enables the iProc PCIe core controller support for Broadcom's - iProc family of SoCs. An appropriate bus interface driver needs - to be enabled to select this. - -config PCIE_IPROC_PLATFORM - tristate "Broadcom iProc PCIe platform bus driver" - depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST) - depends on OF - select PCIE_IPROC - default ARCH_BCM_IPROC - help - Say Y here if you want to use the Broadcom iProc PCIe controller - through the generic platform bus interface - -config PCIE_IPROC_BCMA - tristate "Broadcom iProc PCIe BCMA bus driver" - depends on ARM && (ARCH_BCM_IPROC || COMPILE_TEST) - select PCIE_IPROC - select BCMA - default ARCH_BCM_5301X - help - Say Y here if you want to use the Broadcom iProc PCIe controller - through the BCMA bus interface - -config PCIE_IPROC_MSI - bool "Broadcom iProc PCIe MSI support" - depends on PCIE_IPROC_PLATFORM || PCIE_IPROC_BCMA - depends on PCI_MSI_IRQ_DOMAIN - default ARCH_BCM_IPROC - help - Say Y here if you want to enable MSI support for Broadcom's iProc - PCIe controller - -config PCIE_ALTERA - bool "Altera PCIe controller" - depends on ARM || NIOS2 || COMPILE_TEST - select PCI_DOMAINS - help - Say Y here if you want to enable PCIe controller support on Altera - FPGA. - -config PCIE_ALTERA_MSI - bool "Altera PCIe MSI feature" - depends on PCIE_ALTERA - depends on PCI_MSI_IRQ_DOMAIN - help - Say Y here if you want PCIe MSI support for the Altera FPGA. - This MSI driver supports Altera MSI to GIC controller IP. - -config PCI_HOST_THUNDER_PEM - bool "Cavium Thunder PCIe controller to off-chip devices" - depends on ARM64 || COMPILE_TEST - depends on OF || (ACPI && PCI_QUIRKS) - select PCI_HOST_COMMON - help - Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs. - -config PCI_HOST_THUNDER_ECAM - bool "Cavium Thunder ECAM controller to on-chip devices on pass-1.x silicon" - depends on ARM64 || COMPILE_TEST - depends on OF || (ACPI && PCI_QUIRKS) - select PCI_HOST_COMMON - help - Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs. - -config PCIE_ROCKCHIP - bool - depends on PCI - -config PCIE_ROCKCHIP_HOST - tristate "Rockchip PCIe host controller" - depends on ARCH_ROCKCHIP || COMPILE_TEST - depends on OF - depends on PCI_MSI_IRQ_DOMAIN - select MFD_SYSCON - select PCIE_ROCKCHIP - help - Say Y here if you want internal PCI support on Rockchip SoC. - There is 1 internal PCIe port available to support GEN2 with - 4 slots. - -config PCIE_ROCKCHIP_EP - bool "Rockchip PCIe endpoint controller" - depends on ARCH_ROCKCHIP || COMPILE_TEST - depends on OF - depends on PCI_ENDPOINT - select MFD_SYSCON - select PCIE_ROCKCHIP - help - Say Y here if you want to support Rockchip PCIe controller in - endpoint mode on Rockchip SoC. There is 1 internal PCIe port - available to support GEN2 with 4 slots. - -config PCIE_MEDIATEK - bool "MediaTek PCIe controller" - depends on ARCH_MEDIATEK || COMPILE_TEST - depends on OF - depends on PCI_MSI_IRQ_DOMAIN - help - Say Y here if you want to enable PCIe controller support on - MediaTek SoCs. - -config PCIE_TANGO_SMP8759 - bool "Tango SMP8759 PCIe controller (DANGEROUS)" - depends on ARCH_TANGO && PCI_MSI && OF - depends on BROKEN - select PCI_HOST_COMMON - help - Say Y here to enable PCIe controller support for Sigma Designs - Tango SMP8759-based systems. - - Note: The SMP8759 controller multiplexes PCI config and MMIO - accesses, and Linux doesn't provide a way to serialize them. - This can lead to data corruption if drivers perform concurrent - config and MMIO accesses. - -config VMD - depends on PCI_MSI && X86_64 && SRCU - tristate "Intel Volume Management Device Driver" - ---help--- - Adds support for the Intel Volume Management Device (VMD). VMD is a - secondary PCI host bridge that allows PCI Express root ports, - and devices attached to them, to be removed from the default - PCI domain and placed within the VMD domain. This provides - more bus resources than are otherwise possible with a - single domain. If you know your system provides one of these and - has devices attached to it, say Y; if you are not sure, say N. - - To compile this driver as a module, choose M here: the - module will be called vmd. - -endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile deleted file mode 100644 index 11d21b026d37..000000000000 --- a/drivers/pci/host/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o -obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o -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_PCI_HOST_COMMON) += pci-host-common.o -obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o -obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o -obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o -obj-$(CONFIG_PCI_V3_SEMI) += pci-v3-semi.o -obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o -obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o -obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o -obj-$(CONFIG_PCIE_IPROC_MSI) += pcie-iproc-msi.o -obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o -obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o -obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o -obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o -obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o -obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o -obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o -obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o -obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o -obj-$(CONFIG_VMD) += vmd.o - -# The following drivers are for devices that use the generic ACPI -# pci_root.c driver but don't support standard ECAM config access. -# They contain MCFG quirks to replace the generic ECAM accessors with -# device-specific ones that are shared with the DT driver. - -# The ACPI driver is generic and should not require driver-specific -# config options to be enabled, so we always build these drivers on -# ARM64 and use internal ifdefs to only build the pieces we need -# depending on whether ACPI, the DT driver, or both are enabled. - -ifdef CONFIG_PCI -obj-$(CONFIG_ARM64) += pci-thunder-ecam.o -obj-$(CONFIG_ARM64) += pci-thunder-pem.o -obj-$(CONFIG_ARM64) += pci-xgene.o -endif diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c deleted file mode 100644 index d3172d5d3d35..000000000000 --- a/drivers/pci/host/pci-aardvark.c +++ /dev/null @@ -1,978 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for the Aardvark PCIe controller, used on Marvell Armada - * 3700. - * - * Copyright (C) 2016 Marvell - * - * Author: Hezi Shahmoon - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -/* PCIe core registers */ -#define PCIE_CORE_CMD_STATUS_REG 0x4 -#define PCIE_CORE_CMD_IO_ACCESS_EN BIT(0) -#define PCIE_CORE_CMD_MEM_ACCESS_EN BIT(1) -#define PCIE_CORE_CMD_MEM_IO_REQ_EN BIT(2) -#define PCIE_CORE_DEV_CTRL_STATS_REG 0xc8 -#define PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE (0 << 4) -#define PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT 5 -#define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11) -#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT 12 -#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ 0x2 -#define PCIE_CORE_LINK_CTRL_STAT_REG 0xd0 -#define PCIE_CORE_LINK_L0S_ENTRY BIT(0) -#define PCIE_CORE_LINK_TRAINING BIT(5) -#define PCIE_CORE_LINK_WIDTH_SHIFT 20 -#define PCIE_CORE_ERR_CAPCTL_REG 0x118 -#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) -#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) -#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7) -#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8) - -/* PIO registers base address and register offsets */ -#define PIO_BASE_ADDR 0x4000 -#define PIO_CTRL (PIO_BASE_ADDR + 0x0) -#define PIO_CTRL_TYPE_MASK GENMASK(3, 0) -#define PIO_CTRL_ADDR_WIN_DISABLE BIT(24) -#define PIO_STAT (PIO_BASE_ADDR + 0x4) -#define PIO_COMPLETION_STATUS_SHIFT 7 -#define PIO_COMPLETION_STATUS_MASK GENMASK(9, 7) -#define PIO_COMPLETION_STATUS_OK 0 -#define PIO_COMPLETION_STATUS_UR 1 -#define PIO_COMPLETION_STATUS_CRS 2 -#define PIO_COMPLETION_STATUS_CA 4 -#define PIO_NON_POSTED_REQ BIT(0) -#define PIO_ADDR_LS (PIO_BASE_ADDR + 0x8) -#define PIO_ADDR_MS (PIO_BASE_ADDR + 0xc) -#define PIO_WR_DATA (PIO_BASE_ADDR + 0x10) -#define PIO_WR_DATA_STRB (PIO_BASE_ADDR + 0x14) -#define PIO_RD_DATA (PIO_BASE_ADDR + 0x18) -#define PIO_START (PIO_BASE_ADDR + 0x1c) -#define PIO_ISR (PIO_BASE_ADDR + 0x20) -#define PIO_ISRM (PIO_BASE_ADDR + 0x24) - -/* Aardvark Control registers */ -#define CONTROL_BASE_ADDR 0x4800 -#define PCIE_CORE_CTRL0_REG (CONTROL_BASE_ADDR + 0x0) -#define PCIE_GEN_SEL_MSK 0x3 -#define PCIE_GEN_SEL_SHIFT 0x0 -#define SPEED_GEN_1 0 -#define SPEED_GEN_2 1 -#define SPEED_GEN_3 2 -#define IS_RC_MSK 1 -#define IS_RC_SHIFT 2 -#define LANE_CNT_MSK 0x18 -#define LANE_CNT_SHIFT 0x3 -#define LANE_COUNT_1 (0 << LANE_CNT_SHIFT) -#define LANE_COUNT_2 (1 << LANE_CNT_SHIFT) -#define LANE_COUNT_4 (2 << LANE_CNT_SHIFT) -#define LANE_COUNT_8 (3 << LANE_CNT_SHIFT) -#define LINK_TRAINING_EN BIT(6) -#define LEGACY_INTA BIT(28) -#define LEGACY_INTB BIT(29) -#define LEGACY_INTC BIT(30) -#define LEGACY_INTD BIT(31) -#define PCIE_CORE_CTRL1_REG (CONTROL_BASE_ADDR + 0x4) -#define HOT_RESET_GEN BIT(0) -#define PCIE_CORE_CTRL2_REG (CONTROL_BASE_ADDR + 0x8) -#define PCIE_CORE_CTRL2_RESERVED 0x7 -#define PCIE_CORE_CTRL2_TD_ENABLE BIT(4) -#define PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE BIT(5) -#define PCIE_CORE_CTRL2_OB_WIN_ENABLE BIT(6) -#define PCIE_CORE_CTRL2_MSI_ENABLE BIT(10) -#define PCIE_ISR0_REG (CONTROL_BASE_ADDR + 0x40) -#define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44) -#define PCIE_ISR0_MSI_INT_PENDING BIT(24) -#define PCIE_ISR0_INTX_ASSERT(val) BIT(16 + (val)) -#define PCIE_ISR0_INTX_DEASSERT(val) BIT(20 + (val)) -#define PCIE_ISR0_ALL_MASK GENMASK(26, 0) -#define PCIE_ISR1_REG (CONTROL_BASE_ADDR + 0x48) -#define PCIE_ISR1_MASK_REG (CONTROL_BASE_ADDR + 0x4C) -#define PCIE_ISR1_POWER_STATE_CHANGE BIT(4) -#define PCIE_ISR1_FLUSH BIT(5) -#define PCIE_ISR1_INTX_ASSERT(val) BIT(8 + (val)) -#define PCIE_ISR1_ALL_MASK GENMASK(11, 4) -#define PCIE_MSI_ADDR_LOW_REG (CONTROL_BASE_ADDR + 0x50) -#define PCIE_MSI_ADDR_HIGH_REG (CONTROL_BASE_ADDR + 0x54) -#define PCIE_MSI_STATUS_REG (CONTROL_BASE_ADDR + 0x58) -#define PCIE_MSI_MASK_REG (CONTROL_BASE_ADDR + 0x5C) -#define PCIE_MSI_PAYLOAD_REG (CONTROL_BASE_ADDR + 0x9C) - -/* PCIe window configuration */ -#define OB_WIN_BASE_ADDR 0x4c00 -#define OB_WIN_BLOCK_SIZE 0x20 -#define OB_WIN_REG_ADDR(win, offset) (OB_WIN_BASE_ADDR + \ - OB_WIN_BLOCK_SIZE * (win) + \ - (offset)) -#define OB_WIN_MATCH_LS(win) OB_WIN_REG_ADDR(win, 0x00) -#define OB_WIN_MATCH_MS(win) OB_WIN_REG_ADDR(win, 0x04) -#define OB_WIN_REMAP_LS(win) OB_WIN_REG_ADDR(win, 0x08) -#define OB_WIN_REMAP_MS(win) OB_WIN_REG_ADDR(win, 0x0c) -#define OB_WIN_MASK_LS(win) OB_WIN_REG_ADDR(win, 0x10) -#define OB_WIN_MASK_MS(win) OB_WIN_REG_ADDR(win, 0x14) -#define OB_WIN_ACTIONS(win) OB_WIN_REG_ADDR(win, 0x18) - -/* PCIe window types */ -#define OB_PCIE_MEM 0x0 -#define OB_PCIE_IO 0x4 - -/* LMI registers base address and register offsets */ -#define LMI_BASE_ADDR 0x6000 -#define CFG_REG (LMI_BASE_ADDR + 0x0) -#define LTSSM_SHIFT 24 -#define LTSSM_MASK 0x3f -#define LTSSM_L0 0x10 -#define RC_BAR_CONFIG 0x300 - -/* PCIe core controller registers */ -#define CTRL_CORE_BASE_ADDR 0x18000 -#define CTRL_CONFIG_REG (CTRL_CORE_BASE_ADDR + 0x0) -#define CTRL_MODE_SHIFT 0x0 -#define CTRL_MODE_MASK 0x1 -#define PCIE_CORE_MODE_DIRECT 0x0 -#define PCIE_CORE_MODE_COMMAND 0x1 - -/* PCIe Central Interrupts Registers */ -#define CENTRAL_INT_BASE_ADDR 0x1b000 -#define HOST_CTRL_INT_STATUS_REG (CENTRAL_INT_BASE_ADDR + 0x0) -#define HOST_CTRL_INT_MASK_REG (CENTRAL_INT_BASE_ADDR + 0x4) -#define PCIE_IRQ_CMDQ_INT BIT(0) -#define PCIE_IRQ_MSI_STATUS_INT BIT(1) -#define PCIE_IRQ_CMD_SENT_DONE BIT(3) -#define PCIE_IRQ_DMA_INT BIT(4) -#define PCIE_IRQ_IB_DXFERDONE BIT(5) -#define PCIE_IRQ_OB_DXFERDONE BIT(6) -#define PCIE_IRQ_OB_RXFERDONE BIT(7) -#define PCIE_IRQ_COMPQ_INT BIT(12) -#define PCIE_IRQ_DIR_RD_DDR_DET BIT(13) -#define PCIE_IRQ_DIR_WR_DDR_DET BIT(14) -#define PCIE_IRQ_CORE_INT BIT(16) -#define PCIE_IRQ_CORE_INT_PIO BIT(17) -#define PCIE_IRQ_DPMU_INT BIT(18) -#define PCIE_IRQ_PCIE_MIS_INT BIT(19) -#define PCIE_IRQ_MSI_INT1_DET BIT(20) -#define PCIE_IRQ_MSI_INT2_DET BIT(21) -#define PCIE_IRQ_RC_DBELL_DET BIT(22) -#define PCIE_IRQ_EP_STATUS BIT(23) -#define PCIE_IRQ_ALL_MASK 0xfff0fb -#define PCIE_IRQ_ENABLE_INTS_MASK PCIE_IRQ_CORE_INT - -/* Transaction types */ -#define PCIE_CONFIG_RD_TYPE0 0x8 -#define PCIE_CONFIG_RD_TYPE1 0x9 -#define PCIE_CONFIG_WR_TYPE0 0xa -#define PCIE_CONFIG_WR_TYPE1 0xb - -#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20) -#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15) -#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12) -#define PCIE_CONF_REG(reg) ((reg) & 0xffc) -#define PCIE_CONF_ADDR(bus, devfn, where) \ - (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ - PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where)) - -#define PIO_TIMEOUT_MS 1 - -#define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_USLEEP_MIN 90000 -#define LINK_WAIT_USLEEP_MAX 100000 - -#define MSI_IRQ_NUM 32 - -struct advk_pcie { - struct platform_device *pdev; - void __iomem *base; - struct list_head resources; - struct irq_domain *irq_domain; - struct irq_chip irq_chip; - struct irq_domain *msi_domain; - struct irq_domain *msi_inner_domain; - struct irq_chip msi_bottom_irq_chip; - struct irq_chip msi_irq_chip; - struct msi_domain_info msi_domain_info; - DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); - struct mutex msi_used_lock; - u16 msi_msg; - int root_bus_nr; -}; - -static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg) -{ - writel(val, pcie->base + reg); -} - -static inline u32 advk_readl(struct advk_pcie *pcie, u64 reg) -{ - return readl(pcie->base + reg); -} - -static int advk_pcie_link_up(struct advk_pcie *pcie) -{ - u32 val, ltssm_state; - - val = advk_readl(pcie, CFG_REG); - ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; - return ltssm_state >= LTSSM_L0; -} - -static int advk_pcie_wait_for_link(struct advk_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - int retries; - - /* check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (advk_pcie_link_up(pcie)) { - dev_info(dev, "link up\n"); - return 0; - } - - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); - } - - dev_err(dev, "link never came up\n"); - return -ETIMEDOUT; -} - -/* - * Set PCIe address window register which could be used for memory - * mapping. - */ -static void advk_pcie_set_ob_win(struct advk_pcie *pcie, - u32 win_num, u32 match_ms, - u32 match_ls, u32 mask_ms, - u32 mask_ls, u32 remap_ms, - u32 remap_ls, u32 action) -{ - advk_writel(pcie, match_ls, OB_WIN_MATCH_LS(win_num)); - advk_writel(pcie, match_ms, OB_WIN_MATCH_MS(win_num)); - advk_writel(pcie, mask_ms, OB_WIN_MASK_MS(win_num)); - advk_writel(pcie, mask_ls, OB_WIN_MASK_LS(win_num)); - advk_writel(pcie, remap_ms, OB_WIN_REMAP_MS(win_num)); - advk_writel(pcie, remap_ls, OB_WIN_REMAP_LS(win_num)); - advk_writel(pcie, action, OB_WIN_ACTIONS(win_num)); - advk_writel(pcie, match_ls | BIT(0), OB_WIN_MATCH_LS(win_num)); -} - -static void advk_pcie_setup_hw(struct advk_pcie *pcie) -{ - u32 reg; - int i; - - /* Point PCIe unit MBUS decode windows to DRAM space */ - for (i = 0; i < 8; i++) - advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0); - - /* Set to Direct mode */ - reg = advk_readl(pcie, CTRL_CONFIG_REG); - reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT); - reg |= ((PCIE_CORE_MODE_DIRECT & CTRL_MODE_MASK) << CTRL_MODE_SHIFT); - advk_writel(pcie, reg, CTRL_CONFIG_REG); - - /* Set PCI global control register to RC mode */ - reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); - reg |= (IS_RC_MSK << IS_RC_SHIFT); - advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); - - /* Set Advanced Error Capabilities and Control PF0 register */ - reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX | - PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN | - PCIE_CORE_ERR_CAPCTL_ECRC_CHCK | - PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV; - advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG); - - /* Set PCIe Device Control and Status 1 PF0 register */ - reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE | - (7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) | - PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE | - (PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ << - PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT); - advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG); - - /* Program PCIe Control 2 to disable strict ordering */ - reg = PCIE_CORE_CTRL2_RESERVED | - PCIE_CORE_CTRL2_TD_ENABLE; - advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); - - /* Set GEN2 */ - reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); - reg &= ~PCIE_GEN_SEL_MSK; - reg |= SPEED_GEN_2; - advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); - - /* Set lane X1 */ - reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); - reg &= ~LANE_CNT_MSK; - reg |= LANE_COUNT_1; - advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); - - /* Enable link training */ - reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); - reg |= LINK_TRAINING_EN; - advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); - - /* Enable MSI */ - reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); - reg |= PCIE_CORE_CTRL2_MSI_ENABLE; - advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); - - /* Clear all interrupts */ - advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG); - advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG); - advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG); - - /* Disable All ISR0/1 Sources */ - reg = PCIE_ISR0_ALL_MASK; - reg &= ~PCIE_ISR0_MSI_INT_PENDING; - advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); - - advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG); - - /* Unmask all MSI's */ - advk_writel(pcie, 0, PCIE_MSI_MASK_REG); - - /* Enable summary interrupt for GIC SPI source */ - reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK); - advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG); - - reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); - reg |= PCIE_CORE_CTRL2_OB_WIN_ENABLE; - advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); - - /* Bypass the address window mapping for PIO */ - reg = advk_readl(pcie, PIO_CTRL); - reg |= PIO_CTRL_ADDR_WIN_DISABLE; - advk_writel(pcie, reg, PIO_CTRL); - - /* Start link training */ - reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); - reg |= PCIE_CORE_LINK_TRAINING; - advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); - - advk_pcie_wait_for_link(pcie); - - reg = PCIE_CORE_LINK_L0S_ENTRY | - (1 << PCIE_CORE_LINK_WIDTH_SHIFT); - advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); - - reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG); - reg |= PCIE_CORE_CMD_MEM_ACCESS_EN | - PCIE_CORE_CMD_IO_ACCESS_EN | - PCIE_CORE_CMD_MEM_IO_REQ_EN; - advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); -} - -static void advk_pcie_check_pio_status(struct advk_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - u32 reg; - unsigned int status; - char *strcomp_status, *str_posted; - - reg = advk_readl(pcie, PIO_STAT); - status = (reg & PIO_COMPLETION_STATUS_MASK) >> - PIO_COMPLETION_STATUS_SHIFT; - - if (!status) - return; - - switch (status) { - case PIO_COMPLETION_STATUS_UR: - strcomp_status = "UR"; - break; - case PIO_COMPLETION_STATUS_CRS: - strcomp_status = "CRS"; - break; - case PIO_COMPLETION_STATUS_CA: - strcomp_status = "CA"; - break; - default: - strcomp_status = "Unknown"; - break; - } - - if (reg & PIO_NON_POSTED_REQ) - str_posted = "Non-posted"; - else - str_posted = "Posted"; - - dev_err(dev, "%s PIO Response Status: %s, %#x @ %#x\n", - str_posted, strcomp_status, reg, advk_readl(pcie, PIO_ADDR_LS)); -} - -static int advk_pcie_wait_pio(struct advk_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS); - - while (time_before(jiffies, timeout)) { - u32 start, isr; - - start = advk_readl(pcie, PIO_START); - isr = advk_readl(pcie, PIO_ISR); - if (!start && isr) - return 0; - } - - dev_err(dev, "config read/write timed out\n"); - return -ETIMEDOUT; -} - -static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 *val) -{ - struct advk_pcie *pcie = bus->sysdata; - u32 reg; - int ret; - - if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - /* Start PIO */ - advk_writel(pcie, 0, PIO_START); - advk_writel(pcie, 1, PIO_ISR); - - /* Program the control register */ - reg = advk_readl(pcie, PIO_CTRL); - reg &= ~PIO_CTRL_TYPE_MASK; - if (bus->number == pcie->root_bus_nr) - reg |= PCIE_CONFIG_RD_TYPE0; - else - reg |= PCIE_CONFIG_RD_TYPE1; - advk_writel(pcie, reg, PIO_CTRL); - - /* Program the address registers */ - reg = PCIE_CONF_ADDR(bus->number, devfn, where); - advk_writel(pcie, reg, PIO_ADDR_LS); - advk_writel(pcie, 0, PIO_ADDR_MS); - - /* Program the data strobe */ - advk_writel(pcie, 0xf, PIO_WR_DATA_STRB); - - /* Start the transfer */ - advk_writel(pcie, 1, PIO_START); - - ret = advk_pcie_wait_pio(pcie); - if (ret < 0) - return PCIBIOS_SET_FAILED; - - advk_pcie_check_pio_status(pcie); - - /* Get the read result */ - *val = advk_readl(pcie, PIO_RD_DATA); - if (size == 1) - *val = (*val >> (8 * (where & 3))) & 0xff; - else if (size == 2) - *val = (*val >> (8 * (where & 3))) & 0xffff; - - return PCIBIOS_SUCCESSFUL; -} - -static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) -{ - struct advk_pcie *pcie = bus->sysdata; - u32 reg; - u32 data_strobe = 0x0; - int offset; - int ret; - - if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (where % size) - return PCIBIOS_SET_FAILED; - - /* Start PIO */ - advk_writel(pcie, 0, PIO_START); - advk_writel(pcie, 1, PIO_ISR); - - /* Program the control register */ - reg = advk_readl(pcie, PIO_CTRL); - reg &= ~PIO_CTRL_TYPE_MASK; - if (bus->number == pcie->root_bus_nr) - reg |= PCIE_CONFIG_WR_TYPE0; - else - reg |= PCIE_CONFIG_WR_TYPE1; - advk_writel(pcie, reg, PIO_CTRL); - - /* Program the address registers */ - reg = PCIE_CONF_ADDR(bus->number, devfn, where); - advk_writel(pcie, reg, PIO_ADDR_LS); - advk_writel(pcie, 0, PIO_ADDR_MS); - - /* Calculate the write strobe */ - offset = where & 0x3; - reg = val << (8 * offset); - data_strobe = GENMASK(size - 1, 0) << offset; - - /* Program the data register */ - advk_writel(pcie, reg, PIO_WR_DATA); - - /* Program the data strobe */ - advk_writel(pcie, data_strobe, PIO_WR_DATA_STRB); - - /* Start the transfer */ - advk_writel(pcie, 1, PIO_START); - - ret = advk_pcie_wait_pio(pcie); - if (ret < 0) - return PCIBIOS_SET_FAILED; - - advk_pcie_check_pio_status(pcie); - - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops advk_pcie_ops = { - .read = advk_pcie_rd_conf, - .write = advk_pcie_wr_conf, -}; - -static void advk_msi_irq_compose_msi_msg(struct irq_data *data, - struct msi_msg *msg) -{ - struct advk_pcie *pcie = irq_data_get_irq_chip_data(data); - phys_addr_t msi_msg = virt_to_phys(&pcie->msi_msg); - - msg->address_lo = lower_32_bits(msi_msg); - msg->address_hi = upper_32_bits(msi_msg); - msg->data = data->irq; -} - -static int advk_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) -{ - return -EINVAL; -} - -static int advk_msi_irq_domain_alloc(struct irq_domain *domain, - unsigned int virq, - unsigned int nr_irqs, void *args) -{ - struct advk_pcie *pcie = domain->host_data; - int hwirq, i; - - mutex_lock(&pcie->msi_used_lock); - hwirq = bitmap_find_next_zero_area(pcie->msi_used, MSI_IRQ_NUM, - 0, nr_irqs, 0); - if (hwirq >= MSI_IRQ_NUM) { - mutex_unlock(&pcie->msi_used_lock); - return -ENOSPC; - } - - bitmap_set(pcie->msi_used, hwirq, nr_irqs); - mutex_unlock(&pcie->msi_used_lock); - - for (i = 0; i < nr_irqs; i++) - irq_domain_set_info(domain, virq + i, hwirq + i, - &pcie->msi_bottom_irq_chip, - domain->host_data, handle_simple_irq, - NULL, NULL); - - return hwirq; -} - -static void advk_msi_irq_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) -{ - struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct advk_pcie *pcie = domain->host_data; - - mutex_lock(&pcie->msi_used_lock); - bitmap_clear(pcie->msi_used, d->hwirq, nr_irqs); - mutex_unlock(&pcie->msi_used_lock); -} - -static const struct irq_domain_ops advk_msi_domain_ops = { - .alloc = advk_msi_irq_domain_alloc, - .free = advk_msi_irq_domain_free, -}; - -static void advk_pcie_irq_mask(struct irq_data *d) -{ - struct advk_pcie *pcie = d->domain->host_data; - irq_hw_number_t hwirq = irqd_to_hwirq(d); - u32 mask; - - mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); - mask |= PCIE_ISR1_INTX_ASSERT(hwirq); - advk_writel(pcie, mask, PCIE_ISR1_MASK_REG); -} - -static void advk_pcie_irq_unmask(struct irq_data *d) -{ - struct advk_pcie *pcie = d->domain->host_data; - irq_hw_number_t hwirq = irqd_to_hwirq(d); - u32 mask; - - mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); - mask &= ~PCIE_ISR1_INTX_ASSERT(hwirq); - advk_writel(pcie, mask, PCIE_ISR1_MASK_REG); -} - -static int advk_pcie_irq_map(struct irq_domain *h, - unsigned int virq, irq_hw_number_t hwirq) -{ - struct advk_pcie *pcie = h->host_data; - - advk_pcie_irq_mask(irq_get_irq_data(virq)); - irq_set_status_flags(virq, IRQ_LEVEL); - irq_set_chip_and_handler(virq, &pcie->irq_chip, - handle_level_irq); - irq_set_chip_data(virq, pcie); - - return 0; -} - -static const struct irq_domain_ops advk_pcie_irq_domain_ops = { - .map = advk_pcie_irq_map, - .xlate = irq_domain_xlate_onecell, -}; - -static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - struct device_node *node = dev->of_node; - struct irq_chip *bottom_ic, *msi_ic; - struct msi_domain_info *msi_di; - phys_addr_t msi_msg_phys; - - mutex_init(&pcie->msi_used_lock); - - bottom_ic = &pcie->msi_bottom_irq_chip; - - bottom_ic->name = "MSI"; - bottom_ic->irq_compose_msi_msg = advk_msi_irq_compose_msi_msg; - bottom_ic->irq_set_affinity = advk_msi_set_affinity; - - msi_ic = &pcie->msi_irq_chip; - msi_ic->name = "advk-MSI"; - - msi_di = &pcie->msi_domain_info; - msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI; - msi_di->chip = msi_ic; - - msi_msg_phys = virt_to_phys(&pcie->msi_msg); - - advk_writel(pcie, lower_32_bits(msi_msg_phys), - PCIE_MSI_ADDR_LOW_REG); - advk_writel(pcie, upper_32_bits(msi_msg_phys), - PCIE_MSI_ADDR_HIGH_REG); - - pcie->msi_inner_domain = - irq_domain_add_linear(NULL, MSI_IRQ_NUM, - &advk_msi_domain_ops, pcie); - if (!pcie->msi_inner_domain) - return -ENOMEM; - - pcie->msi_domain = - pci_msi_create_irq_domain(of_node_to_fwnode(node), - msi_di, pcie->msi_inner_domain); - if (!pcie->msi_domain) { - irq_domain_remove(pcie->msi_inner_domain); - return -ENOMEM; - } - - return 0; -} - -static void advk_pcie_remove_msi_irq_domain(struct advk_pcie *pcie) -{ - irq_domain_remove(pcie->msi_domain); - irq_domain_remove(pcie->msi_inner_domain); -} - -static int advk_pcie_init_irq_domain(struct advk_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - struct device_node *node = dev->of_node; - struct device_node *pcie_intc_node; - struct irq_chip *irq_chip; - - pcie_intc_node = of_get_next_child(node, NULL); - if (!pcie_intc_node) { - dev_err(dev, "No PCIe Intc node found\n"); - return -ENODEV; - } - - irq_chip = &pcie->irq_chip; - - irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", - dev_name(dev)); - if (!irq_chip->name) { - of_node_put(pcie_intc_node); - return -ENOMEM; - } - - irq_chip->irq_mask = advk_pcie_irq_mask; - irq_chip->irq_mask_ack = advk_pcie_irq_mask; - irq_chip->irq_unmask = advk_pcie_irq_unmask; - - pcie->irq_domain = - irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, - &advk_pcie_irq_domain_ops, pcie); - if (!pcie->irq_domain) { - dev_err(dev, "Failed to get a INTx IRQ domain\n"); - of_node_put(pcie_intc_node); - return -ENOMEM; - } - - return 0; -} - -static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie) -{ - irq_domain_remove(pcie->irq_domain); -} - -static void advk_pcie_handle_msi(struct advk_pcie *pcie) -{ - u32 msi_val, msi_mask, msi_status, msi_idx; - u16 msi_data; - - msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG); - msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG); - msi_status = msi_val & ~msi_mask; - - for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) { - if (!(BIT(msi_idx) & msi_status)) - continue; - - advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG); - msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & 0xFF; - generic_handle_irq(msi_data); - } - - advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING, - PCIE_ISR0_REG); -} - -static void advk_pcie_handle_int(struct advk_pcie *pcie) -{ - u32 isr0_val, isr0_mask, isr0_status; - u32 isr1_val, isr1_mask, isr1_status; - int i, virq; - - isr0_val = advk_readl(pcie, PCIE_ISR0_REG); - isr0_mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); - isr0_status = isr0_val & ((~isr0_mask) & PCIE_ISR0_ALL_MASK); - - isr1_val = advk_readl(pcie, PCIE_ISR1_REG); - isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); - isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK); - - if (!isr0_status && !isr1_status) { - advk_writel(pcie, isr0_val, PCIE_ISR0_REG); - advk_writel(pcie, isr1_val, PCIE_ISR1_REG); - return; - } - - /* Process MSI interrupts */ - if (isr0_status & PCIE_ISR0_MSI_INT_PENDING) - advk_pcie_handle_msi(pcie); - - /* Process legacy interrupts */ - for (i = 0; i < PCI_NUM_INTX; i++) { - if (!(isr1_status & PCIE_ISR1_INTX_ASSERT(i))) - continue; - - advk_writel(pcie, PCIE_ISR1_INTX_ASSERT(i), - PCIE_ISR1_REG); - - virq = irq_find_mapping(pcie->irq_domain, i); - generic_handle_irq(virq); - } -} - -static irqreturn_t advk_pcie_irq_handler(int irq, void *arg) -{ - struct advk_pcie *pcie = arg; - u32 status; - - status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG); - if (!(status & PCIE_IRQ_CORE_INT)) - return IRQ_NONE; - - advk_pcie_handle_int(pcie); - - /* Clear interrupt */ - advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG); - - return IRQ_HANDLED; -} - -static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie) -{ - int err, res_valid = 0; - struct device *dev = &pcie->pdev->dev; - struct resource_entry *win, *tmp; - resource_size_t iobase; - - INIT_LIST_HEAD(&pcie->resources); - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &pcie->resources, &iobase); - if (err) - return err; - - err = devm_request_pci_bus_resources(dev, &pcie->resources); - if (err) - goto out_release_res; - - resource_list_for_each_entry_safe(win, tmp, &pcie->resources) { - struct resource *res = win->res; - - switch (resource_type(res)) { - case IORESOURCE_IO: - advk_pcie_set_ob_win(pcie, 1, - upper_32_bits(res->start), - lower_32_bits(res->start), - 0, 0xF8000000, 0, - lower_32_bits(res->start), - OB_PCIE_IO); - err = pci_remap_iospace(res, iobase); - if (err) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - err, res); - resource_list_destroy_entry(win); - } - break; - case IORESOURCE_MEM: - advk_pcie_set_ob_win(pcie, 0, - upper_32_bits(res->start), - lower_32_bits(res->start), - 0x0, 0xF8000000, 0, - lower_32_bits(res->start), - (2 << 20) | OB_PCIE_MEM); - res_valid |= !(res->flags & IORESOURCE_PREFETCH); - break; - case IORESOURCE_BUS: - pcie->root_bus_nr = res->start; - break; - } - } - - if (!res_valid) { - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; - goto out_release_res; - } - - return 0; - -out_release_res: - pci_free_resource_list(&pcie->resources); - return err; -} - -static int advk_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct advk_pcie *pcie; - struct resource *res; - struct pci_bus *bus, *child; - struct pci_host_bridge *bridge; - int ret, irq; - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie)); - if (!bridge) - return -ENOMEM; - - pcie = pci_host_bridge_priv(bridge); - pcie->pdev = pdev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pcie->base = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->base)) - return PTR_ERR(pcie->base); - - irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(dev, irq, advk_pcie_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie", - pcie); - if (ret) { - dev_err(dev, "Failed to register interrupt\n"); - return ret; - } - - ret = advk_pcie_parse_request_of_pci_ranges(pcie); - if (ret) { - dev_err(dev, "Failed to parse resources\n"); - return ret; - } - - advk_pcie_setup_hw(pcie); - - ret = advk_pcie_init_irq_domain(pcie); - if (ret) { - dev_err(dev, "Failed to initialize irq\n"); - return ret; - } - - ret = advk_pcie_init_msi_irq_domain(pcie); - if (ret) { - dev_err(dev, "Failed to initialize irq\n"); - advk_pcie_remove_irq_domain(pcie); - return ret; - } - - list_splice_init(&pcie->resources, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = pcie; - bridge->busnr = 0; - bridge->ops = &advk_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) { - advk_pcie_remove_msi_irq_domain(pcie); - advk_pcie_remove_irq_domain(pcie); - return ret; - } - - bus = bridge->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 const struct of_device_id advk_pcie_of_match_table[] = { - { .compatible = "marvell,armada-3700-pcie", }, - {}, -}; - -static struct platform_driver advk_pcie_driver = { - .driver = { - .name = "advk-pcie", - .of_match_table = advk_pcie_of_match_table, - /* Driver unloading/unbinding currently not supported */ - .suppress_bind_attrs = true, - }, - .probe = advk_pcie_probe, -}; -builtin_platform_driver(advk_pcie_driver); diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c deleted file mode 100644 index a1ebe9ed441f..000000000000 --- a/drivers/pci/host/pci-ftpci100.c +++ /dev/null @@ -1,619 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Support for Faraday Technology FTPC100 PCI Controller - * - * Copyright (C) 2017 Linus Walleij - * - * Based on the out-of-tree OpenWRT patch for Cortina Gemini: - * Copyright (C) 2009 Janos Laube - * Copyright (C) 2009 Paulius Zaleckas - * Based on SL2312 PCI controller code - * Storlink (C) 2003 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -/* - * Special configuration registers directly in the first few words - * in I/O space. - */ -#define PCI_IOSIZE 0x00 -#define PCI_PROT 0x04 /* AHB protection */ -#define PCI_CTRL 0x08 /* PCI control signal */ -#define PCI_SOFTRST 0x10 /* Soft reset counter and response error enable */ -#define PCI_CONFIG 0x28 /* PCI configuration command register */ -#define PCI_DATA 0x2C - -#define FARADAY_PCI_STATUS_CMD 0x04 /* Status and command */ -#define FARADAY_PCI_PMC 0x40 /* Power management control */ -#define FARADAY_PCI_PMCSR 0x44 /* Power management status */ -#define FARADAY_PCI_CTRL1 0x48 /* Control register 1 */ -#define FARADAY_PCI_CTRL2 0x4C /* Control register 2 */ -#define FARADAY_PCI_MEM1_BASE_SIZE 0x50 /* Memory base and size #1 */ -#define FARADAY_PCI_MEM2_BASE_SIZE 0x54 /* Memory base and size #2 */ -#define FARADAY_PCI_MEM3_BASE_SIZE 0x58 /* Memory base and size #3 */ - -#define PCI_STATUS_66MHZ_CAPABLE BIT(21) - -/* Bits 31..28 gives INTD..INTA status */ -#define PCI_CTRL2_INTSTS_SHIFT 28 -#define PCI_CTRL2_INTMASK_CMDERR BIT(27) -#define PCI_CTRL2_INTMASK_PARERR BIT(26) -/* Bits 25..22 masks INTD..INTA */ -#define PCI_CTRL2_INTMASK_SHIFT 22 -#define PCI_CTRL2_INTMASK_MABRT_RX BIT(21) -#define PCI_CTRL2_INTMASK_TABRT_RX BIT(20) -#define PCI_CTRL2_INTMASK_TABRT_TX BIT(19) -#define PCI_CTRL2_INTMASK_RETRY4 BIT(18) -#define PCI_CTRL2_INTMASK_SERR_RX BIT(17) -#define PCI_CTRL2_INTMASK_PERR_RX BIT(16) -/* Bit 15 reserved */ -#define PCI_CTRL2_MSTPRI_REQ6 BIT(14) -#define PCI_CTRL2_MSTPRI_REQ5 BIT(13) -#define PCI_CTRL2_MSTPRI_REQ4 BIT(12) -#define PCI_CTRL2_MSTPRI_REQ3 BIT(11) -#define PCI_CTRL2_MSTPRI_REQ2 BIT(10) -#define PCI_CTRL2_MSTPRI_REQ1 BIT(9) -#define PCI_CTRL2_MSTPRI_REQ0 BIT(8) -/* Bits 7..4 reserved */ -/* Bits 3..0 TRDYW */ - -/* - * Memory configs: - * Bit 31..20 defines the PCI side memory base - * Bit 19..16 (4 bits) defines the size per below - */ -#define FARADAY_PCI_MEMBASE_MASK 0xfff00000 -#define FARADAY_PCI_MEMSIZE_1MB 0x0 -#define FARADAY_PCI_MEMSIZE_2MB 0x1 -#define FARADAY_PCI_MEMSIZE_4MB 0x2 -#define FARADAY_PCI_MEMSIZE_8MB 0x3 -#define FARADAY_PCI_MEMSIZE_16MB 0x4 -#define FARADAY_PCI_MEMSIZE_32MB 0x5 -#define FARADAY_PCI_MEMSIZE_64MB 0x6 -#define FARADAY_PCI_MEMSIZE_128MB 0x7 -#define FARADAY_PCI_MEMSIZE_256MB 0x8 -#define FARADAY_PCI_MEMSIZE_512MB 0x9 -#define FARADAY_PCI_MEMSIZE_1GB 0xa -#define FARADAY_PCI_MEMSIZE_2GB 0xb -#define FARADAY_PCI_MEMSIZE_SHIFT 16 - -/* - * The DMA base is set to 0x0 for all memory segments, it reflects the - * fact that the memory of the host system starts at 0x0. - */ -#define FARADAY_PCI_DMA_MEM1_BASE 0x00000000 -#define FARADAY_PCI_DMA_MEM2_BASE 0x00000000 -#define FARADAY_PCI_DMA_MEM3_BASE 0x00000000 - -/* Defines for PCI configuration command register */ -#define PCI_CONF_ENABLE BIT(31) -#define PCI_CONF_WHERE(r) ((r) & 0xFC) -#define PCI_CONF_BUS(b) (((b) & 0xFF) << 16) -#define PCI_CONF_DEVICE(d) (((d) & 0x1F) << 11) -#define PCI_CONF_FUNCTION(f) (((f) & 0x07) << 8) - -/** - * struct faraday_pci_variant - encodes IP block differences - * @cascaded_irq: this host has cascaded IRQs from an interrupt controller - * embedded in the host bridge. - */ -struct faraday_pci_variant { - bool cascaded_irq; -}; - -struct faraday_pci { - struct device *dev; - void __iomem *base; - struct irq_domain *irqdomain; - struct pci_bus *bus; - struct clk *bus_clk; -}; - -static int faraday_res_to_memcfg(resource_size_t mem_base, - resource_size_t mem_size, u32 *val) -{ - u32 outval; - - switch (mem_size) { - case SZ_1M: - outval = FARADAY_PCI_MEMSIZE_1MB; - break; - case SZ_2M: - outval = FARADAY_PCI_MEMSIZE_2MB; - break; - case SZ_4M: - outval = FARADAY_PCI_MEMSIZE_4MB; - break; - case SZ_8M: - outval = FARADAY_PCI_MEMSIZE_8MB; - break; - case SZ_16M: - outval = FARADAY_PCI_MEMSIZE_16MB; - break; - case SZ_32M: - outval = FARADAY_PCI_MEMSIZE_32MB; - break; - case SZ_64M: - outval = FARADAY_PCI_MEMSIZE_64MB; - break; - case SZ_128M: - outval = FARADAY_PCI_MEMSIZE_128MB; - break; - case SZ_256M: - outval = FARADAY_PCI_MEMSIZE_256MB; - break; - case SZ_512M: - outval = FARADAY_PCI_MEMSIZE_512MB; - break; - case SZ_1G: - outval = FARADAY_PCI_MEMSIZE_1GB; - break; - case SZ_2G: - outval = FARADAY_PCI_MEMSIZE_2GB; - break; - default: - return -EINVAL; - } - outval <<= FARADAY_PCI_MEMSIZE_SHIFT; - - /* This is probably not good */ - if (mem_base & ~(FARADAY_PCI_MEMBASE_MASK)) - pr_warn("truncated PCI memory base\n"); - /* Translate to bridge side address space */ - outval |= (mem_base & FARADAY_PCI_MEMBASE_MASK); - pr_debug("Translated pci base @%pap, size %pap to config %08x\n", - &mem_base, &mem_size, outval); - - *val = outval; - return 0; -} - -static int faraday_raw_pci_read_config(struct faraday_pci *p, int bus_number, - unsigned int fn, int config, int size, - u32 *value) -{ - writel(PCI_CONF_BUS(bus_number) | - PCI_CONF_DEVICE(PCI_SLOT(fn)) | - PCI_CONF_FUNCTION(PCI_FUNC(fn)) | - PCI_CONF_WHERE(config) | - PCI_CONF_ENABLE, - p->base + PCI_CONFIG); - - *value = readl(p->base + PCI_DATA); - - if (size == 1) - *value = (*value >> (8 * (config & 3))) & 0xFF; - else if (size == 2) - *value = (*value >> (8 * (config & 3))) & 0xFFFF; - - return PCIBIOS_SUCCESSFUL; -} - -static int faraday_pci_read_config(struct pci_bus *bus, unsigned int fn, - int config, int size, u32 *value) -{ - struct faraday_pci *p = bus->sysdata; - - dev_dbg(&bus->dev, - "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", - PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); - - return faraday_raw_pci_read_config(p, bus->number, fn, config, size, value); -} - -static int faraday_raw_pci_write_config(struct faraday_pci *p, int bus_number, - unsigned int fn, int config, int size, - u32 value) -{ - int ret = PCIBIOS_SUCCESSFUL; - - writel(PCI_CONF_BUS(bus_number) | - PCI_CONF_DEVICE(PCI_SLOT(fn)) | - PCI_CONF_FUNCTION(PCI_FUNC(fn)) | - PCI_CONF_WHERE(config) | - PCI_CONF_ENABLE, - p->base + PCI_CONFIG); - - switch (size) { - case 4: - writel(value, p->base + PCI_DATA); - break; - case 2: - writew(value, p->base + PCI_DATA + (config & 3)); - break; - case 1: - writeb(value, p->base + PCI_DATA + (config & 3)); - break; - default: - ret = PCIBIOS_BAD_REGISTER_NUMBER; - } - - return ret; -} - -static int faraday_pci_write_config(struct pci_bus *bus, unsigned int fn, - int config, int size, u32 value) -{ - struct faraday_pci *p = bus->sysdata; - - dev_dbg(&bus->dev, - "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", - PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); - - return faraday_raw_pci_write_config(p, bus->number, fn, config, size, - value); -} - -static struct pci_ops faraday_pci_ops = { - .read = faraday_pci_read_config, - .write = faraday_pci_write_config, -}; - -static void faraday_pci_ack_irq(struct irq_data *d) -{ - struct faraday_pci *p = irq_data_get_irq_chip_data(d); - unsigned int reg; - - faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); - reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); - reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTSTS_SHIFT); - faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); -} - -static void faraday_pci_mask_irq(struct irq_data *d) -{ - struct faraday_pci *p = irq_data_get_irq_chip_data(d); - unsigned int reg; - - faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); - reg &= ~((0xF << PCI_CTRL2_INTSTS_SHIFT) - | BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT)); - faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); -} - -static void faraday_pci_unmask_irq(struct irq_data *d) -{ - struct faraday_pci *p = irq_data_get_irq_chip_data(d); - unsigned int reg; - - faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); - reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); - reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT); - faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); -} - -static void faraday_pci_irq_handler(struct irq_desc *desc) -{ - struct faraday_pci *p = irq_desc_get_handler_data(desc); - struct irq_chip *irqchip = irq_desc_get_chip(desc); - unsigned int irq_stat, reg, i; - - faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); - irq_stat = reg >> PCI_CTRL2_INTSTS_SHIFT; - - chained_irq_enter(irqchip, desc); - - for (i = 0; i < 4; i++) { - if ((irq_stat & BIT(i)) == 0) - continue; - generic_handle_irq(irq_find_mapping(p->irqdomain, i)); - } - - chained_irq_exit(irqchip, desc); -} - -static struct irq_chip faraday_pci_irq_chip = { - .name = "PCI", - .irq_ack = faraday_pci_ack_irq, - .irq_mask = faraday_pci_mask_irq, - .irq_unmask = faraday_pci_unmask_irq, -}; - -static int faraday_pci_irq_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &faraday_pci_irq_chip, handle_level_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -static const struct irq_domain_ops faraday_pci_irqdomain_ops = { - .map = faraday_pci_irq_map, -}; - -static int faraday_pci_setup_cascaded_irq(struct faraday_pci *p) -{ - struct device_node *intc = of_get_next_child(p->dev->of_node, NULL); - int irq; - int i; - - if (!intc) { - dev_err(p->dev, "missing child interrupt-controller node\n"); - return -EINVAL; - } - - /* All PCI IRQs cascade off this one */ - irq = of_irq_get(intc, 0); - if (irq <= 0) { - dev_err(p->dev, "failed to get parent IRQ\n"); - return irq ?: -EINVAL; - } - - p->irqdomain = irq_domain_add_linear(intc, PCI_NUM_INTX, - &faraday_pci_irqdomain_ops, p); - if (!p->irqdomain) { - dev_err(p->dev, "failed to create Gemini PCI IRQ domain\n"); - return -EINVAL; - } - - irq_set_chained_handler_and_data(irq, faraday_pci_irq_handler, p); - - for (i = 0; i < 4; i++) - irq_create_mapping(p->irqdomain, i); - - return 0; -} - -static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, - struct device_node *np) -{ - struct of_pci_range range; - struct of_pci_range_parser parser; - struct device *dev = p->dev; - u32 confreg[3] = { - FARADAY_PCI_MEM1_BASE_SIZE, - FARADAY_PCI_MEM2_BASE_SIZE, - FARADAY_PCI_MEM3_BASE_SIZE, - }; - int i = 0; - u32 val; - - if (of_pci_dma_range_parser_init(&parser, np)) { - dev_err(dev, "missing dma-ranges property\n"); - return -EINVAL; - } - - /* - * Get the dma-ranges from the device tree - */ - for_each_of_pci_range(&parser, &range) { - u64 end = range.pci_addr + range.size - 1; - int ret; - - ret = faraday_res_to_memcfg(range.pci_addr, range.size, &val); - if (ret) { - dev_err(dev, - "DMA range %d: illegal MEM resource size\n", i); - return -EINVAL; - } - - dev_info(dev, "DMA MEM%d BASE: 0x%016llx -> 0x%016llx config %08x\n", - i + 1, range.pci_addr, end, val); - if (i <= 2) { - faraday_raw_pci_write_config(p, 0, 0, confreg[i], - 4, val); - } else { - dev_err(dev, "ignore extraneous dma-range %d\n", i); - break; - } - - i++; - } - - return 0; -} - -static int faraday_pci_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct faraday_pci_variant *variant = - of_device_get_match_data(dev); - struct resource *regs; - resource_size_t io_base; - struct resource_entry *win; - struct faraday_pci *p; - struct resource *mem; - struct resource *io; - struct pci_host_bridge *host; - struct clk *clk; - unsigned char max_bus_speed = PCI_SPEED_33MHz; - unsigned char cur_bus_speed = PCI_SPEED_33MHz; - int ret; - u32 val; - LIST_HEAD(res); - - host = devm_pci_alloc_host_bridge(dev, sizeof(*p)); - if (!host) - return -ENOMEM; - - host->dev.parent = dev; - host->ops = &faraday_pci_ops; - host->busnr = 0; - host->msi = NULL; - host->map_irq = of_irq_parse_and_map_pci; - host->swizzle_irq = pci_common_swizzle; - p = pci_host_bridge_priv(host); - host->sysdata = p; - p->dev = dev; - - /* Retrieve and enable optional clocks */ - clk = devm_clk_get(dev, "PCLK"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "could not prepare PCLK\n"); - return ret; - } - p->bus_clk = devm_clk_get(dev, "PCICLK"); - if (IS_ERR(p->bus_clk)) - return PTR_ERR(p->bus_clk); - ret = clk_prepare_enable(p->bus_clk); - if (ret) { - dev_err(dev, "could not prepare PCICLK\n"); - return ret; - } - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - p->base = devm_ioremap_resource(dev, regs); - if (IS_ERR(p->base)) - return PTR_ERR(p->base); - - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &res, &io_base); - if (ret) - return ret; - - ret = devm_request_pci_bus_resources(dev, &res); - if (ret) - return ret; - - /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry(win, &res) { - switch (resource_type(win->res)) { - case IORESOURCE_IO: - io = win->res; - io->name = "Gemini PCI I/O"; - if (!faraday_res_to_memcfg(io->start - win->offset, - resource_size(io), &val)) { - /* setup I/O space size */ - writel(val, p->base + PCI_IOSIZE); - } else { - dev_err(dev, "illegal IO mem size\n"); - return -EINVAL; - } - ret = pci_remap_iospace(io, io_base); - if (ret) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - ret, io); - continue; - } - break; - case IORESOURCE_MEM: - mem = win->res; - mem->name = "Gemini PCI MEM"; - break; - case IORESOURCE_BUS: - break; - default: - break; - } - } - - /* Setup hostbridge */ - val = readl(p->base + PCI_CTRL); - val |= PCI_COMMAND_IO; - val |= PCI_COMMAND_MEMORY; - val |= PCI_COMMAND_MASTER; - writel(val, p->base + PCI_CTRL); - /* Mask and clear all interrupts */ - faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2 + 2, 2, 0xF000); - if (variant->cascaded_irq) { - ret = faraday_pci_setup_cascaded_irq(p); - if (ret) { - dev_err(dev, "failed to setup cascaded IRQ\n"); - return ret; - } - } - - /* Check bus clock if we can gear up to 66 MHz */ - if (!IS_ERR(p->bus_clk)) { - unsigned long rate; - u32 val; - - faraday_raw_pci_read_config(p, 0, 0, - FARADAY_PCI_STATUS_CMD, 4, &val); - rate = clk_get_rate(p->bus_clk); - - if ((rate == 33000000) && (val & PCI_STATUS_66MHZ_CAPABLE)) { - dev_info(dev, "33MHz bus is 66MHz capable\n"); - max_bus_speed = PCI_SPEED_66MHz; - ret = clk_set_rate(p->bus_clk, 66000000); - if (ret) - dev_err(dev, "failed to set bus clock\n"); - } else { - dev_info(dev, "33MHz only bus\n"); - max_bus_speed = PCI_SPEED_33MHz; - } - - /* Bumping the clock may fail so read back the rate */ - rate = clk_get_rate(p->bus_clk); - if (rate == 33000000) - cur_bus_speed = PCI_SPEED_33MHz; - if (rate == 66000000) - cur_bus_speed = PCI_SPEED_66MHz; - } - - ret = faraday_pci_parse_map_dma_ranges(p, dev->of_node); - if (ret) - return ret; - - list_splice_init(&res, &host->windows); - ret = pci_scan_root_bus_bridge(host); - if (ret) { - dev_err(dev, "failed to scan host: %d\n", ret); - return ret; - } - p->bus = host->bus; - p->bus->max_bus_speed = max_bus_speed; - p->bus->cur_bus_speed = cur_bus_speed; - - pci_bus_assign_resources(p->bus); - pci_bus_add_devices(p->bus); - pci_free_resource_list(&res); - - return 0; -} - -/* - * We encode bridge variants here, we have at least two so it doesn't - * hurt to have infrastructure to encompass future variants as well. - */ -static const struct faraday_pci_variant faraday_regular = { - .cascaded_irq = true, -}; - -static const struct faraday_pci_variant faraday_dual = { - .cascaded_irq = false, -}; - -static const struct of_device_id faraday_pci_of_match[] = { - { - .compatible = "faraday,ftpci100", - .data = &faraday_regular, - }, - { - .compatible = "faraday,ftpci100-dual", - .data = &faraday_dual, - }, - {}, -}; - -static struct platform_driver faraday_pci_driver = { - .driver = { - .name = "ftpci100", - .of_match_table = of_match_ptr(faraday_pci_of_match), - .suppress_bind_attrs = true, - }, - .probe = faraday_pci_probe, -}; -builtin_platform_driver(faraday_pci_driver); diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c deleted file mode 100644 index d8f10451f273..000000000000 --- a/drivers/pci/host/pci-host-common.c +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Generic PCI host driver common code - * - * Copyright (C) 2014 ARM Limited - * - * Author: Will Deacon - */ - -#include -#include -#include -#include -#include - -static void gen_pci_unmap_cfg(void *ptr) -{ - pci_ecam_free((struct pci_config_window *)ptr); -} - -static struct pci_config_window *gen_pci_init(struct device *dev, - struct list_head *resources, struct pci_ecam_ops *ops) -{ - int err; - struct resource cfgres; - struct resource *bus_range = NULL; - struct pci_config_window *cfg; - - /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range); - if (err) - return ERR_PTR(err); - - err = of_address_to_resource(dev->of_node, 0, &cfgres); - if (err) { - dev_err(dev, "missing \"reg\" property\n"); - goto err_out; - } - - cfg = pci_ecam_create(dev, &cfgres, bus_range, ops); - if (IS_ERR(cfg)) { - err = PTR_ERR(cfg); - goto err_out; - } - - err = devm_add_action(dev, gen_pci_unmap_cfg, cfg); - if (err) { - gen_pci_unmap_cfg(cfg); - goto err_out; - } - return cfg; - -err_out: - pci_free_resource_list(resources); - return ERR_PTR(err); -} - -int pci_host_common_probe(struct platform_device *pdev, - struct pci_ecam_ops *ops) -{ - const char *type; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct pci_host_bridge *bridge; - struct pci_config_window *cfg; - struct list_head resources; - int ret; - - bridge = devm_pci_alloc_host_bridge(dev, 0); - if (!bridge) - return -ENOMEM; - - type = of_get_property(np, "device_type", NULL); - if (!type || strcmp(type, "pci")) { - dev_err(dev, "invalid \"device_type\" %s\n", type); - return -EINVAL; - } - - of_pci_check_probe_only(); - - /* Parse and map our Configuration Space windows */ - cfg = gen_pci_init(dev, &resources, ops); - if (IS_ERR(cfg)) - return PTR_ERR(cfg); - - /* Do not reassign resources if probe only */ - if (!pci_has_flag(PCI_PROBE_ONLY)) - pci_add_flags(PCI_REASSIGN_ALL_BUS); - - list_splice_init(&resources, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = cfg; - bridge->busnr = cfg->busr.start; - bridge->ops = &ops->pci_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - ret = pci_host_probe(bridge); - if (ret < 0) { - pci_free_resource_list(&resources); - return ret; - } - - platform_set_drvdata(pdev, bridge->bus); - return 0; -} - -int pci_host_common_remove(struct platform_device *pdev) -{ - struct pci_bus *bus = platform_get_drvdata(pdev); - - pci_lock_rescan_remove(); - pci_stop_root_bus(bus); - pci_remove_root_bus(bus); - pci_unlock_rescan_remove(); - - return 0; -} diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c deleted file mode 100644 index dea3ec7592a2..000000000000 --- a/drivers/pci/host/pci-host-generic.c +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Simple, generic PCI host controller driver targetting firmware-initialised - * systems and virtual machines (e.g. the PCI emulation provided by kvmtool). - * - * Copyright (C) 2014 ARM Limited - * - * Author: Will Deacon - */ - -#include -#include -#include -#include -#include -#include - -static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { - .bus_shift = 16, - .pci_ops = { - .map_bus = pci_ecam_map_bus, - .read = pci_generic_config_read, - .write = pci_generic_config_write, - } -}; - -static bool pci_dw_valid_device(struct pci_bus *bus, unsigned int devfn) -{ - struct pci_config_window *cfg = bus->sysdata; - - /* - * The Synopsys DesignWare PCIe controller in ECAM mode will not filter - * type 0 config TLPs sent to devices 1 and up on its downstream port, - * resulting in devices appearing multiple times on bus 0 unless we - * filter out those accesses here. - */ - if (bus->number == cfg->busr.start && PCI_SLOT(devfn) > 0) - return false; - - return true; -} - -static void __iomem *pci_dw_ecam_map_bus(struct pci_bus *bus, - unsigned int devfn, int where) -{ - if (!pci_dw_valid_device(bus, devfn)) - return NULL; - - return pci_ecam_map_bus(bus, devfn, where); -} - -static struct pci_ecam_ops pci_dw_ecam_bus_ops = { - .bus_shift = 20, - .pci_ops = { - .map_bus = pci_dw_ecam_map_bus, - .read = pci_generic_config_read, - .write = pci_generic_config_write, - } -}; - -static const struct of_device_id gen_pci_of_match[] = { - { .compatible = "pci-host-cam-generic", - .data = &gen_pci_cfg_cam_bus_ops }, - - { .compatible = "pci-host-ecam-generic", - .data = &pci_generic_ecam_ops }, - - { .compatible = "marvell,armada8k-pcie-ecam", - .data = &pci_dw_ecam_bus_ops }, - - { .compatible = "socionext,synquacer-pcie-ecam", - .data = &pci_dw_ecam_bus_ops }, - - { .compatible = "snps,dw-pcie-ecam", - .data = &pci_dw_ecam_bus_ops }, - - { }, -}; - -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, - .suppress_bind_attrs = true, - }, - .probe = gen_pci_probe, - .remove = pci_host_common_remove, -}; -builtin_platform_driver(gen_pci_driver); diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c deleted file mode 100644 index 6cc5036ac83c..000000000000 --- a/drivers/pci/host/pci-hyperv.c +++ /dev/null @@ -1,2694 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) Microsoft Corporation. - * - * Author: - * Jake Oshins - * - * This driver acts as a paravirtual front-end for PCI Express root buses. - * When a PCI Express function (either an entire device or an SR-IOV - * Virtual Function) is being passed through to the VM, this driver exposes - * a new bus to the guest VM. This is modeled as a root PCI bus because - * no bridges are being exposed to the VM. In fact, with a "Generation 2" - * VM within Hyper-V, there may seem to be no PCI bus at all in the VM - * until a device as been exposed using this driver. - * - * Each root PCI bus has its own PCI domain, which is called "Segment" in - * the PCI Firmware Specifications. Thus while each device passed through - * to the VM using this front-end will appear at "device 0", the domain will - * be unique. Typically, each bus will have one PCI function on it, though - * this driver does support more than one. - * - * In order to map the interrupts from the device through to the guest VM, - * this driver also implements an IRQ Domain, which handles interrupts (either - * MSI or MSI-X) associated with the functions on the bus. As interrupts are - * set up, torn down, or reaffined, this driver communicates with the - * underlying hypervisor to adjust the mappings in the I/O MMU so that each - * interrupt will be delivered to the correct virtual processor at the right - * vector. This driver does not support level-triggered (line-based) - * interrupts, and will report that the Interrupt Line register in the - * function's configuration space is zero. - * - * The rest of this driver mostly maps PCI concepts onto underlying Hyper-V - * facilities. For instance, the configuration space of a function exposed - * by Hyper-V is mapped into a single page of memory space, and the - * read and write handlers for config space must be aware of this mechanism. - * Similarly, device setup and teardown involves messages sent to and from - * the PCI back-end driver in Hyper-V. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Protocol versions. The low word is the minor version, the high word the - * major version. - */ - -#define PCI_MAKE_VERSION(major, minor) ((u32)(((major) << 16) | (minor))) -#define PCI_MAJOR_VERSION(version) ((u32)(version) >> 16) -#define PCI_MINOR_VERSION(version) ((u32)(version) & 0xff) - -enum pci_protocol_version_t { - PCI_PROTOCOL_VERSION_1_1 = PCI_MAKE_VERSION(1, 1), /* Win10 */ - PCI_PROTOCOL_VERSION_1_2 = PCI_MAKE_VERSION(1, 2), /* RS1 */ -}; - -#define CPU_AFFINITY_ALL -1ULL - -/* - * Supported protocol versions in the order of probing - highest go - * first. - */ -static enum pci_protocol_version_t pci_protocol_versions[] = { - PCI_PROTOCOL_VERSION_1_2, - PCI_PROTOCOL_VERSION_1_1, -}; - -/* - * Protocol version negotiated by hv_pci_protocol_negotiation(). - */ -static enum pci_protocol_version_t pci_protocol_version; - -#define PCI_CONFIG_MMIO_LENGTH 0x2000 -#define CFG_PAGE_OFFSET 0x1000 -#define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET) - -#define MAX_SUPPORTED_MSI_MESSAGES 0x400 - -#define STATUS_REVISION_MISMATCH 0xC0000059 - -/* - * Message Types - */ - -enum pci_message_type { - /* - * Version 1.1 - */ - PCI_MESSAGE_BASE = 0x42490000, - PCI_BUS_RELATIONS = PCI_MESSAGE_BASE + 0, - PCI_QUERY_BUS_RELATIONS = PCI_MESSAGE_BASE + 1, - PCI_POWER_STATE_CHANGE = PCI_MESSAGE_BASE + 4, - PCI_QUERY_RESOURCE_REQUIREMENTS = PCI_MESSAGE_BASE + 5, - PCI_QUERY_RESOURCE_RESOURCES = PCI_MESSAGE_BASE + 6, - PCI_BUS_D0ENTRY = PCI_MESSAGE_BASE + 7, - PCI_BUS_D0EXIT = PCI_MESSAGE_BASE + 8, - PCI_READ_BLOCK = PCI_MESSAGE_BASE + 9, - PCI_WRITE_BLOCK = PCI_MESSAGE_BASE + 0xA, - PCI_EJECT = PCI_MESSAGE_BASE + 0xB, - PCI_QUERY_STOP = PCI_MESSAGE_BASE + 0xC, - PCI_REENABLE = PCI_MESSAGE_BASE + 0xD, - PCI_QUERY_STOP_FAILED = PCI_MESSAGE_BASE + 0xE, - PCI_EJECTION_COMPLETE = PCI_MESSAGE_BASE + 0xF, - PCI_RESOURCES_ASSIGNED = PCI_MESSAGE_BASE + 0x10, - PCI_RESOURCES_RELEASED = PCI_MESSAGE_BASE + 0x11, - PCI_INVALIDATE_BLOCK = PCI_MESSAGE_BASE + 0x12, - PCI_QUERY_PROTOCOL_VERSION = PCI_MESSAGE_BASE + 0x13, - PCI_CREATE_INTERRUPT_MESSAGE = PCI_MESSAGE_BASE + 0x14, - PCI_DELETE_INTERRUPT_MESSAGE = PCI_MESSAGE_BASE + 0x15, - PCI_RESOURCES_ASSIGNED2 = PCI_MESSAGE_BASE + 0x16, - PCI_CREATE_INTERRUPT_MESSAGE2 = PCI_MESSAGE_BASE + 0x17, - PCI_DELETE_INTERRUPT_MESSAGE2 = PCI_MESSAGE_BASE + 0x18, /* unused */ - PCI_MESSAGE_MAXIMUM -}; - -/* - * Structures defining the virtual PCI Express protocol. - */ - -union pci_version { - struct { - u16 minor_version; - u16 major_version; - } parts; - u32 version; -} __packed; - -/* - * Function numbers are 8-bits wide on Express, as interpreted through ARI, - * which is all this driver does. This representation is the one used in - * Windows, which is what is expected when sending this back and forth with - * the Hyper-V parent partition. - */ -union win_slot_encoding { - struct { - u32 dev:5; - u32 func:3; - u32 reserved:24; - } bits; - u32 slot; -} __packed; - -/* - * Pretty much as defined in the PCI Specifications. - */ -struct pci_function_description { - u16 v_id; /* vendor ID */ - u16 d_id; /* device ID */ - u8 rev; - u8 prog_intf; - u8 subclass; - u8 base_class; - u32 subsystem_id; - union win_slot_encoding win_slot; - u32 ser; /* serial number */ -} __packed; - -/** - * struct hv_msi_desc - * @vector: IDT entry - * @delivery_mode: As defined in Intel's Programmer's - * Reference Manual, Volume 3, Chapter 8. - * @vector_count: Number of contiguous entries in the - * Interrupt Descriptor Table that are - * occupied by this Message-Signaled - * Interrupt. For "MSI", as first defined - * in PCI 2.2, this can be between 1 and - * 32. For "MSI-X," as first defined in PCI - * 3.0, this must be 1, as each MSI-X table - * entry would have its own descriptor. - * @reserved: Empty space - * @cpu_mask: All the target virtual processors. - */ -struct hv_msi_desc { - u8 vector; - u8 delivery_mode; - u16 vector_count; - u32 reserved; - u64 cpu_mask; -} __packed; - -/** - * struct hv_msi_desc2 - 1.2 version of hv_msi_desc - * @vector: IDT entry - * @delivery_mode: As defined in Intel's Programmer's - * Reference Manual, Volume 3, Chapter 8. - * @vector_count: Number of contiguous entries in the - * Interrupt Descriptor Table that are - * occupied by this Message-Signaled - * Interrupt. For "MSI", as first defined - * in PCI 2.2, this can be between 1 and - * 32. For "MSI-X," as first defined in PCI - * 3.0, this must be 1, as each MSI-X table - * entry would have its own descriptor. - * @processor_count: number of bits enabled in array. - * @processor_array: All the target virtual processors. - */ -struct hv_msi_desc2 { - u8 vector; - u8 delivery_mode; - u16 vector_count; - u16 processor_count; - u16 processor_array[32]; -} __packed; - -/** - * struct tran_int_desc - * @reserved: unused, padding - * @vector_count: same as in hv_msi_desc - * @data: This is the "data payload" value that is - * written by the device when it generates - * a message-signaled interrupt, either MSI - * or MSI-X. - * @address: This is the address to which the data - * payload is written on interrupt - * generation. - */ -struct tran_int_desc { - u16 reserved; - u16 vector_count; - u32 data; - u64 address; -} __packed; - -/* - * A generic message format for virtual PCI. - * Specific message formats are defined later in the file. - */ - -struct pci_message { - u32 type; -} __packed; - -struct pci_child_message { - struct pci_message message_type; - union win_slot_encoding wslot; -} __packed; - -struct pci_incoming_message { - struct vmpacket_descriptor hdr; - struct pci_message message_type; -} __packed; - -struct pci_response { - struct vmpacket_descriptor hdr; - s32 status; /* negative values are failures */ -} __packed; - -struct pci_packet { - void (*completion_func)(void *context, struct pci_response *resp, - int resp_packet_size); - void *compl_ctxt; - - struct pci_message message[0]; -}; - -/* - * Specific message types supporting the PCI protocol. - */ - -/* - * Version negotiation message. Sent from the guest to the host. - * The guest is free to try different versions until the host - * accepts the version. - * - * pci_version: The protocol version requested. - * is_last_attempt: If TRUE, this is the last version guest will request. - * reservedz: Reserved field, set to zero. - */ - -struct pci_version_request { - struct pci_message message_type; - u32 protocol_version; -} __packed; - -/* - * Bus D0 Entry. This is sent from the guest to the host when the virtual - * bus (PCI Express port) is ready for action. - */ - -struct pci_bus_d0_entry { - struct pci_message message_type; - u32 reserved; - u64 mmio_base; -} __packed; - -struct pci_bus_relations { - struct pci_incoming_message incoming; - u32 device_count; - struct pci_function_description func[0]; -} __packed; - -struct pci_q_res_req_response { - struct vmpacket_descriptor hdr; - s32 status; /* negative values are failures */ - u32 probed_bar[6]; -} __packed; - -struct pci_set_power { - struct pci_message message_type; - union win_slot_encoding wslot; - u32 power_state; /* In Windows terms */ - u32 reserved; -} __packed; - -struct pci_set_power_response { - struct vmpacket_descriptor hdr; - s32 status; /* negative values are failures */ - union win_slot_encoding wslot; - u32 resultant_state; /* In Windows terms */ - u32 reserved; -} __packed; - -struct pci_resources_assigned { - struct pci_message message_type; - union win_slot_encoding wslot; - u8 memory_range[0x14][6]; /* not used here */ - u32 msi_descriptors; - u32 reserved[4]; -} __packed; - -struct pci_resources_assigned2 { - struct pci_message message_type; - union win_slot_encoding wslot; - u8 memory_range[0x14][6]; /* not used here */ - u32 msi_descriptor_count; - u8 reserved[70]; -} __packed; - -struct pci_create_interrupt { - struct pci_message message_type; - union win_slot_encoding wslot; - struct hv_msi_desc int_desc; -} __packed; - -struct pci_create_int_response { - struct pci_response response; - u32 reserved; - struct tran_int_desc int_desc; -} __packed; - -struct pci_create_interrupt2 { - struct pci_message message_type; - union win_slot_encoding wslot; - struct hv_msi_desc2 int_desc; -} __packed; - -struct pci_delete_interrupt { - struct pci_message message_type; - union win_slot_encoding wslot; - struct tran_int_desc int_desc; -} __packed; - -struct pci_dev_incoming { - struct pci_incoming_message incoming; - union win_slot_encoding wslot; -} __packed; - -struct pci_eject_response { - struct pci_message message_type; - union win_slot_encoding wslot; - u32 status; -} __packed; - -static int pci_ring_size = (4 * PAGE_SIZE); - -/* - * Definitions or interrupt steering hypercall. - */ -#define HV_PARTITION_ID_SELF ((u64)-1) -#define HVCALL_RETARGET_INTERRUPT 0x7e - -struct hv_interrupt_entry { - u32 source; /* 1 for MSI(-X) */ - u32 reserved1; - u32 address; - u32 data; -}; - -#define HV_VP_SET_BANK_COUNT_MAX 5 /* current implementation limit */ - -struct hv_vp_set { - u64 format; /* 0 (HvGenericSetSparse4k) */ - u64 valid_banks; - u64 masks[HV_VP_SET_BANK_COUNT_MAX]; -}; - -/* - * flags for hv_device_interrupt_target.flags - */ -#define HV_DEVICE_INTERRUPT_TARGET_MULTICAST 1 -#define HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET 2 - -struct hv_device_interrupt_target { - u32 vector; - u32 flags; - union { - u64 vp_mask; - struct hv_vp_set vp_set; - }; -}; - -struct retarget_msi_interrupt { - u64 partition_id; /* use "self" */ - u64 device_id; - struct hv_interrupt_entry int_entry; - u64 reserved2; - struct hv_device_interrupt_target int_target; -} __packed; - -/* - * Driver specific state. - */ - -enum hv_pcibus_state { - hv_pcibus_init = 0, - hv_pcibus_probed, - hv_pcibus_installed, - hv_pcibus_removed, - hv_pcibus_maximum -}; - -struct hv_pcibus_device { - struct pci_sysdata sysdata; - enum hv_pcibus_state state; - refcount_t remove_lock; - struct hv_device *hdev; - resource_size_t low_mmio_space; - resource_size_t high_mmio_space; - struct resource *mem_config; - struct resource *low_mmio_res; - struct resource *high_mmio_res; - struct completion *survey_event; - struct completion remove_event; - struct pci_bus *pci_bus; - spinlock_t config_lock; /* Avoid two threads writing index page */ - spinlock_t device_list_lock; /* Protect lists below */ - void __iomem *cfg_addr; - - struct list_head resources_for_children; - - struct list_head children; - struct list_head dr_list; - - struct msi_domain_info msi_info; - struct msi_controller msi_chip; - struct irq_domain *irq_domain; - - /* hypercall arg, must not cross page boundary */ - struct retarget_msi_interrupt retarget_msi_interrupt_params; - - spinlock_t retarget_msi_interrupt_lock; - - struct workqueue_struct *wq; -}; - -/* - * Tracks "Device Relations" messages from the host, which must be both - * processed in order and deferred so that they don't run in the context - * of the incoming packet callback. - */ -struct hv_dr_work { - struct work_struct wrk; - struct hv_pcibus_device *bus; -}; - -struct hv_dr_state { - struct list_head list_entry; - u32 device_count; - struct pci_function_description func[0]; -}; - -enum hv_pcichild_state { - hv_pcichild_init = 0, - hv_pcichild_requirements, - hv_pcichild_resourced, - hv_pcichild_ejecting, - hv_pcichild_maximum -}; - -struct hv_pci_dev { - /* List protected by pci_rescan_remove_lock */ - struct list_head list_entry; - refcount_t refs; - enum hv_pcichild_state state; - struct pci_function_description desc; - bool reported_missing; - struct hv_pcibus_device *hbus; - struct work_struct wrk; - - /* - * What would be observed if one wrote 0xFFFFFFFF to a BAR and then - * read it back, for each of the BAR offsets within config space. - */ - u32 probed_bar[6]; -}; - -struct hv_pci_compl { - struct completion host_event; - s32 completion_status; -}; - -static void hv_pci_onchannelcallback(void *context); - -/** - * hv_pci_generic_compl() - Invoked for a completion packet - * @context: Set up by the sender of the packet. - * @resp: The response packet - * @resp_packet_size: Size in bytes of the packet - * - * This function is used to trigger an event and report status - * for any message for which the completion packet contains a - * status and nothing else. - */ -static void hv_pci_generic_compl(void *context, struct pci_response *resp, - int resp_packet_size) -{ - struct hv_pci_compl *comp_pkt = context; - - if (resp_packet_size >= offsetofend(struct pci_response, status)) - comp_pkt->completion_status = resp->status; - else - comp_pkt->completion_status = -1; - - complete(&comp_pkt->host_event); -} - -static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus, - u32 wslot); - -static void get_pcichild(struct hv_pci_dev *hpdev) -{ - refcount_inc(&hpdev->refs); -} - -static void put_pcichild(struct hv_pci_dev *hpdev) -{ - if (refcount_dec_and_test(&hpdev->refs)) - kfree(hpdev); -} - -static void get_hvpcibus(struct hv_pcibus_device *hv_pcibus); -static void put_hvpcibus(struct hv_pcibus_device *hv_pcibus); - -/* - * There is no good way to get notified from vmbus_onoffer_rescind(), - * so let's use polling here, since this is not a hot path. - */ -static int wait_for_response(struct hv_device *hdev, - struct completion *comp) -{ - while (true) { - if (hdev->channel->rescind) { - dev_warn_once(&hdev->device, "The device is gone.\n"); - return -ENODEV; - } - - if (wait_for_completion_timeout(comp, HZ / 10)) - break; - } - - return 0; -} - -/** - * devfn_to_wslot() - Convert from Linux PCI slot to Windows - * @devfn: The Linux representation of PCI slot - * - * Windows uses a slightly different representation of PCI slot. - * - * Return: The Windows representation - */ -static u32 devfn_to_wslot(int devfn) -{ - union win_slot_encoding wslot; - - wslot.slot = 0; - wslot.bits.dev = PCI_SLOT(devfn); - wslot.bits.func = PCI_FUNC(devfn); - - return wslot.slot; -} - -/** - * wslot_to_devfn() - Convert from Windows PCI slot to Linux - * @wslot: The Windows representation of PCI slot - * - * Windows uses a slightly different representation of PCI slot. - * - * Return: The Linux representation - */ -static int wslot_to_devfn(u32 wslot) -{ - union win_slot_encoding slot_no; - - slot_no.slot = wslot; - return PCI_DEVFN(slot_no.bits.dev, slot_no.bits.func); -} - -/* - * PCI Configuration Space for these root PCI buses is implemented as a pair - * of pages in memory-mapped I/O space. Writing to the first page chooses - * the PCI function being written or read. Once the first page has been - * written to, the following page maps in the entire configuration space of - * the function. - */ - -/** - * _hv_pcifront_read_config() - Internal PCI config read - * @hpdev: The PCI driver's representation of the device - * @where: Offset within config space - * @size: Size of the transfer - * @val: Pointer to the buffer receiving the data - */ -static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where, - int size, u32 *val) -{ - unsigned long flags; - void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where; - - /* - * If the attempt is to read the IDs or the ROM BAR, simulate that. - */ - if (where + size <= PCI_COMMAND) { - memcpy(val, ((u8 *)&hpdev->desc.v_id) + where, size); - } else if (where >= PCI_CLASS_REVISION && where + size <= - PCI_CACHE_LINE_SIZE) { - memcpy(val, ((u8 *)&hpdev->desc.rev) + where - - PCI_CLASS_REVISION, size); - } else if (where >= PCI_SUBSYSTEM_VENDOR_ID && where + size <= - PCI_ROM_ADDRESS) { - memcpy(val, (u8 *)&hpdev->desc.subsystem_id + where - - PCI_SUBSYSTEM_VENDOR_ID, size); - } else if (where >= PCI_ROM_ADDRESS && where + size <= - PCI_CAPABILITY_LIST) { - /* ROM BARs are unimplemented */ - *val = 0; - } else if (where >= PCI_INTERRUPT_LINE && where + size <= - PCI_INTERRUPT_PIN) { - /* - * Interrupt Line and Interrupt PIN are hard-wired to zero - * because this front-end only supports message-signaled - * interrupts. - */ - *val = 0; - } else if (where + size <= CFG_PAGE_SIZE) { - spin_lock_irqsave(&hpdev->hbus->config_lock, flags); - /* Choose the function to be read. (See comment above) */ - writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr); - /* Make sure the function was chosen before we start reading. */ - mb(); - /* Read from that function's config space. */ - switch (size) { - case 1: - *val = readb(addr); - break; - case 2: - *val = readw(addr); - break; - default: - *val = readl(addr); - break; - } - /* - * Make sure the read was done before we release the spinlock - * allowing consecutive reads/writes. - */ - mb(); - spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags); - } else { - dev_err(&hpdev->hbus->hdev->device, - "Attempt to read beyond a function's config space.\n"); - } -} - -static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev) -{ - u16 ret; - unsigned long flags; - void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + - PCI_VENDOR_ID; - - spin_lock_irqsave(&hpdev->hbus->config_lock, flags); - - /* Choose the function to be read. (See comment above) */ - writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr); - /* Make sure the function was chosen before we start reading. */ - mb(); - /* Read from that function's config space. */ - ret = readw(addr); - /* - * mb() is not required here, because the spin_unlock_irqrestore() - * is a barrier. - */ - - spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags); - - return ret; -} - -/** - * _hv_pcifront_write_config() - Internal PCI config write - * @hpdev: The PCI driver's representation of the device - * @where: Offset within config space - * @size: Size of the transfer - * @val: The data being transferred - */ -static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where, - int size, u32 val) -{ - unsigned long flags; - void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where; - - if (where >= PCI_SUBSYSTEM_VENDOR_ID && - where + size <= PCI_CAPABILITY_LIST) { - /* SSIDs and ROM BARs are read-only */ - } else if (where >= PCI_COMMAND && where + size <= CFG_PAGE_SIZE) { - spin_lock_irqsave(&hpdev->hbus->config_lock, flags); - /* Choose the function to be written. (See comment above) */ - writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr); - /* Make sure the function was chosen before we start writing. */ - wmb(); - /* Write to that function's config space. */ - switch (size) { - case 1: - writeb(val, addr); - break; - case 2: - writew(val, addr); - break; - default: - writel(val, addr); - break; - } - /* - * Make sure the write was done before we release the spinlock - * allowing consecutive reads/writes. - */ - mb(); - spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags); - } else { - dev_err(&hpdev->hbus->hdev->device, - "Attempt to write beyond a function's config space.\n"); - } -} - -/** - * hv_pcifront_read_config() - Read configuration space - * @bus: PCI Bus structure - * @devfn: Device/function - * @where: Offset from base - * @size: Byte/word/dword - * @val: Value to be read - * - * Return: PCIBIOS_SUCCESSFUL on success - * PCIBIOS_DEVICE_NOT_FOUND on failure - */ -static int hv_pcifront_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct hv_pcibus_device *hbus = - container_of(bus->sysdata, struct hv_pcibus_device, sysdata); - struct hv_pci_dev *hpdev; - - hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(devfn)); - if (!hpdev) - return PCIBIOS_DEVICE_NOT_FOUND; - - _hv_pcifront_read_config(hpdev, where, size, val); - - put_pcichild(hpdev); - return PCIBIOS_SUCCESSFUL; -} - -/** - * hv_pcifront_write_config() - Write configuration space - * @bus: PCI Bus structure - * @devfn: Device/function - * @where: Offset from base - * @size: Byte/word/dword - * @val: Value to be written to device - * - * Return: PCIBIOS_SUCCESSFUL on success - * PCIBIOS_DEVICE_NOT_FOUND on failure - */ -static int hv_pcifront_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct hv_pcibus_device *hbus = - container_of(bus->sysdata, struct hv_pcibus_device, sysdata); - struct hv_pci_dev *hpdev; - - hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(devfn)); - if (!hpdev) - return PCIBIOS_DEVICE_NOT_FOUND; - - _hv_pcifront_write_config(hpdev, where, size, val); - - put_pcichild(hpdev); - return PCIBIOS_SUCCESSFUL; -} - -/* PCIe operations */ -static struct pci_ops hv_pcifront_ops = { - .read = hv_pcifront_read_config, - .write = hv_pcifront_write_config, -}; - -/* Interrupt management hooks */ -static void hv_int_desc_free(struct hv_pci_dev *hpdev, - struct tran_int_desc *int_desc) -{ - struct pci_delete_interrupt *int_pkt; - struct { - struct pci_packet pkt; - u8 buffer[sizeof(struct pci_delete_interrupt)]; - } ctxt; - - memset(&ctxt, 0, sizeof(ctxt)); - int_pkt = (struct pci_delete_interrupt *)&ctxt.pkt.message; - int_pkt->message_type.type = - PCI_DELETE_INTERRUPT_MESSAGE; - int_pkt->wslot.slot = hpdev->desc.win_slot.slot; - int_pkt->int_desc = *int_desc; - vmbus_sendpacket(hpdev->hbus->hdev->channel, int_pkt, sizeof(*int_pkt), - (unsigned long)&ctxt.pkt, VM_PKT_DATA_INBAND, 0); - kfree(int_desc); -} - -/** - * hv_msi_free() - Free the MSI. - * @domain: The interrupt domain pointer - * @info: Extra MSI-related context - * @irq: Identifies the IRQ. - * - * The Hyper-V parent partition and hypervisor are tracking the - * messages that are in use, keeping the interrupt redirection - * table up to date. This callback sends a message that frees - * the IRT entry and related tracking nonsense. - */ -static void hv_msi_free(struct irq_domain *domain, struct msi_domain_info *info, - unsigned int irq) -{ - struct hv_pcibus_device *hbus; - struct hv_pci_dev *hpdev; - struct pci_dev *pdev; - struct tran_int_desc *int_desc; - struct irq_data *irq_data = irq_domain_get_irq_data(domain, irq); - struct msi_desc *msi = irq_data_get_msi_desc(irq_data); - - pdev = msi_desc_to_pci_dev(msi); - hbus = info->data; - int_desc = irq_data_get_irq_chip_data(irq_data); - if (!int_desc) - return; - - irq_data->chip_data = NULL; - hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn)); - if (!hpdev) { - kfree(int_desc); - return; - } - - hv_int_desc_free(hpdev, int_desc); - put_pcichild(hpdev); -} - -static int hv_set_affinity(struct irq_data *data, const struct cpumask *dest, - bool force) -{ - struct irq_data *parent = data->parent_data; - - return parent->chip->irq_set_affinity(parent, dest, force); -} - -static void hv_irq_mask(struct irq_data *data) -{ - pci_msi_mask_irq(data); -} - -/** - * hv_irq_unmask() - "Unmask" the IRQ by setting its current - * affinity. - * @data: Describes the IRQ - * - * Build new a destination for the MSI and make a hypercall to - * update the Interrupt Redirection Table. "Device Logical ID" - * is built out of this PCI bus's instance GUID and the function - * number of the device. - */ -static void hv_irq_unmask(struct irq_data *data) -{ - struct msi_desc *msi_desc = irq_data_get_msi_desc(data); - struct irq_cfg *cfg = irqd_cfg(data); - struct retarget_msi_interrupt *params; - struct hv_pcibus_device *hbus; - struct cpumask *dest; - struct pci_bus *pbus; - struct pci_dev *pdev; - unsigned long flags; - u32 var_size = 0; - int cpu_vmbus; - int cpu; - u64 res; - - dest = irq_data_get_effective_affinity_mask(data); - pdev = msi_desc_to_pci_dev(msi_desc); - pbus = pdev->bus; - hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); - - spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags); - - params = &hbus->retarget_msi_interrupt_params; - memset(params, 0, sizeof(*params)); - params->partition_id = HV_PARTITION_ID_SELF; - params->int_entry.source = 1; /* MSI(-X) */ - params->int_entry.address = msi_desc->msg.address_lo; - params->int_entry.data = msi_desc->msg.data; - params->device_id = (hbus->hdev->dev_instance.b[5] << 24) | - (hbus->hdev->dev_instance.b[4] << 16) | - (hbus->hdev->dev_instance.b[7] << 8) | - (hbus->hdev->dev_instance.b[6] & 0xf8) | - PCI_FUNC(pdev->devfn); - params->int_target.vector = cfg->vector; - - /* - * Honoring apic->irq_delivery_mode set to dest_Fixed by - * setting the HV_DEVICE_INTERRUPT_TARGET_MULTICAST flag results in a - * spurious interrupt storm. Not doing so does not seem to have a - * negative effect (yet?). - */ - - if (pci_protocol_version >= PCI_PROTOCOL_VERSION_1_2) { - /* - * PCI_PROTOCOL_VERSION_1_2 supports the VP_SET version of the - * HVCALL_RETARGET_INTERRUPT hypercall, which also coincides - * with >64 VP support. - * ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED - * is not sufficient for this hypercall. - */ - params->int_target.flags |= - HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET; - params->int_target.vp_set.valid_banks = - (1ull << HV_VP_SET_BANK_COUNT_MAX) - 1; - - /* - * var-sized hypercall, var-size starts after vp_mask (thus - * vp_set.format does not count, but vp_set.valid_banks does). - */ - var_size = 1 + HV_VP_SET_BANK_COUNT_MAX; - - for_each_cpu_and(cpu, dest, cpu_online_mask) { - cpu_vmbus = hv_cpu_number_to_vp_number(cpu); - - if (cpu_vmbus >= HV_VP_SET_BANK_COUNT_MAX * 64) { - dev_err(&hbus->hdev->device, - "too high CPU %d", cpu_vmbus); - res = 1; - goto exit_unlock; - } - - params->int_target.vp_set.masks[cpu_vmbus / 64] |= - (1ULL << (cpu_vmbus & 63)); - } - } else { - for_each_cpu_and(cpu, dest, cpu_online_mask) { - params->int_target.vp_mask |= - (1ULL << hv_cpu_number_to_vp_number(cpu)); - } - } - - res = hv_do_hypercall(HVCALL_RETARGET_INTERRUPT | (var_size << 17), - params, NULL); - -exit_unlock: - spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags); - - if (res) { - dev_err(&hbus->hdev->device, - "%s() failed: %#llx", __func__, res); - return; - } - - pci_msi_unmask_irq(data); -} - -struct compose_comp_ctxt { - struct hv_pci_compl comp_pkt; - struct tran_int_desc int_desc; -}; - -static void hv_pci_compose_compl(void *context, struct pci_response *resp, - int resp_packet_size) -{ - struct compose_comp_ctxt *comp_pkt = context; - struct pci_create_int_response *int_resp = - (struct pci_create_int_response *)resp; - - comp_pkt->comp_pkt.completion_status = resp->status; - comp_pkt->int_desc = int_resp->int_desc; - complete(&comp_pkt->comp_pkt.host_event); -} - -static u32 hv_compose_msi_req_v1( - struct pci_create_interrupt *int_pkt, struct cpumask *affinity, - u32 slot, u8 vector) -{ - int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE; - int_pkt->wslot.slot = slot; - int_pkt->int_desc.vector = vector; - int_pkt->int_desc.vector_count = 1; - int_pkt->int_desc.delivery_mode = dest_Fixed; - - /* - * Create MSI w/ dummy vCPU set, overwritten by subsequent retarget in - * hv_irq_unmask(). - */ - int_pkt->int_desc.cpu_mask = CPU_AFFINITY_ALL; - - return sizeof(*int_pkt); -} - -static u32 hv_compose_msi_req_v2( - struct pci_create_interrupt2 *int_pkt, struct cpumask *affinity, - u32 slot, u8 vector) -{ - int cpu; - - int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE2; - int_pkt->wslot.slot = slot; - int_pkt->int_desc.vector = vector; - int_pkt->int_desc.vector_count = 1; - int_pkt->int_desc.delivery_mode = dest_Fixed; - - /* - * Create MSI w/ dummy vCPU set targeting just one vCPU, overwritten - * by subsequent retarget in hv_irq_unmask(). - */ - cpu = cpumask_first_and(affinity, cpu_online_mask); - int_pkt->int_desc.processor_array[0] = - hv_cpu_number_to_vp_number(cpu); - int_pkt->int_desc.processor_count = 1; - - return sizeof(*int_pkt); -} - -/** - * hv_compose_msi_msg() - Supplies a valid MSI address/data - * @data: Everything about this MSI - * @msg: Buffer that is filled in by this function - * - * This function unpacks the IRQ looking for target CPU set, IDT - * vector and mode and sends a message to the parent partition - * asking for a mapping for that tuple in this partition. The - * response supplies a data value and address to which that data - * should be written to trigger that interrupt. - */ -static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct irq_cfg *cfg = irqd_cfg(data); - struct hv_pcibus_device *hbus; - struct hv_pci_dev *hpdev; - struct pci_bus *pbus; - struct pci_dev *pdev; - struct cpumask *dest; - struct compose_comp_ctxt comp; - struct tran_int_desc *int_desc; - struct { - struct pci_packet pci_pkt; - union { - struct pci_create_interrupt v1; - struct pci_create_interrupt2 v2; - } int_pkts; - } __packed ctxt; - - u32 size; - int ret; - - pdev = msi_desc_to_pci_dev(irq_data_get_msi_desc(data)); - dest = irq_data_get_effective_affinity_mask(data); - pbus = pdev->bus; - hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); - hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn)); - if (!hpdev) - goto return_null_message; - - /* Free any previous message that might have already been composed. */ - if (data->chip_data) { - int_desc = data->chip_data; - data->chip_data = NULL; - hv_int_desc_free(hpdev, int_desc); - } - - int_desc = kzalloc(sizeof(*int_desc), GFP_ATOMIC); - if (!int_desc) - goto drop_reference; - - memset(&ctxt, 0, sizeof(ctxt)); - init_completion(&comp.comp_pkt.host_event); - ctxt.pci_pkt.completion_func = hv_pci_compose_compl; - ctxt.pci_pkt.compl_ctxt = ∁ - - switch (pci_protocol_version) { - case PCI_PROTOCOL_VERSION_1_1: - size = hv_compose_msi_req_v1(&ctxt.int_pkts.v1, - dest, - hpdev->desc.win_slot.slot, - cfg->vector); - break; - - case PCI_PROTOCOL_VERSION_1_2: - size = hv_compose_msi_req_v2(&ctxt.int_pkts.v2, - dest, - hpdev->desc.win_slot.slot, - cfg->vector); - break; - - default: - /* As we only negotiate protocol versions known to this driver, - * this path should never hit. However, this is it not a hot - * path so we print a message to aid future updates. - */ - dev_err(&hbus->hdev->device, - "Unexpected vPCI protocol, update driver."); - goto free_int_desc; - } - - ret = vmbus_sendpacket(hpdev->hbus->hdev->channel, &ctxt.int_pkts, - size, (unsigned long)&ctxt.pci_pkt, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret) { - dev_err(&hbus->hdev->device, - "Sending request for interrupt failed: 0x%x", - comp.comp_pkt.completion_status); - goto free_int_desc; - } - - /* - * Since this function is called with IRQ locks held, can't - * do normal wait for completion; instead poll. - */ - while (!try_wait_for_completion(&comp.comp_pkt.host_event)) { - /* 0xFFFF means an invalid PCI VENDOR ID. */ - if (hv_pcifront_get_vendor_id(hpdev) == 0xFFFF) { - dev_err_once(&hbus->hdev->device, - "the device has gone\n"); - goto free_int_desc; - } - - /* - * When the higher level interrupt code calls us with - * interrupt disabled, we must poll the channel by calling - * the channel callback directly when channel->target_cpu is - * the current CPU. When the higher level interrupt code - * calls us with interrupt enabled, let's add the - * local_bh_disable()/enable() to avoid race. - */ - local_bh_disable(); - - if (hbus->hdev->channel->target_cpu == smp_processor_id()) - hv_pci_onchannelcallback(hbus); - - local_bh_enable(); - - if (hpdev->state == hv_pcichild_ejecting) { - dev_err_once(&hbus->hdev->device, - "the device is being ejected\n"); - goto free_int_desc; - } - - udelay(100); - } - - if (comp.comp_pkt.completion_status < 0) { - dev_err(&hbus->hdev->device, - "Request for interrupt failed: 0x%x", - comp.comp_pkt.completion_status); - goto free_int_desc; - } - - /* - * Record the assignment so that this can be unwound later. Using - * irq_set_chip_data() here would be appropriate, but the lock it takes - * is already held. - */ - *int_desc = comp.int_desc; - data->chip_data = int_desc; - - /* Pass up the result. */ - msg->address_hi = comp.int_desc.address >> 32; - msg->address_lo = comp.int_desc.address & 0xffffffff; - msg->data = comp.int_desc.data; - - put_pcichild(hpdev); - return; - -free_int_desc: - kfree(int_desc); -drop_reference: - put_pcichild(hpdev); -return_null_message: - msg->address_hi = 0; - msg->address_lo = 0; - msg->data = 0; -} - -/* HW Interrupt Chip Descriptor */ -static struct irq_chip hv_msi_irq_chip = { - .name = "Hyper-V PCIe MSI", - .irq_compose_msi_msg = hv_compose_msi_msg, - .irq_set_affinity = hv_set_affinity, - .irq_ack = irq_chip_ack_parent, - .irq_mask = hv_irq_mask, - .irq_unmask = hv_irq_unmask, -}; - -static irq_hw_number_t hv_msi_domain_ops_get_hwirq(struct msi_domain_info *info, - msi_alloc_info_t *arg) -{ - return arg->msi_hwirq; -} - -static struct msi_domain_ops hv_msi_ops = { - .get_hwirq = hv_msi_domain_ops_get_hwirq, - .msi_prepare = pci_msi_prepare, - .set_desc = pci_msi_set_desc, - .msi_free = hv_msi_free, -}; - -/** - * hv_pcie_init_irq_domain() - Initialize IRQ domain - * @hbus: The root PCI bus - * - * This function creates an IRQ domain which will be used for - * interrupts from devices that have been passed through. These - * devices only support MSI and MSI-X, not line-based interrupts - * or simulations of line-based interrupts through PCIe's - * fabric-layer messages. Because interrupts are remapped, we - * can support multi-message MSI here. - * - * Return: '0' on success and error value on failure - */ -static int hv_pcie_init_irq_domain(struct hv_pcibus_device *hbus) -{ - hbus->msi_info.chip = &hv_msi_irq_chip; - hbus->msi_info.ops = &hv_msi_ops; - hbus->msi_info.flags = (MSI_FLAG_USE_DEF_DOM_OPS | - MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_MULTI_PCI_MSI | - MSI_FLAG_PCI_MSIX); - hbus->msi_info.handler = handle_edge_irq; - hbus->msi_info.handler_name = "edge"; - hbus->msi_info.data = hbus; - hbus->irq_domain = pci_msi_create_irq_domain(hbus->sysdata.fwnode, - &hbus->msi_info, - x86_vector_domain); - if (!hbus->irq_domain) { - dev_err(&hbus->hdev->device, - "Failed to build an MSI IRQ domain\n"); - return -ENODEV; - } - - return 0; -} - -/** - * get_bar_size() - Get the address space consumed by a BAR - * @bar_val: Value that a BAR returned after -1 was written - * to it. - * - * This function returns the size of the BAR, rounded up to 1 - * page. It has to be rounded up because the hypervisor's page - * table entry that maps the BAR into the VM can't specify an - * offset within a page. The invariant is that the hypervisor - * must place any BARs of smaller than page length at the - * beginning of a page. - * - * Return: Size in bytes of the consumed MMIO space. - */ -static u64 get_bar_size(u64 bar_val) -{ - return round_up((1 + ~(bar_val & PCI_BASE_ADDRESS_MEM_MASK)), - PAGE_SIZE); -} - -/** - * survey_child_resources() - Total all MMIO requirements - * @hbus: Root PCI bus, as understood by this driver - */ -static void survey_child_resources(struct hv_pcibus_device *hbus) -{ - struct hv_pci_dev *hpdev; - resource_size_t bar_size = 0; - unsigned long flags; - struct completion *event; - u64 bar_val; - int i; - - /* If nobody is waiting on the answer, don't compute it. */ - event = xchg(&hbus->survey_event, NULL); - if (!event) - return; - - /* If the answer has already been computed, go with it. */ - if (hbus->low_mmio_space || hbus->high_mmio_space) { - complete(event); - return; - } - - spin_lock_irqsave(&hbus->device_list_lock, flags); - - /* - * Due to an interesting quirk of the PCI spec, all memory regions - * for a child device are a power of 2 in size and aligned in memory, - * so it's sufficient to just add them up without tracking alignment. - */ - list_for_each_entry(hpdev, &hbus->children, list_entry) { - for (i = 0; i < 6; i++) { - if (hpdev->probed_bar[i] & PCI_BASE_ADDRESS_SPACE_IO) - dev_err(&hbus->hdev->device, - "There's an I/O BAR in this list!\n"); - - if (hpdev->probed_bar[i] != 0) { - /* - * A probed BAR has all the upper bits set that - * can be changed. - */ - - bar_val = hpdev->probed_bar[i]; - if (bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64) - bar_val |= - ((u64)hpdev->probed_bar[++i] << 32); - else - bar_val |= 0xffffffff00000000ULL; - - bar_size = get_bar_size(bar_val); - - if (bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64) - hbus->high_mmio_space += bar_size; - else - hbus->low_mmio_space += bar_size; - } - } - } - - spin_unlock_irqrestore(&hbus->device_list_lock, flags); - complete(event); -} - -/** - * prepopulate_bars() - Fill in BARs with defaults - * @hbus: Root PCI bus, as understood by this driver - * - * The core PCI driver code seems much, much happier if the BARs - * for a device have values upon first scan. So fill them in. - * The algorithm below works down from large sizes to small, - * attempting to pack the assignments optimally. The assumption, - * enforced in other parts of the code, is that the beginning of - * the memory-mapped I/O space will be aligned on the largest - * BAR size. - */ -static void prepopulate_bars(struct hv_pcibus_device *hbus) -{ - resource_size_t high_size = 0; - resource_size_t low_size = 0; - resource_size_t high_base = 0; - resource_size_t low_base = 0; - resource_size_t bar_size; - struct hv_pci_dev *hpdev; - unsigned long flags; - u64 bar_val; - u32 command; - bool high; - int i; - - if (hbus->low_mmio_space) { - low_size = 1ULL << (63 - __builtin_clzll(hbus->low_mmio_space)); - low_base = hbus->low_mmio_res->start; - } - - if (hbus->high_mmio_space) { - high_size = 1ULL << - (63 - __builtin_clzll(hbus->high_mmio_space)); - high_base = hbus->high_mmio_res->start; - } - - spin_lock_irqsave(&hbus->device_list_lock, flags); - - /* Pick addresses for the BARs. */ - do { - list_for_each_entry(hpdev, &hbus->children, list_entry) { - for (i = 0; i < 6; i++) { - bar_val = hpdev->probed_bar[i]; - if (bar_val == 0) - continue; - high = bar_val & PCI_BASE_ADDRESS_MEM_TYPE_64; - if (high) { - bar_val |= - ((u64)hpdev->probed_bar[i + 1] - << 32); - } else { - bar_val |= 0xffffffffULL << 32; - } - bar_size = get_bar_size(bar_val); - if (high) { - if (high_size != bar_size) { - i++; - continue; - } - _hv_pcifront_write_config(hpdev, - PCI_BASE_ADDRESS_0 + (4 * i), - 4, - (u32)(high_base & 0xffffff00)); - i++; - _hv_pcifront_write_config(hpdev, - PCI_BASE_ADDRESS_0 + (4 * i), - 4, (u32)(high_base >> 32)); - high_base += bar_size; - } else { - if (low_size != bar_size) - continue; - _hv_pcifront_write_config(hpdev, - PCI_BASE_ADDRESS_0 + (4 * i), - 4, - (u32)(low_base & 0xffffff00)); - low_base += bar_size; - } - } - if (high_size <= 1 && low_size <= 1) { - /* Set the memory enable bit. */ - _hv_pcifront_read_config(hpdev, PCI_COMMAND, 2, - &command); - command |= PCI_COMMAND_MEMORY; - _hv_pcifront_write_config(hpdev, PCI_COMMAND, 2, - command); - break; - } - } - - high_size >>= 1; - low_size >>= 1; - } while (high_size || low_size); - - spin_unlock_irqrestore(&hbus->device_list_lock, flags); -} - -/** - * create_root_hv_pci_bus() - Expose a new root PCI bus - * @hbus: Root PCI bus, as understood by this driver - * - * Return: 0 on success, -errno on failure - */ -static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus) -{ - /* Register the device */ - hbus->pci_bus = pci_create_root_bus(&hbus->hdev->device, - 0, /* bus number is always zero */ - &hv_pcifront_ops, - &hbus->sysdata, - &hbus->resources_for_children); - if (!hbus->pci_bus) - return -ENODEV; - - hbus->pci_bus->msi = &hbus->msi_chip; - hbus->pci_bus->msi->dev = &hbus->hdev->device; - - pci_lock_rescan_remove(); - pci_scan_child_bus(hbus->pci_bus); - pci_bus_assign_resources(hbus->pci_bus); - pci_bus_add_devices(hbus->pci_bus); - pci_unlock_rescan_remove(); - hbus->state = hv_pcibus_installed; - return 0; -} - -struct q_res_req_compl { - struct completion host_event; - struct hv_pci_dev *hpdev; -}; - -/** - * q_resource_requirements() - Query Resource Requirements - * @context: The completion context. - * @resp: The response that came from the host. - * @resp_packet_size: The size in bytes of resp. - * - * This function is invoked on completion of a Query Resource - * Requirements packet. - */ -static void q_resource_requirements(void *context, struct pci_response *resp, - int resp_packet_size) -{ - struct q_res_req_compl *completion = context; - struct pci_q_res_req_response *q_res_req = - (struct pci_q_res_req_response *)resp; - int i; - - if (resp->status < 0) { - dev_err(&completion->hpdev->hbus->hdev->device, - "query resource requirements failed: %x\n", - resp->status); - } else { - for (i = 0; i < 6; i++) { - completion->hpdev->probed_bar[i] = - q_res_req->probed_bar[i]; - } - } - - complete(&completion->host_event); -} - -/** - * new_pcichild_device() - Create a new child device - * @hbus: The internal struct tracking this root PCI bus. - * @desc: The information supplied so far from the host - * about the device. - * - * This function creates the tracking structure for a new child - * device and kicks off the process of figuring out what it is. - * - * Return: Pointer to the new tracking struct - */ -static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus, - struct pci_function_description *desc) -{ - struct hv_pci_dev *hpdev; - struct pci_child_message *res_req; - struct q_res_req_compl comp_pkt; - struct { - struct pci_packet init_packet; - u8 buffer[sizeof(struct pci_child_message)]; - } pkt; - unsigned long flags; - int ret; - - hpdev = kzalloc(sizeof(*hpdev), GFP_ATOMIC); - if (!hpdev) - return NULL; - - hpdev->hbus = hbus; - - memset(&pkt, 0, sizeof(pkt)); - init_completion(&comp_pkt.host_event); - comp_pkt.hpdev = hpdev; - pkt.init_packet.compl_ctxt = &comp_pkt; - pkt.init_packet.completion_func = q_resource_requirements; - res_req = (struct pci_child_message *)&pkt.init_packet.message; - res_req->message_type.type = PCI_QUERY_RESOURCE_REQUIREMENTS; - res_req->wslot.slot = desc->win_slot.slot; - - ret = vmbus_sendpacket(hbus->hdev->channel, res_req, - sizeof(struct pci_child_message), - (unsigned long)&pkt.init_packet, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret) - goto error; - - if (wait_for_response(hbus->hdev, &comp_pkt.host_event)) - goto error; - - hpdev->desc = *desc; - refcount_set(&hpdev->refs, 1); - get_pcichild(hpdev); - spin_lock_irqsave(&hbus->device_list_lock, flags); - - list_add_tail(&hpdev->list_entry, &hbus->children); - spin_unlock_irqrestore(&hbus->device_list_lock, flags); - return hpdev; - -error: - kfree(hpdev); - return NULL; -} - -/** - * get_pcichild_wslot() - Find device from slot - * @hbus: Root PCI bus, as understood by this driver - * @wslot: Location on the bus - * - * This function looks up a PCI device and returns the internal - * representation of it. It acquires a reference on it, so that - * the device won't be deleted while somebody is using it. The - * caller is responsible for calling put_pcichild() to release - * this reference. - * - * Return: Internal representation of a PCI device - */ -static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus, - u32 wslot) -{ - unsigned long flags; - struct hv_pci_dev *iter, *hpdev = NULL; - - spin_lock_irqsave(&hbus->device_list_lock, flags); - list_for_each_entry(iter, &hbus->children, list_entry) { - if (iter->desc.win_slot.slot == wslot) { - hpdev = iter; - get_pcichild(hpdev); - break; - } - } - spin_unlock_irqrestore(&hbus->device_list_lock, flags); - - return hpdev; -} - -/** - * pci_devices_present_work() - Handle new list of child devices - * @work: Work struct embedded in struct hv_dr_work - * - * "Bus Relations" is the Windows term for "children of this - * bus." The terminology is preserved here for people trying to - * debug the interaction between Hyper-V and Linux. This - * function is called when the parent partition reports a list - * of functions that should be observed under this PCI Express - * port (bus). - * - * This function updates the list, and must tolerate being - * called multiple times with the same information. The typical - * number of child devices is one, with very atypical cases - * involving three or four, so the algorithms used here can be - * simple and inefficient. - * - * It must also treat the omission of a previously observed device as - * notification that the device no longer exists. - * - * Note that this function is serialized with hv_eject_device_work(), - * because both are pushed to the ordered workqueue hbus->wq. - */ -static void pci_devices_present_work(struct work_struct *work) -{ - u32 child_no; - bool found; - struct pci_function_description *new_desc; - struct hv_pci_dev *hpdev; - struct hv_pcibus_device *hbus; - struct list_head removed; - struct hv_dr_work *dr_wrk; - struct hv_dr_state *dr = NULL; - unsigned long flags; - - dr_wrk = container_of(work, struct hv_dr_work, wrk); - hbus = dr_wrk->bus; - kfree(dr_wrk); - - INIT_LIST_HEAD(&removed); - - /* Pull this off the queue and process it if it was the last one. */ - spin_lock_irqsave(&hbus->device_list_lock, flags); - while (!list_empty(&hbus->dr_list)) { - dr = list_first_entry(&hbus->dr_list, struct hv_dr_state, - list_entry); - list_del(&dr->list_entry); - - /* Throw this away if the list still has stuff in it. */ - if (!list_empty(&hbus->dr_list)) { - kfree(dr); - continue; - } - } - spin_unlock_irqrestore(&hbus->device_list_lock, flags); - - if (!dr) { - put_hvpcibus(hbus); - return; - } - - /* First, mark all existing children as reported missing. */ - spin_lock_irqsave(&hbus->device_list_lock, flags); - list_for_each_entry(hpdev, &hbus->children, list_entry) { - hpdev->reported_missing = true; - } - spin_unlock_irqrestore(&hbus->device_list_lock, flags); - - /* Next, add back any reported devices. */ - for (child_no = 0; child_no < dr->device_count; child_no++) { - found = false; - new_desc = &dr->func[child_no]; - - spin_lock_irqsave(&hbus->device_list_lock, flags); - list_for_each_entry(hpdev, &hbus->children, list_entry) { - if ((hpdev->desc.win_slot.slot == new_desc->win_slot.slot) && - (hpdev->desc.v_id == new_desc->v_id) && - (hpdev->desc.d_id == new_desc->d_id) && - (hpdev->desc.ser == new_desc->ser)) { - hpdev->reported_missing = false; - found = true; - } - } - spin_unlock_irqrestore(&hbus->device_list_lock, flags); - - if (!found) { - hpdev = new_pcichild_device(hbus, new_desc); - if (!hpdev) - dev_err(&hbus->hdev->device, - "couldn't record a child device.\n"); - } - } - - /* Move missing children to a list on the stack. */ - spin_lock_irqsave(&hbus->device_list_lock, flags); - do { - found = false; - list_for_each_entry(hpdev, &hbus->children, list_entry) { - if (hpdev->reported_missing) { - found = true; - put_pcichild(hpdev); - list_move_tail(&hpdev->list_entry, &removed); - break; - } - } - } while (found); - spin_unlock_irqrestore(&hbus->device_list_lock, flags); - - /* Delete everything that should no longer exist. */ - while (!list_empty(&removed)) { - hpdev = list_first_entry(&removed, struct hv_pci_dev, - list_entry); - list_del(&hpdev->list_entry); - put_pcichild(hpdev); - } - - switch (hbus->state) { - case hv_pcibus_installed: - /* - * Tell the core to rescan bus - * because there may have been changes. - */ - pci_lock_rescan_remove(); - pci_scan_child_bus(hbus->pci_bus); - pci_unlock_rescan_remove(); - break; - - case hv_pcibus_init: - case hv_pcibus_probed: - survey_child_resources(hbus); - break; - - default: - break; - } - - put_hvpcibus(hbus); - kfree(dr); -} - -/** - * hv_pci_devices_present() - Handles list of new children - * @hbus: Root PCI bus, as understood by this driver - * @relations: Packet from host listing children - * - * This function is invoked whenever a new list of devices for - * this bus appears. - */ -static void hv_pci_devices_present(struct hv_pcibus_device *hbus, - struct pci_bus_relations *relations) -{ - struct hv_dr_state *dr; - struct hv_dr_work *dr_wrk; - unsigned long flags; - bool pending_dr; - - dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT); - if (!dr_wrk) - return; - - dr = kzalloc(offsetof(struct hv_dr_state, func) + - (sizeof(struct pci_function_description) * - (relations->device_count)), GFP_NOWAIT); - if (!dr) { - kfree(dr_wrk); - return; - } - - INIT_WORK(&dr_wrk->wrk, pci_devices_present_work); - dr_wrk->bus = hbus; - dr->device_count = relations->device_count; - if (dr->device_count != 0) { - memcpy(dr->func, relations->func, - sizeof(struct pci_function_description) * - dr->device_count); - } - - spin_lock_irqsave(&hbus->device_list_lock, flags); - /* - * If pending_dr is true, we have already queued a work, - * which will see the new dr. Otherwise, we need to - * queue a new work. - */ - pending_dr = !list_empty(&hbus->dr_list); - list_add_tail(&dr->list_entry, &hbus->dr_list); - spin_unlock_irqrestore(&hbus->device_list_lock, flags); - - if (pending_dr) { - kfree(dr_wrk); - } else { - get_hvpcibus(hbus); - queue_work(hbus->wq, &dr_wrk->wrk); - } -} - -/** - * hv_eject_device_work() - Asynchronously handles ejection - * @work: Work struct embedded in internal device struct - * - * This function handles ejecting a device. Windows will - * attempt to gracefully eject a device, waiting 60 seconds to - * hear back from the guest OS that this completed successfully. - * If this timer expires, the device will be forcibly removed. - */ -static void hv_eject_device_work(struct work_struct *work) -{ - struct pci_eject_response *ejct_pkt; - struct hv_pci_dev *hpdev; - struct pci_dev *pdev; - unsigned long flags; - int wslot; - struct { - struct pci_packet pkt; - u8 buffer[sizeof(struct pci_eject_response)]; - } ctxt; - - hpdev = container_of(work, struct hv_pci_dev, wrk); - - WARN_ON(hpdev->state != hv_pcichild_ejecting); - - /* - * Ejection can come before or after the PCI bus has been set up, so - * attempt to find it and tear down the bus state, if it exists. This - * must be done without constructs like pci_domain_nr(hbus->pci_bus) - * because hbus->pci_bus may not exist yet. - */ - wslot = wslot_to_devfn(hpdev->desc.win_slot.slot); - pdev = pci_get_domain_bus_and_slot(hpdev->hbus->sysdata.domain, 0, - wslot); - if (pdev) { - pci_lock_rescan_remove(); - pci_stop_and_remove_bus_device(pdev); - pci_dev_put(pdev); - pci_unlock_rescan_remove(); - } - - spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags); - list_del(&hpdev->list_entry); - spin_unlock_irqrestore(&hpdev->hbus->device_list_lock, flags); - - memset(&ctxt, 0, sizeof(ctxt)); - ejct_pkt = (struct pci_eject_response *)&ctxt.pkt.message; - ejct_pkt->message_type.type = PCI_EJECTION_COMPLETE; - ejct_pkt->wslot.slot = hpdev->desc.win_slot.slot; - vmbus_sendpacket(hpdev->hbus->hdev->channel, ejct_pkt, - sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt, - VM_PKT_DATA_INBAND, 0); - - put_pcichild(hpdev); - put_pcichild(hpdev); - put_hvpcibus(hpdev->hbus); -} - -/** - * hv_pci_eject_device() - Handles device ejection - * @hpdev: Internal device tracking struct - * - * This function is invoked when an ejection packet arrives. It - * just schedules work so that we don't re-enter the packet - * delivery code handling the ejection. - */ -static void hv_pci_eject_device(struct hv_pci_dev *hpdev) -{ - hpdev->state = hv_pcichild_ejecting; - get_pcichild(hpdev); - INIT_WORK(&hpdev->wrk, hv_eject_device_work); - get_hvpcibus(hpdev->hbus); - queue_work(hpdev->hbus->wq, &hpdev->wrk); -} - -/** - * hv_pci_onchannelcallback() - Handles incoming packets - * @context: Internal bus tracking struct - * - * This function is invoked whenever the host sends a packet to - * this channel (which is private to this root PCI bus). - */ -static void hv_pci_onchannelcallback(void *context) -{ - const int packet_size = 0x100; - int ret; - struct hv_pcibus_device *hbus = context; - u32 bytes_recvd; - u64 req_id; - struct vmpacket_descriptor *desc; - unsigned char *buffer; - int bufferlen = packet_size; - struct pci_packet *comp_packet; - struct pci_response *response; - struct pci_incoming_message *new_message; - struct pci_bus_relations *bus_rel; - struct pci_dev_incoming *dev_message; - struct hv_pci_dev *hpdev; - - buffer = kmalloc(bufferlen, GFP_ATOMIC); - if (!buffer) - return; - - while (1) { - ret = vmbus_recvpacket_raw(hbus->hdev->channel, buffer, - bufferlen, &bytes_recvd, &req_id); - - if (ret == -ENOBUFS) { - kfree(buffer); - /* Handle large packet */ - bufferlen = bytes_recvd; - buffer = kmalloc(bytes_recvd, GFP_ATOMIC); - if (!buffer) - return; - continue; - } - - /* Zero length indicates there are no more packets. */ - if (ret || !bytes_recvd) - break; - - /* - * All incoming packets must be at least as large as a - * response. - */ - if (bytes_recvd <= sizeof(struct pci_response)) - continue; - desc = (struct vmpacket_descriptor *)buffer; - - switch (desc->type) { - case VM_PKT_COMP: - - /* - * The host is trusted, and thus it's safe to interpret - * this transaction ID as a pointer. - */ - comp_packet = (struct pci_packet *)req_id; - response = (struct pci_response *)buffer; - comp_packet->completion_func(comp_packet->compl_ctxt, - response, - bytes_recvd); - break; - - case VM_PKT_DATA_INBAND: - - new_message = (struct pci_incoming_message *)buffer; - switch (new_message->message_type.type) { - case PCI_BUS_RELATIONS: - - bus_rel = (struct pci_bus_relations *)buffer; - if (bytes_recvd < - offsetof(struct pci_bus_relations, func) + - (sizeof(struct pci_function_description) * - (bus_rel->device_count))) { - dev_err(&hbus->hdev->device, - "bus relations too small\n"); - break; - } - - hv_pci_devices_present(hbus, bus_rel); - break; - - case PCI_EJECT: - - dev_message = (struct pci_dev_incoming *)buffer; - hpdev = get_pcichild_wslot(hbus, - dev_message->wslot.slot); - if (hpdev) { - hv_pci_eject_device(hpdev); - put_pcichild(hpdev); - } - break; - - default: - dev_warn(&hbus->hdev->device, - "Unimplemented protocol message %x\n", - new_message->message_type.type); - break; - } - break; - - default: - dev_err(&hbus->hdev->device, - "unhandled packet type %d, tid %llx len %d\n", - desc->type, req_id, bytes_recvd); - break; - } - } - - kfree(buffer); -} - -/** - * hv_pci_protocol_negotiation() - Set up protocol - * @hdev: VMBus's tracking struct for this root PCI bus - * - * This driver is intended to support running on Windows 10 - * (server) and later versions. It will not run on earlier - * versions, as they assume that many of the operations which - * Linux needs accomplished with a spinlock held were done via - * asynchronous messaging via VMBus. Windows 10 increases the - * surface area of PCI emulation so that these actions can take - * place by suspending a virtual processor for their duration. - * - * This function negotiates the channel protocol version, - * failing if the host doesn't support the necessary protocol - * level. - */ -static int hv_pci_protocol_negotiation(struct hv_device *hdev) -{ - struct pci_version_request *version_req; - struct hv_pci_compl comp_pkt; - struct pci_packet *pkt; - int ret; - int i; - - /* - * Initiate the handshake with the host and negotiate - * a version that the host can support. We start with the - * highest version number and go down if the host cannot - * support it. - */ - pkt = kzalloc(sizeof(*pkt) + sizeof(*version_req), GFP_KERNEL); - if (!pkt) - return -ENOMEM; - - init_completion(&comp_pkt.host_event); - pkt->completion_func = hv_pci_generic_compl; - pkt->compl_ctxt = &comp_pkt; - version_req = (struct pci_version_request *)&pkt->message; - version_req->message_type.type = PCI_QUERY_PROTOCOL_VERSION; - - for (i = 0; i < ARRAY_SIZE(pci_protocol_versions); i++) { - version_req->protocol_version = pci_protocol_versions[i]; - ret = vmbus_sendpacket(hdev->channel, version_req, - sizeof(struct pci_version_request), - (unsigned long)pkt, VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (!ret) - ret = wait_for_response(hdev, &comp_pkt.host_event); - - if (ret) { - dev_err(&hdev->device, - "PCI Pass-through VSP failed to request version: %d", - ret); - goto exit; - } - - if (comp_pkt.completion_status >= 0) { - pci_protocol_version = pci_protocol_versions[i]; - dev_info(&hdev->device, - "PCI VMBus probing: Using version %#x\n", - pci_protocol_version); - goto exit; - } - - if (comp_pkt.completion_status != STATUS_REVISION_MISMATCH) { - dev_err(&hdev->device, - "PCI Pass-through VSP failed version request: %#x", - comp_pkt.completion_status); - ret = -EPROTO; - goto exit; - } - - reinit_completion(&comp_pkt.host_event); - } - - dev_err(&hdev->device, - "PCI pass-through VSP failed to find supported version"); - ret = -EPROTO; - -exit: - kfree(pkt); - return ret; -} - -/** - * hv_pci_free_bridge_windows() - Release memory regions for the - * bus - * @hbus: Root PCI bus, as understood by this driver - */ -static void hv_pci_free_bridge_windows(struct hv_pcibus_device *hbus) -{ - /* - * Set the resources back to the way they looked when they - * were allocated by setting IORESOURCE_BUSY again. - */ - - if (hbus->low_mmio_space && hbus->low_mmio_res) { - hbus->low_mmio_res->flags |= IORESOURCE_BUSY; - vmbus_free_mmio(hbus->low_mmio_res->start, - resource_size(hbus->low_mmio_res)); - } - - if (hbus->high_mmio_space && hbus->high_mmio_res) { - hbus->high_mmio_res->flags |= IORESOURCE_BUSY; - vmbus_free_mmio(hbus->high_mmio_res->start, - resource_size(hbus->high_mmio_res)); - } -} - -/** - * hv_pci_allocate_bridge_windows() - Allocate memory regions - * for the bus - * @hbus: Root PCI bus, as understood by this driver - * - * This function calls vmbus_allocate_mmio(), which is itself a - * bit of a compromise. Ideally, we might change the pnp layer - * in the kernel such that it comprehends either PCI devices - * which are "grandchildren of ACPI," with some intermediate bus - * node (in this case, VMBus) or change it such that it - * understands VMBus. The pnp layer, however, has been declared - * deprecated, and not subject to change. - * - * The workaround, implemented here, is to ask VMBus to allocate - * MMIO space for this bus. VMBus itself knows which ranges are - * appropriate by looking at its own ACPI objects. Then, after - * these ranges are claimed, they're modified to look like they - * would have looked if the ACPI and pnp code had allocated - * bridge windows. These descriptors have to exist in this form - * in order to satisfy the code which will get invoked when the - * endpoint PCI function driver calls request_mem_region() or - * request_mem_region_exclusive(). - * - * Return: 0 on success, -errno on failure - */ -static int hv_pci_allocate_bridge_windows(struct hv_pcibus_device *hbus) -{ - resource_size_t align; - int ret; - - if (hbus->low_mmio_space) { - align = 1ULL << (63 - __builtin_clzll(hbus->low_mmio_space)); - ret = vmbus_allocate_mmio(&hbus->low_mmio_res, hbus->hdev, 0, - (u64)(u32)0xffffffff, - hbus->low_mmio_space, - align, false); - if (ret) { - dev_err(&hbus->hdev->device, - "Need %#llx of low MMIO space. Consider reconfiguring the VM.\n", - hbus->low_mmio_space); - return ret; - } - - /* Modify this resource to become a bridge window. */ - hbus->low_mmio_res->flags |= IORESOURCE_WINDOW; - hbus->low_mmio_res->flags &= ~IORESOURCE_BUSY; - pci_add_resource(&hbus->resources_for_children, - hbus->low_mmio_res); - } - - if (hbus->high_mmio_space) { - align = 1ULL << (63 - __builtin_clzll(hbus->high_mmio_space)); - ret = vmbus_allocate_mmio(&hbus->high_mmio_res, hbus->hdev, - 0x100000000, -1, - hbus->high_mmio_space, align, - false); - if (ret) { - dev_err(&hbus->hdev->device, - "Need %#llx of high MMIO space. Consider reconfiguring the VM.\n", - hbus->high_mmio_space); - goto release_low_mmio; - } - - /* Modify this resource to become a bridge window. */ - hbus->high_mmio_res->flags |= IORESOURCE_WINDOW; - hbus->high_mmio_res->flags &= ~IORESOURCE_BUSY; - pci_add_resource(&hbus->resources_for_children, - hbus->high_mmio_res); - } - - return 0; - -release_low_mmio: - if (hbus->low_mmio_res) { - vmbus_free_mmio(hbus->low_mmio_res->start, - resource_size(hbus->low_mmio_res)); - } - - return ret; -} - -/** - * hv_allocate_config_window() - Find MMIO space for PCI Config - * @hbus: Root PCI bus, as understood by this driver - * - * This function claims memory-mapped I/O space for accessing - * configuration space for the functions on this bus. - * - * Return: 0 on success, -errno on failure - */ -static int hv_allocate_config_window(struct hv_pcibus_device *hbus) -{ - int ret; - - /* - * Set up a region of MMIO space to use for accessing configuration - * space. - */ - ret = vmbus_allocate_mmio(&hbus->mem_config, hbus->hdev, 0, -1, - PCI_CONFIG_MMIO_LENGTH, 0x1000, false); - if (ret) - return ret; - - /* - * vmbus_allocate_mmio() gets used for allocating both device endpoint - * resource claims (those which cannot be overlapped) and the ranges - * which are valid for the children of this bus, which are intended - * to be overlapped by those children. Set the flag on this claim - * meaning that this region can't be overlapped. - */ - - hbus->mem_config->flags |= IORESOURCE_BUSY; - - return 0; -} - -static void hv_free_config_window(struct hv_pcibus_device *hbus) -{ - vmbus_free_mmio(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH); -} - -/** - * hv_pci_enter_d0() - Bring the "bus" into the D0 power state - * @hdev: VMBus's tracking struct for this root PCI bus - * - * Return: 0 on success, -errno on failure - */ -static int hv_pci_enter_d0(struct hv_device *hdev) -{ - struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); - struct pci_bus_d0_entry *d0_entry; - struct hv_pci_compl comp_pkt; - struct pci_packet *pkt; - int ret; - - /* - * Tell the host that the bus is ready to use, and moved into the - * powered-on state. This includes telling the host which region - * of memory-mapped I/O space has been chosen for configuration space - * access. - */ - pkt = kzalloc(sizeof(*pkt) + sizeof(*d0_entry), GFP_KERNEL); - if (!pkt) - return -ENOMEM; - - init_completion(&comp_pkt.host_event); - pkt->completion_func = hv_pci_generic_compl; - pkt->compl_ctxt = &comp_pkt; - d0_entry = (struct pci_bus_d0_entry *)&pkt->message; - d0_entry->message_type.type = PCI_BUS_D0ENTRY; - d0_entry->mmio_base = hbus->mem_config->start; - - ret = vmbus_sendpacket(hdev->channel, d0_entry, sizeof(*d0_entry), - (unsigned long)pkt, VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (!ret) - ret = wait_for_response(hdev, &comp_pkt.host_event); - - if (ret) - goto exit; - - if (comp_pkt.completion_status < 0) { - dev_err(&hdev->device, - "PCI Pass-through VSP failed D0 Entry with status %x\n", - comp_pkt.completion_status); - ret = -EPROTO; - goto exit; - } - - ret = 0; - -exit: - kfree(pkt); - return ret; -} - -/** - * hv_pci_query_relations() - Ask host to send list of child - * devices - * @hdev: VMBus's tracking struct for this root PCI bus - * - * Return: 0 on success, -errno on failure - */ -static int hv_pci_query_relations(struct hv_device *hdev) -{ - struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); - struct pci_message message; - struct completion comp; - int ret; - - /* Ask the host to send along the list of child devices */ - init_completion(&comp); - if (cmpxchg(&hbus->survey_event, NULL, &comp)) - return -ENOTEMPTY; - - memset(&message, 0, sizeof(message)); - message.type = PCI_QUERY_BUS_RELATIONS; - - ret = vmbus_sendpacket(hdev->channel, &message, sizeof(message), - 0, VM_PKT_DATA_INBAND, 0); - if (!ret) - ret = wait_for_response(hdev, &comp); - - return ret; -} - -/** - * hv_send_resources_allocated() - Report local resource choices - * @hdev: VMBus's tracking struct for this root PCI bus - * - * The host OS is expecting to be sent a request as a message - * which contains all the resources that the device will use. - * The response contains those same resources, "translated" - * which is to say, the values which should be used by the - * hardware, when it delivers an interrupt. (MMIO resources are - * used in local terms.) This is nice for Windows, and lines up - * with the FDO/PDO split, which doesn't exist in Linux. Linux - * is deeply expecting to scan an emulated PCI configuration - * space. So this message is sent here only to drive the state - * machine on the host forward. - * - * Return: 0 on success, -errno on failure - */ -static int hv_send_resources_allocated(struct hv_device *hdev) -{ - struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); - struct pci_resources_assigned *res_assigned; - struct pci_resources_assigned2 *res_assigned2; - struct hv_pci_compl comp_pkt; - struct hv_pci_dev *hpdev; - struct pci_packet *pkt; - size_t size_res; - u32 wslot; - int ret; - - size_res = (pci_protocol_version < PCI_PROTOCOL_VERSION_1_2) - ? sizeof(*res_assigned) : sizeof(*res_assigned2); - - pkt = kmalloc(sizeof(*pkt) + size_res, GFP_KERNEL); - if (!pkt) - return -ENOMEM; - - ret = 0; - - for (wslot = 0; wslot < 256; wslot++) { - hpdev = get_pcichild_wslot(hbus, wslot); - if (!hpdev) - continue; - - memset(pkt, 0, sizeof(*pkt) + size_res); - init_completion(&comp_pkt.host_event); - pkt->completion_func = hv_pci_generic_compl; - pkt->compl_ctxt = &comp_pkt; - - if (pci_protocol_version < PCI_PROTOCOL_VERSION_1_2) { - res_assigned = - (struct pci_resources_assigned *)&pkt->message; - res_assigned->message_type.type = - PCI_RESOURCES_ASSIGNED; - res_assigned->wslot.slot = hpdev->desc.win_slot.slot; - } else { - res_assigned2 = - (struct pci_resources_assigned2 *)&pkt->message; - res_assigned2->message_type.type = - PCI_RESOURCES_ASSIGNED2; - res_assigned2->wslot.slot = hpdev->desc.win_slot.slot; - } - put_pcichild(hpdev); - - ret = vmbus_sendpacket(hdev->channel, &pkt->message, - size_res, (unsigned long)pkt, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (!ret) - ret = wait_for_response(hdev, &comp_pkt.host_event); - if (ret) - break; - - if (comp_pkt.completion_status < 0) { - ret = -EPROTO; - dev_err(&hdev->device, - "resource allocated returned 0x%x", - comp_pkt.completion_status); - break; - } - } - - kfree(pkt); - return ret; -} - -/** - * hv_send_resources_released() - Report local resources - * released - * @hdev: VMBus's tracking struct for this root PCI bus - * - * Return: 0 on success, -errno on failure - */ -static int hv_send_resources_released(struct hv_device *hdev) -{ - struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); - struct pci_child_message pkt; - struct hv_pci_dev *hpdev; - u32 wslot; - int ret; - - for (wslot = 0; wslot < 256; wslot++) { - hpdev = get_pcichild_wslot(hbus, wslot); - if (!hpdev) - continue; - - memset(&pkt, 0, sizeof(pkt)); - pkt.message_type.type = PCI_RESOURCES_RELEASED; - pkt.wslot.slot = hpdev->desc.win_slot.slot; - - put_pcichild(hpdev); - - ret = vmbus_sendpacket(hdev->channel, &pkt, sizeof(pkt), 0, - VM_PKT_DATA_INBAND, 0); - if (ret) - return ret; - } - - return 0; -} - -static void get_hvpcibus(struct hv_pcibus_device *hbus) -{ - refcount_inc(&hbus->remove_lock); -} - -static void put_hvpcibus(struct hv_pcibus_device *hbus) -{ - if (refcount_dec_and_test(&hbus->remove_lock)) - complete(&hbus->remove_event); -} - -/** - * hv_pci_probe() - New VMBus channel probe, for a root PCI bus - * @hdev: VMBus's tracking struct for this root PCI bus - * @dev_id: Identifies the device itself - * - * Return: 0 on success, -errno on failure - */ -static int hv_pci_probe(struct hv_device *hdev, - const struct hv_vmbus_device_id *dev_id) -{ - struct hv_pcibus_device *hbus; - int ret; - - /* - * hv_pcibus_device contains the hypercall arguments for retargeting in - * hv_irq_unmask(). Those must not cross a page boundary. - */ - BUILD_BUG_ON(sizeof(*hbus) > PAGE_SIZE); - - hbus = (struct hv_pcibus_device *)get_zeroed_page(GFP_KERNEL); - if (!hbus) - return -ENOMEM; - hbus->state = hv_pcibus_init; - - /* - * The PCI bus "domain" is what is called "segment" in ACPI and - * other specs. Pull it from the instance ID, to get something - * unique. Bytes 8 and 9 are what is used in Windows guests, so - * do the same thing for consistency. Note that, since this code - * only runs in a Hyper-V VM, Hyper-V can (and does) guarantee - * that (1) the only domain in use for something that looks like - * a physical PCI bus (which is actually emulated by the - * hypervisor) is domain 0 and (2) there will be no overlap - * between domains derived from these instance IDs in the same - * VM. - */ - hbus->sysdata.domain = hdev->dev_instance.b[9] | - hdev->dev_instance.b[8] << 8; - - hbus->hdev = hdev; - refcount_set(&hbus->remove_lock, 1); - INIT_LIST_HEAD(&hbus->children); - INIT_LIST_HEAD(&hbus->dr_list); - INIT_LIST_HEAD(&hbus->resources_for_children); - spin_lock_init(&hbus->config_lock); - spin_lock_init(&hbus->device_list_lock); - spin_lock_init(&hbus->retarget_msi_interrupt_lock); - init_completion(&hbus->remove_event); - hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0, - hbus->sysdata.domain); - if (!hbus->wq) { - ret = -ENOMEM; - goto free_bus; - } - - ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0, - hv_pci_onchannelcallback, hbus); - if (ret) - goto destroy_wq; - - hv_set_drvdata(hdev, hbus); - - ret = hv_pci_protocol_negotiation(hdev); - if (ret) - goto close; - - ret = hv_allocate_config_window(hbus); - if (ret) - goto close; - - hbus->cfg_addr = ioremap(hbus->mem_config->start, - PCI_CONFIG_MMIO_LENGTH); - if (!hbus->cfg_addr) { - dev_err(&hdev->device, - "Unable to map a virtual address for config space\n"); - ret = -ENOMEM; - goto free_config; - } - - hbus->sysdata.fwnode = irq_domain_alloc_fwnode(hbus); - if (!hbus->sysdata.fwnode) { - ret = -ENOMEM; - goto unmap; - } - - ret = hv_pcie_init_irq_domain(hbus); - if (ret) - goto free_fwnode; - - ret = hv_pci_query_relations(hdev); - if (ret) - goto free_irq_domain; - - ret = hv_pci_enter_d0(hdev); - if (ret) - goto free_irq_domain; - - ret = hv_pci_allocate_bridge_windows(hbus); - if (ret) - goto free_irq_domain; - - ret = hv_send_resources_allocated(hdev); - if (ret) - goto free_windows; - - prepopulate_bars(hbus); - - hbus->state = hv_pcibus_probed; - - ret = create_root_hv_pci_bus(hbus); - if (ret) - goto free_windows; - - return 0; - -free_windows: - hv_pci_free_bridge_windows(hbus); -free_irq_domain: - irq_domain_remove(hbus->irq_domain); -free_fwnode: - irq_domain_free_fwnode(hbus->sysdata.fwnode); -unmap: - iounmap(hbus->cfg_addr); -free_config: - hv_free_config_window(hbus); -close: - vmbus_close(hdev->channel); -destroy_wq: - destroy_workqueue(hbus->wq); -free_bus: - free_page((unsigned long)hbus); - return ret; -} - -static void hv_pci_bus_exit(struct hv_device *hdev) -{ - struct hv_pcibus_device *hbus = hv_get_drvdata(hdev); - struct { - struct pci_packet teardown_packet; - u8 buffer[sizeof(struct pci_message)]; - } pkt; - struct pci_bus_relations relations; - struct hv_pci_compl comp_pkt; - int ret; - - /* - * After the host sends the RESCIND_CHANNEL message, it doesn't - * access the per-channel ringbuffer any longer. - */ - if (hdev->channel->rescind) - return; - - /* Delete any children which might still exist. */ - memset(&relations, 0, sizeof(relations)); - hv_pci_devices_present(hbus, &relations); - - ret = hv_send_resources_released(hdev); - if (ret) - dev_err(&hdev->device, - "Couldn't send resources released packet(s)\n"); - - memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet)); - init_completion(&comp_pkt.host_event); - pkt.teardown_packet.completion_func = hv_pci_generic_compl; - pkt.teardown_packet.compl_ctxt = &comp_pkt; - pkt.teardown_packet.message[0].type = PCI_BUS_D0EXIT; - - ret = vmbus_sendpacket(hdev->channel, &pkt.teardown_packet.message, - sizeof(struct pci_message), - (unsigned long)&pkt.teardown_packet, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (!ret) - wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ); -} - -/** - * hv_pci_remove() - Remove routine for this VMBus channel - * @hdev: VMBus's tracking struct for this root PCI bus - * - * Return: 0 on success, -errno on failure - */ -static int hv_pci_remove(struct hv_device *hdev) -{ - struct hv_pcibus_device *hbus; - - hbus = hv_get_drvdata(hdev); - if (hbus->state == hv_pcibus_installed) { - /* Remove the bus from PCI's point of view. */ - pci_lock_rescan_remove(); - pci_stop_root_bus(hbus->pci_bus); - pci_remove_root_bus(hbus->pci_bus); - pci_unlock_rescan_remove(); - hbus->state = hv_pcibus_removed; - } - - hv_pci_bus_exit(hdev); - - vmbus_close(hdev->channel); - - iounmap(hbus->cfg_addr); - hv_free_config_window(hbus); - pci_free_resource_list(&hbus->resources_for_children); - hv_pci_free_bridge_windows(hbus); - irq_domain_remove(hbus->irq_domain); - irq_domain_free_fwnode(hbus->sysdata.fwnode); - put_hvpcibus(hbus); - wait_for_completion(&hbus->remove_event); - destroy_workqueue(hbus->wq); - free_page((unsigned long)hbus); - return 0; -} - -static const struct hv_vmbus_device_id hv_pci_id_table[] = { - /* PCI Pass-through Class ID */ - /* 44C4F61D-4444-4400-9D52-802E27EDE19F */ - { HV_PCIE_GUID, }, - { }, -}; - -MODULE_DEVICE_TABLE(vmbus, hv_pci_id_table); - -static struct hv_driver hv_pci_drv = { - .name = "hv_pci", - .id_table = hv_pci_id_table, - .probe = hv_pci_probe, - .remove = hv_pci_remove, -}; - -static void __exit exit_hv_pci_drv(void) -{ - vmbus_driver_unregister(&hv_pci_drv); -} - -static int __init init_hv_pci_drv(void) -{ - return vmbus_driver_register(&hv_pci_drv); -} - -module_init(init_hv_pci_drv); -module_exit(exit_hv_pci_drv); - -MODULE_DESCRIPTION("Hyper-V PCI"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c deleted file mode 100644 index 23e270839e6a..000000000000 --- a/drivers/pci/host/pci-mvebu.c +++ /dev/null @@ -1,1313 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe driver for Marvell Armada 370 and Armada XP SoCs - * - * Author: Thomas Petazzoni - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -/* - * PCIe unit register offsets. - */ -#define PCIE_DEV_ID_OFF 0x0000 -#define PCIE_CMD_OFF 0x0004 -#define PCIE_DEV_REV_OFF 0x0008 -#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) -#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) -#define PCIE_CAP_PCIEXP 0x0060 -#define PCIE_HEADER_LOG_4_OFF 0x0128 -#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) -#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) -#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) -#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4)) -#define PCIE_WIN5_CTRL_OFF 0x1880 -#define PCIE_WIN5_BASE_OFF 0x1884 -#define PCIE_WIN5_REMAP_OFF 0x188c -#define PCIE_CONF_ADDR_OFF 0x18f8 -#define PCIE_CONF_ADDR_EN 0x80000000 -#define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc)) -#define PCIE_CONF_BUS(b) (((b) & 0xff) << 16) -#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11) -#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 8) -#define PCIE_CONF_ADDR(bus, devfn, where) \ - (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ - PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where) | \ - PCIE_CONF_ADDR_EN) -#define PCIE_CONF_DATA_OFF 0x18fc -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 -#define PCIE_CTRL_OFF 0x1a00 -#define PCIE_CTRL_X1_MODE 0x0001 -#define PCIE_STAT_OFF 0x1a04 -#define PCIE_STAT_BUS 0xff00 -#define PCIE_STAT_DEV 0x1f0000 -#define PCIE_STAT_LINK_DOWN BIT(0) -#define PCIE_RC_RTSTA 0x1a14 -#define PCIE_DEBUG_CTRL 0x1a60 -#define PCIE_DEBUG_SOFT_RESET BIT(20) - -enum { - PCISWCAP = PCI_BRIDGE_CONTROL + 2, - PCISWCAP_EXP_LIST_ID = PCISWCAP + PCI_CAP_LIST_ID, - PCISWCAP_EXP_DEVCAP = PCISWCAP + PCI_EXP_DEVCAP, - PCISWCAP_EXP_DEVCTL = PCISWCAP + PCI_EXP_DEVCTL, - PCISWCAP_EXP_LNKCAP = PCISWCAP + PCI_EXP_LNKCAP, - PCISWCAP_EXP_LNKCTL = PCISWCAP + PCI_EXP_LNKCTL, - PCISWCAP_EXP_SLTCAP = PCISWCAP + PCI_EXP_SLTCAP, - PCISWCAP_EXP_SLTCTL = PCISWCAP + PCI_EXP_SLTCTL, - PCISWCAP_EXP_RTCTL = PCISWCAP + PCI_EXP_RTCTL, - PCISWCAP_EXP_RTSTA = PCISWCAP + PCI_EXP_RTSTA, - PCISWCAP_EXP_DEVCAP2 = PCISWCAP + PCI_EXP_DEVCAP2, - PCISWCAP_EXP_DEVCTL2 = PCISWCAP + PCI_EXP_DEVCTL2, - PCISWCAP_EXP_LNKCAP2 = PCISWCAP + PCI_EXP_LNKCAP2, - PCISWCAP_EXP_LNKCTL2 = PCISWCAP + PCI_EXP_LNKCTL2, - PCISWCAP_EXP_SLTCAP2 = PCISWCAP + PCI_EXP_SLTCAP2, - PCISWCAP_EXP_SLTCTL2 = PCISWCAP + PCI_EXP_SLTCTL2, -}; - -/* PCI configuration space of a PCI-to-PCI bridge */ -struct mvebu_sw_pci_bridge { - u16 vendor; - u16 device; - u16 command; - u16 status; - u16 class; - u8 interface; - u8 revision; - u8 bist; - u8 header_type; - u8 latency_timer; - u8 cache_line_size; - u32 bar[2]; - u8 primary_bus; - u8 secondary_bus; - u8 subordinate_bus; - u8 secondary_latency_timer; - u8 iobase; - u8 iolimit; - u16 secondary_status; - u16 membase; - u16 memlimit; - u16 iobaseupper; - u16 iolimitupper; - u32 romaddr; - u8 intline; - u8 intpin; - u16 bridgectrl; - - /* PCI express capability */ - u32 pcie_sltcap; - u16 pcie_devctl; - u16 pcie_rtctl; -}; - -struct mvebu_pcie_port; - -/* Structure representing all PCIe interfaces */ -struct mvebu_pcie { - struct platform_device *pdev; - struct mvebu_pcie_port *ports; - struct msi_controller *msi; - struct resource io; - struct resource realio; - struct resource mem; - struct resource busn; - int nports; -}; - -struct mvebu_pcie_window { - phys_addr_t base; - phys_addr_t remap; - size_t size; -}; - -/* Structure representing one PCIe interface */ -struct mvebu_pcie_port { - char *name; - void __iomem *base; - u32 port; - u32 lane; - int devfn; - unsigned int mem_target; - unsigned int mem_attr; - unsigned int io_target; - unsigned int io_attr; - struct clk *clk; - struct gpio_desc *reset_gpio; - char *reset_name; - struct mvebu_sw_pci_bridge bridge; - struct device_node *dn; - struct mvebu_pcie *pcie; - struct mvebu_pcie_window memwin; - struct mvebu_pcie_window iowin; - u32 saved_pcie_stat; -}; - -static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) -{ - writel(val, port->base + reg); -} - -static inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg) -{ - return readl(port->base + reg); -} - -static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) -{ - return port->io_target != -1 && port->io_attr != -1; -} - -static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) -{ - return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); -} - -static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr) -{ - u32 stat; - - stat = mvebu_readl(port, PCIE_STAT_OFF); - stat &= ~PCIE_STAT_BUS; - stat |= nr << 8; - mvebu_writel(port, stat, PCIE_STAT_OFF); -} - -static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr) -{ - u32 stat; - - stat = mvebu_readl(port, PCIE_STAT_OFF); - stat &= ~PCIE_STAT_DEV; - stat |= nr << 16; - mvebu_writel(port, stat, PCIE_STAT_OFF); -} - -/* - * Setup PCIE BARs and Address Decode Wins: - * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks - * WIN[0-3] -> DRAM bank[0-3] - */ -static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) -{ - const struct mbus_dram_target_info *dram; - u32 size; - int i; - - dram = mv_mbus_dram_info(); - - /* First, disable and clear BARs and windows. */ - for (i = 1; i < 3; i++) { - mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i)); - mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i)); - mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i)); - } - - for (i = 0; i < 5; i++) { - mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i)); - mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i)); - mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i)); - } - - mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF); - mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF); - mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF); - - /* Setup windows for DDR banks. Count total DDR size on the fly. */ - size = 0; - for (i = 0; i < dram->num_cs; i++) { - const struct mbus_dram_window *cs = dram->cs + i; - - mvebu_writel(port, cs->base & 0xffff0000, - PCIE_WIN04_BASE_OFF(i)); - mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i)); - mvebu_writel(port, - ((cs->size - 1) & 0xffff0000) | - (cs->mbus_attr << 8) | - (dram->mbus_dram_target_id << 4) | 1, - PCIE_WIN04_CTRL_OFF(i)); - - size += cs->size; - } - - /* Round up 'size' to the nearest power of two. */ - if ((size & (size - 1)) != 0) - size = 1 << fls(size); - - /* Setup BAR[1] to all DRAM banks. */ - mvebu_writel(port, dram->cs[0].base, PCIE_BAR_LO_OFF(1)); - mvebu_writel(port, 0, PCIE_BAR_HI_OFF(1)); - mvebu_writel(port, ((size - 1) & 0xffff0000) | 1, - PCIE_BAR_CTRL_OFF(1)); -} - -static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) -{ - u32 cmd, mask; - - /* Point PCIe unit MBUS decode windows to DRAM space. */ - mvebu_pcie_setup_wins(port); - - /* Master + slave enable. */ - cmd = mvebu_readl(port, PCIE_CMD_OFF); - cmd |= PCI_COMMAND_IO; - cmd |= PCI_COMMAND_MEMORY; - cmd |= PCI_COMMAND_MASTER; - mvebu_writel(port, cmd, PCIE_CMD_OFF); - - /* Enable interrupt lines A-D. */ - mask = mvebu_readl(port, PCIE_MASK_OFF); - mask |= PCIE_MASK_ENABLE_INTS; - mvebu_writel(port, mask, PCIE_MASK_OFF); -} - -static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, - struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) -{ - void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; - - mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), - PCIE_CONF_ADDR_OFF); - - switch (size) { - case 1: - *val = readb_relaxed(conf_data + (where & 3)); - break; - case 2: - *val = readw_relaxed(conf_data + (where & 2)); - break; - case 4: - *val = readl_relaxed(conf_data); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - -static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, - struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) -{ - void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; - - mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), - PCIE_CONF_ADDR_OFF); - - switch (size) { - case 1: - writeb(val, conf_data + (where & 3)); - break; - case 2: - writew(val, conf_data + (where & 2)); - break; - case 4: - writel(val, conf_data); - break; - default: - return PCIBIOS_BAD_REGISTER_NUMBER; - } - - return PCIBIOS_SUCCESSFUL; -} - -/* - * Remove windows, starting from the largest ones to the smallest - * ones. - */ -static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port, - phys_addr_t base, size_t size) -{ - while (size) { - size_t sz = 1 << (fls(size) - 1); - - mvebu_mbus_del_window(base, sz); - base += sz; - size -= sz; - } -} - -/* - * MBus windows can only have a power of two size, but PCI BARs do not - * have this constraint. Therefore, we have to split the PCI BAR into - * areas each having a power of two size. We start from the largest - * one (i.e highest order bit set in the size). - */ -static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, - unsigned int target, unsigned int attribute, - phys_addr_t base, size_t size, - phys_addr_t remap) -{ - size_t size_mapped = 0; - - while (size) { - size_t sz = 1 << (fls(size) - 1); - int ret; - - ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base, - sz, remap); - if (ret) { - phys_addr_t end = base + sz - 1; - - dev_err(&port->pcie->pdev->dev, - "Could not create MBus window at [mem %pa-%pa]: %d\n", - &base, &end, ret); - mvebu_pcie_del_windows(port, base - size_mapped, - size_mapped); - return; - } - - size -= sz; - size_mapped += sz; - base += sz; - if (remap != MVEBU_MBUS_NO_REMAP) - remap += sz; - } -} - -static void mvebu_pcie_set_window(struct mvebu_pcie_port *port, - unsigned int target, unsigned int attribute, - const struct mvebu_pcie_window *desired, - struct mvebu_pcie_window *cur) -{ - if (desired->base == cur->base && desired->remap == cur->remap && - desired->size == cur->size) - return; - - if (cur->size != 0) { - mvebu_pcie_del_windows(port, cur->base, cur->size); - cur->size = 0; - cur->base = 0; - - /* - * If something tries to change the window while it is enabled - * the change will not be done atomically. That would be - * difficult to do in the general case. - */ - } - - if (desired->size == 0) - return; - - mvebu_pcie_add_windows(port, target, attribute, desired->base, - desired->size, desired->remap); - *cur = *desired; -} - -static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) -{ - struct mvebu_pcie_window desired = {}; - - /* Are the new iobase/iolimit values invalid? */ - if (port->bridge.iolimit < port->bridge.iobase || - port->bridge.iolimitupper < port->bridge.iobaseupper || - !(port->bridge.command & PCI_COMMAND_IO)) { - mvebu_pcie_set_window(port, port->io_target, port->io_attr, - &desired, &port->iowin); - return; - } - - if (!mvebu_has_ioport(port)) { - dev_WARN(&port->pcie->pdev->dev, - "Attempt to set IO when IO is disabled\n"); - return; - } - - /* - * We read the PCI-to-PCI bridge emulated registers, and - * calculate the base address and size of the address decoding - * window to setup, according to the PCI-to-PCI bridge - * specifications. iobase is the bus address, port->iowin_base - * is the CPU address. - */ - desired.remap = ((port->bridge.iobase & 0xF0) << 8) | - (port->bridge.iobaseupper << 16); - desired.base = port->pcie->io.start + desired.remap; - desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | - (port->bridge.iolimitupper << 16)) - - desired.remap) + - 1; - - mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, - &port->iowin); -} - -static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) -{ - struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP}; - - /* Are the new membase/memlimit values invalid? */ - if (port->bridge.memlimit < port->bridge.membase || - !(port->bridge.command & PCI_COMMAND_MEMORY)) { - mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, - &desired, &port->memwin); - return; - } - - /* - * We read the PCI-to-PCI bridge emulated registers, and - * calculate the base address and size of the address decoding - * window to setup, according to the PCI-to-PCI bridge - * specifications. - */ - desired.base = ((port->bridge.membase & 0xFFF0) << 16); - desired.size = (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - - desired.base + 1; - - mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, - &port->memwin); -} - -/* - * Initialize the configuration space of the PCI-to-PCI bridge - * associated with the given PCIe interface. - */ -static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port) -{ - struct mvebu_sw_pci_bridge *bridge = &port->bridge; - - memset(bridge, 0, sizeof(struct mvebu_sw_pci_bridge)); - - bridge->class = PCI_CLASS_BRIDGE_PCI; - bridge->vendor = PCI_VENDOR_ID_MARVELL; - bridge->device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16; - bridge->revision = mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff; - bridge->header_type = PCI_HEADER_TYPE_BRIDGE; - bridge->cache_line_size = 0x10; - - /* We support 32 bits I/O addressing */ - bridge->iobase = PCI_IO_RANGE_TYPE_32; - bridge->iolimit = PCI_IO_RANGE_TYPE_32; - - /* Add capabilities */ - bridge->status = PCI_STATUS_CAP_LIST; -} - -/* - * Read the configuration space of the PCI-to-PCI bridge associated to - * the given PCIe interface. - */ -static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port, - unsigned int where, int size, u32 *value) -{ - struct mvebu_sw_pci_bridge *bridge = &port->bridge; - - switch (where & ~3) { - case PCI_VENDOR_ID: - *value = bridge->device << 16 | bridge->vendor; - break; - - case PCI_COMMAND: - *value = bridge->command | bridge->status << 16; - break; - - case PCI_CLASS_REVISION: - *value = bridge->class << 16 | bridge->interface << 8 | - bridge->revision; - break; - - case PCI_CACHE_LINE_SIZE: - *value = bridge->bist << 24 | bridge->header_type << 16 | - bridge->latency_timer << 8 | bridge->cache_line_size; - break; - - case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1: - *value = bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4]; - break; - - case PCI_PRIMARY_BUS: - *value = (bridge->secondary_latency_timer << 24 | - bridge->subordinate_bus << 16 | - bridge->secondary_bus << 8 | - bridge->primary_bus); - break; - - case PCI_IO_BASE: - if (!mvebu_has_ioport(port)) - *value = bridge->secondary_status << 16; - else - *value = (bridge->secondary_status << 16 | - bridge->iolimit << 8 | - bridge->iobase); - break; - - case PCI_MEMORY_BASE: - *value = (bridge->memlimit << 16 | bridge->membase); - break; - - case PCI_PREF_MEMORY_BASE: - *value = 0; - break; - - case PCI_IO_BASE_UPPER16: - *value = (bridge->iolimitupper << 16 | bridge->iobaseupper); - break; - - case PCI_CAPABILITY_LIST: - *value = PCISWCAP; - break; - - case PCI_ROM_ADDRESS1: - *value = 0; - break; - - case PCI_INTERRUPT_LINE: - /* LINE PIN MIN_GNT MAX_LAT */ - *value = 0; - break; - - case PCISWCAP_EXP_LIST_ID: - /* Set PCIe v2, root port, slot support */ - *value = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2 | - PCI_EXP_FLAGS_SLOT) << 16 | PCI_CAP_ID_EXP; - break; - - case PCISWCAP_EXP_DEVCAP: - *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP); - break; - - case PCISWCAP_EXP_DEVCTL: - *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) & - ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | - PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); - *value |= bridge->pcie_devctl; - break; - - case PCISWCAP_EXP_LNKCAP: - /* - * PCIe requires the clock power management capability to be - * hard-wired to zero for downstream ports - */ - *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & - ~PCI_EXP_LNKCAP_CLKPM; - break; - - case PCISWCAP_EXP_LNKCTL: - *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); - break; - - case PCISWCAP_EXP_SLTCAP: - *value = bridge->pcie_sltcap; - break; - - case PCISWCAP_EXP_SLTCTL: - *value = PCI_EXP_SLTSTA_PDS << 16; - break; - - case PCISWCAP_EXP_RTCTL: - *value = bridge->pcie_rtctl; - break; - - case PCISWCAP_EXP_RTSTA: - *value = mvebu_readl(port, PCIE_RC_RTSTA); - break; - - /* PCIe requires the v2 fields to be hard-wired to zero */ - case PCISWCAP_EXP_DEVCAP2: - case PCISWCAP_EXP_DEVCTL2: - case PCISWCAP_EXP_LNKCAP2: - case PCISWCAP_EXP_LNKCTL2: - case PCISWCAP_EXP_SLTCAP2: - case PCISWCAP_EXP_SLTCTL2: - default: - /* - * PCI defines configuration read accesses to reserved or - * unimplemented registers to read as zero and complete - * normally. - */ - *value = 0; - return PCIBIOS_SUCCESSFUL; - } - - if (size == 2) - *value = (*value >> (8 * (where & 3))) & 0xffff; - else if (size == 1) - *value = (*value >> (8 * (where & 3))) & 0xff; - - return PCIBIOS_SUCCESSFUL; -} - -/* Write to the PCI-to-PCI bridge configuration space */ -static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port, - unsigned int where, int size, u32 value) -{ - struct mvebu_sw_pci_bridge *bridge = &port->bridge; - u32 mask, reg; - int err; - - if (size == 4) - mask = 0x0; - else if (size == 2) - mask = ~(0xffff << ((where & 3) * 8)); - else if (size == 1) - mask = ~(0xff << ((where & 3) * 8)); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, ®); - if (err) - return err; - - value = (reg & mask) | value << ((where & 3) * 8); - - switch (where & ~3) { - case PCI_COMMAND: - { - u32 old = bridge->command; - - if (!mvebu_has_ioport(port)) - value &= ~PCI_COMMAND_IO; - - bridge->command = value & 0xffff; - if ((old ^ bridge->command) & PCI_COMMAND_IO) - mvebu_pcie_handle_iobase_change(port); - if ((old ^ bridge->command) & PCI_COMMAND_MEMORY) - mvebu_pcie_handle_membase_change(port); - break; - } - - case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1: - bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4] = value; - break; - - case PCI_IO_BASE: - /* - * We also keep bit 1 set, it is a read-only bit that - * indicates we support 32 bits addressing for the - * I/O - */ - bridge->iobase = (value & 0xff) | PCI_IO_RANGE_TYPE_32; - bridge->iolimit = ((value >> 8) & 0xff) | PCI_IO_RANGE_TYPE_32; - mvebu_pcie_handle_iobase_change(port); - break; - - case PCI_MEMORY_BASE: - bridge->membase = value & 0xffff; - bridge->memlimit = value >> 16; - mvebu_pcie_handle_membase_change(port); - break; - - case PCI_IO_BASE_UPPER16: - bridge->iobaseupper = value & 0xffff; - bridge->iolimitupper = value >> 16; - mvebu_pcie_handle_iobase_change(port); - break; - - case PCI_PRIMARY_BUS: - bridge->primary_bus = value & 0xff; - bridge->secondary_bus = (value >> 8) & 0xff; - bridge->subordinate_bus = (value >> 16) & 0xff; - bridge->secondary_latency_timer = (value >> 24) & 0xff; - mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus); - break; - - case PCISWCAP_EXP_DEVCTL: - /* - * Armada370 data says these bits must always - * be zero when in root complex mode. - */ - value &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | - PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); - - /* - * If the mask is 0xffff0000, then we only want to write - * the device control register, rather than clearing the - * RW1C bits in the device status register. Mask out the - * status register bits. - */ - if (mask == 0xffff0000) - value &= 0xffff; - - mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); - break; - - case PCISWCAP_EXP_LNKCTL: - /* - * If we don't support CLKREQ, we must ensure that the - * CLKREQ enable bit always reads zero. Since we haven't - * had this capability, and it's dependent on board wiring, - * disable it for the time being. - */ - value &= ~PCI_EXP_LNKCTL_CLKREQ_EN; - - /* - * If the mask is 0xffff0000, then we only want to write - * the link control register, rather than clearing the - * RW1C bits in the link status register. Mask out the - * RW1C status register bits. - */ - if (mask == 0xffff0000) - value &= ~((PCI_EXP_LNKSTA_LABS | - PCI_EXP_LNKSTA_LBMS) << 16); - - mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); - break; - - case PCISWCAP_EXP_RTSTA: - mvebu_writel(port, value, PCIE_RC_RTSTA); - break; - - default: - break; - } - - return PCIBIOS_SUCCESSFUL; -} - -static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) -{ - return sys->private_data; -} - -static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, - struct pci_bus *bus, - int devfn) -{ - int i; - - for (i = 0; i < pcie->nports; i++) { - struct mvebu_pcie_port *port = &pcie->ports[i]; - - if (bus->number == 0 && port->devfn == devfn) - return port; - if (bus->number != 0 && - bus->number >= port->bridge.secondary_bus && - bus->number <= port->bridge.subordinate_bus) - return port; - } - - return NULL; -} - -/* PCI configuration space write function */ -static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) -{ - struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata); - struct mvebu_pcie_port *port; - int ret; - - port = mvebu_pcie_find_port(pcie, bus, devfn); - if (!port) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Access the emulated PCI-to-PCI bridge */ - if (bus->number == 0) - return mvebu_sw_pci_bridge_write(port, where, size, val); - - if (!mvebu_pcie_link_up(port)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Access the real PCIe interface */ - ret = mvebu_pcie_hw_wr_conf(port, bus, devfn, - where, size, val); - - return ret; -} - -/* PCI configuration space read function */ -static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) -{ - struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata); - struct mvebu_pcie_port *port; - int ret; - - port = mvebu_pcie_find_port(pcie, bus, devfn); - if (!port) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - /* Access the emulated PCI-to-PCI bridge */ - if (bus->number == 0) - return mvebu_sw_pci_bridge_read(port, where, size, val); - - if (!mvebu_pcie_link_up(port)) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - /* Access the real PCIe interface */ - ret = mvebu_pcie_hw_rd_conf(port, bus, devfn, - where, size, val); - - return ret; -} - -static struct pci_ops mvebu_pcie_ops = { - .read = mvebu_pcie_rd_conf, - .write = mvebu_pcie_wr_conf, -}; - -static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) -{ - struct mvebu_pcie *pcie = sys_to_pcie(sys); - int err, i; - - pcie->mem.name = "PCI MEM"; - pcie->realio.name = "PCI I/O"; - - if (resource_size(&pcie->realio) != 0) - pci_add_resource_offset(&sys->resources, &pcie->realio, - sys->io_offset); - - pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); - pci_add_resource(&sys->resources, &pcie->busn); - - err = devm_request_pci_bus_resources(&pcie->pdev->dev, &sys->resources); - if (err) - return 0; - - for (i = 0; i < pcie->nports; i++) { - struct mvebu_pcie_port *port = &pcie->ports[i]; - - if (!port->base) - continue; - mvebu_pcie_setup_hw(port); - } - - return 1; -} - -static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, - const struct resource *res, - resource_size_t start, - resource_size_t size, - resource_size_t align) -{ - if (dev->bus->number != 0) - return start; - - /* - * On the PCI-to-PCI bridge side, the I/O windows must have at - * least a 64 KB size and the memory windows must have at - * least a 1 MB size. Moreover, MBus windows need to have a - * base address aligned on their size, and their size must be - * a power of two. This means that if the BAR doesn't have a - * power of two size, several MBus windows will actually be - * created. We need to ensure that the biggest MBus window - * (which will be the first one) is aligned on its size, which - * explains the rounddown_pow_of_two() being done here. - */ - if (res->flags & IORESOURCE_IO) - return round_up(start, max_t(resource_size_t, SZ_64K, - rounddown_pow_of_two(size))); - else if (res->flags & IORESOURCE_MEM) - return round_up(start, max_t(resource_size_t, SZ_1M, - rounddown_pow_of_two(size))); - else - return start; -} - -static void mvebu_pcie_enable(struct mvebu_pcie *pcie) -{ - struct hw_pci hw; - - memset(&hw, 0, sizeof(hw)); - -#ifdef CONFIG_PCI_MSI - hw.msi_ctrl = pcie->msi; -#endif - - hw.nr_controllers = 1; - hw.private_data = (void **)&pcie; - hw.setup = mvebu_pcie_setup; - hw.map_irq = of_irq_parse_and_map_pci; - hw.ops = &mvebu_pcie_ops; - hw.align_resource = mvebu_pcie_align_resource; - - pci_common_init_dev(&pcie->pdev->dev, &hw); -} - -/* - * Looks up the list of register addresses encoded into the reg = - * <...> property for one that matches the given port/lane. Once - * found, maps it. - */ -static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev, - struct device_node *np, - struct mvebu_pcie_port *port) -{ - struct resource regs; - int ret = 0; - - ret = of_address_to_resource(np, 0, ®s); - if (ret) - return ERR_PTR(ret); - - return devm_ioremap_resource(&pdev->dev, ®s); -} - -#define DT_FLAGS_TO_TYPE(flags) (((flags) >> 24) & 0x03) -#define DT_TYPE_IO 0x1 -#define DT_TYPE_MEM32 0x2 -#define DT_CPUADDR_TO_TARGET(cpuaddr) (((cpuaddr) >> 56) & 0xFF) -#define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF) - -static int mvebu_get_tgt_attr(struct device_node *np, int devfn, - unsigned long type, - unsigned int *tgt, - unsigned int *attr) -{ - const int na = 3, ns = 2; - const __be32 *range; - int rlen, nranges, rangesz, pna, i; - - *tgt = -1; - *attr = -1; - - range = of_get_property(np, "ranges", &rlen); - if (!range) - return -EINVAL; - - pna = of_n_addr_cells(np); - rangesz = pna + na + ns; - nranges = rlen / sizeof(__be32) / rangesz; - - for (i = 0; i < nranges; i++, range += rangesz) { - u32 flags = of_read_number(range, 1); - u32 slot = of_read_number(range + 1, 1); - u64 cpuaddr = of_read_number(range + na, pna); - unsigned long rtype; - - if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_IO) - rtype = IORESOURCE_IO; - else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32) - rtype = IORESOURCE_MEM; - else - continue; - - if (slot == PCI_SLOT(devfn) && type == rtype) { - *tgt = DT_CPUADDR_TO_TARGET(cpuaddr); - *attr = DT_CPUADDR_TO_ATTR(cpuaddr); - return 0; - } - } - - return -ENOENT; -} - -#ifdef CONFIG_PM_SLEEP -static int mvebu_pcie_suspend(struct device *dev) -{ - struct mvebu_pcie *pcie; - int i; - - pcie = dev_get_drvdata(dev); - for (i = 0; i < pcie->nports; i++) { - struct mvebu_pcie_port *port = pcie->ports + i; - port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF); - } - - return 0; -} - -static int mvebu_pcie_resume(struct device *dev) -{ - struct mvebu_pcie *pcie; - int i; - - pcie = dev_get_drvdata(dev); - for (i = 0; i < pcie->nports; i++) { - struct mvebu_pcie_port *port = pcie->ports + i; - mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF); - mvebu_pcie_setup_hw(port); - } - - return 0; -} -#endif - -static void mvebu_pcie_port_clk_put(void *data) -{ - struct mvebu_pcie_port *port = data; - - clk_put(port->clk); -} - -static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, - struct mvebu_pcie_port *port, struct device_node *child) -{ - struct device *dev = &pcie->pdev->dev; - enum of_gpio_flags flags; - int reset_gpio, ret; - - port->pcie = pcie; - - if (of_property_read_u32(child, "marvell,pcie-port", &port->port)) { - dev_warn(dev, "ignoring %pOF, missing pcie-port property\n", - child); - goto skip; - } - - if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane)) - port->lane = 0; - - port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port, - port->lane); - if (!port->name) { - ret = -ENOMEM; - goto err; - } - - port->devfn = of_pci_get_devfn(child); - if (port->devfn < 0) - goto skip; - - ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM, - &port->mem_target, &port->mem_attr); - if (ret < 0) { - dev_err(dev, "%s: cannot get tgt/attr for mem window\n", - port->name); - goto skip; - } - - if (resource_size(&pcie->io) != 0) { - mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_IO, - &port->io_target, &port->io_attr); - } else { - port->io_target = -1; - port->io_attr = -1; - } - - reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags); - if (reset_gpio == -EPROBE_DEFER) { - ret = reset_gpio; - goto err; - } - - if (gpio_is_valid(reset_gpio)) { - unsigned long gpio_flags; - - port->reset_name = devm_kasprintf(dev, GFP_KERNEL, "%s-reset", - port->name); - if (!port->reset_name) { - ret = -ENOMEM; - goto err; - } - - if (flags & OF_GPIO_ACTIVE_LOW) { - dev_info(dev, "%pOF: reset gpio is active low\n", - child); - gpio_flags = GPIOF_ACTIVE_LOW | - GPIOF_OUT_INIT_LOW; - } else { - gpio_flags = GPIOF_OUT_INIT_HIGH; - } - - ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags, - port->reset_name); - if (ret) { - if (ret == -EPROBE_DEFER) - goto err; - goto skip; - } - - port->reset_gpio = gpio_to_desc(reset_gpio); - } - - port->clk = of_clk_get_by_name(child, NULL); - if (IS_ERR(port->clk)) { - dev_err(dev, "%s: cannot get clock\n", port->name); - goto skip; - } - - ret = devm_add_action(dev, mvebu_pcie_port_clk_put, port); - if (ret < 0) { - clk_put(port->clk); - goto err; - } - - return 1; - -skip: - ret = 0; - - /* In the case of skipping, we need to free these */ - devm_kfree(dev, port->reset_name); - port->reset_name = NULL; - devm_kfree(dev, port->name); - port->name = NULL; - -err: - return ret; -} - -/* - * Power up a PCIe port. 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. - */ -static int mvebu_pcie_powerup(struct mvebu_pcie_port *port) -{ - int ret; - - ret = clk_prepare_enable(port->clk); - if (ret < 0) - return ret; - - if (port->reset_gpio) { - u32 reset_udelay = PCI_PM_D3COLD_WAIT * 1000; - - of_property_read_u32(port->dn, "reset-delay-us", - &reset_udelay); - - udelay(100); - - gpiod_set_value_cansleep(port->reset_gpio, 0); - msleep(reset_udelay / 1000); - } - - return 0; -} - -/* - * Power down a PCIe port. Strictly, PCIe requires us to place the card - * in D3hot state before asserting PERST#. - */ -static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port) -{ - gpiod_set_value_cansleep(port->reset_gpio, 1); - - clk_disable_unprepare(port->clk); -} - -static int mvebu_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mvebu_pcie *pcie; - struct device_node *np = dev->of_node; - struct device_node *child; - int num, i, ret; - - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); - if (!pcie) - return -ENOMEM; - - pcie->pdev = pdev; - platform_set_drvdata(pdev, pcie); - - /* Get the PCIe memory and I/O aperture */ - mvebu_mbus_get_pcie_mem_aperture(&pcie->mem); - if (resource_size(&pcie->mem) == 0) { - dev_err(dev, "invalid memory aperture size\n"); - return -EINVAL; - } - - mvebu_mbus_get_pcie_io_aperture(&pcie->io); - - if (resource_size(&pcie->io) != 0) { - pcie->realio.flags = pcie->io.flags; - pcie->realio.start = PCIBIOS_MIN_IO; - pcie->realio.end = min_t(resource_size_t, - IO_SPACE_LIMIT, - resource_size(&pcie->io)); - } else - pcie->realio = pcie->io; - - /* Get the bus range */ - ret = of_pci_parse_bus_range(np, &pcie->busn); - if (ret) { - dev_err(dev, "failed to parse bus-range property: %d\n", ret); - return ret; - } - - num = of_get_available_child_count(np); - - pcie->ports = devm_kcalloc(dev, num, sizeof(*pcie->ports), GFP_KERNEL); - if (!pcie->ports) - return -ENOMEM; - - i = 0; - for_each_available_child_of_node(np, child) { - struct mvebu_pcie_port *port = &pcie->ports[i]; - - ret = mvebu_pcie_parse_port(pcie, port, child); - if (ret < 0) { - of_node_put(child); - return ret; - } else if (ret == 0) { - continue; - } - - port->dn = child; - i++; - } - pcie->nports = i; - - for (i = 0; i < pcie->nports; i++) { - struct mvebu_pcie_port *port = &pcie->ports[i]; - - child = port->dn; - if (!child) - continue; - - ret = mvebu_pcie_powerup(port); - if (ret < 0) - continue; - - port->base = mvebu_pcie_map_registers(pdev, child, port); - if (IS_ERR(port->base)) { - dev_err(dev, "%s: cannot map registers\n", port->name); - port->base = NULL; - mvebu_pcie_powerdown(port); - continue; - } - - mvebu_pcie_set_local_dev_nr(port, 1); - mvebu_sw_pci_bridge_init(port); - } - - pcie->nports = i; - - for (i = 0; i < (IO_SPACE_LIMIT - SZ_64K); i += SZ_64K) - pci_ioremap_io(i, pcie->io.start + i); - - mvebu_pcie_enable(pcie); - - platform_set_drvdata(pdev, pcie); - - return 0; -} - -static const struct of_device_id mvebu_pcie_of_match_table[] = { - { .compatible = "marvell,armada-xp-pcie", }, - { .compatible = "marvell,armada-370-pcie", }, - { .compatible = "marvell,dove-pcie", }, - { .compatible = "marvell,kirkwood-pcie", }, - {}, -}; - -static const struct dev_pm_ops mvebu_pcie_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mvebu_pcie_suspend, mvebu_pcie_resume) -}; - -static struct platform_driver mvebu_pcie_driver = { - .driver = { - .name = "mvebu-pcie", - .of_match_table = mvebu_pcie_of_match_table, - /* driver unloading/unbinding currently not supported */ - .suppress_bind_attrs = true, - .pm = &mvebu_pcie_pm_ops, - }, - .probe = mvebu_pcie_probe, -}; -builtin_platform_driver(mvebu_pcie_driver); diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c deleted file mode 100644 index 326171cb1a97..000000000000 --- a/drivers/pci/host/pci-rcar-gen2.c +++ /dev/null @@ -1,428 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * pci-rcar-gen2: internal PCI bus support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * Author: Valentine Barshak - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -/* AHB-PCI Bridge PCI communication registers */ -#define RCAR_AHBPCI_PCICOM_OFFSET 0x800 - -#define RCAR_PCIAHB_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x00) -#define RCAR_PCIAHB_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x04) -#define RCAR_PCIAHB_PREFETCH0 0x0 -#define RCAR_PCIAHB_PREFETCH4 0x1 -#define RCAR_PCIAHB_PREFETCH8 0x2 -#define RCAR_PCIAHB_PREFETCH16 0x3 - -#define RCAR_AHBPCI_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x10) -#define RCAR_AHBPCI_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x14) -#define RCAR_AHBPCI_WIN_CTR_MEM (3 << 1) -#define RCAR_AHBPCI_WIN_CTR_CFG (5 << 1) -#define RCAR_AHBPCI_WIN1_HOST (1 << 30) -#define RCAR_AHBPCI_WIN1_DEVICE (1 << 31) - -#define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20) -#define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24) -#define RCAR_PCI_INT_SIGTABORT (1 << 0) -#define RCAR_PCI_INT_SIGRETABORT (1 << 1) -#define RCAR_PCI_INT_REMABORT (1 << 2) -#define RCAR_PCI_INT_PERR (1 << 3) -#define RCAR_PCI_INT_SIGSERR (1 << 4) -#define RCAR_PCI_INT_RESERR (1 << 5) -#define RCAR_PCI_INT_WIN1ERR (1 << 12) -#define RCAR_PCI_INT_WIN2ERR (1 << 13) -#define RCAR_PCI_INT_A (1 << 16) -#define RCAR_PCI_INT_B (1 << 17) -#define RCAR_PCI_INT_PME (1 << 19) -#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \ - RCAR_PCI_INT_SIGRETABORT | \ - RCAR_PCI_INT_REMABORT | \ - RCAR_PCI_INT_PERR | \ - RCAR_PCI_INT_SIGSERR | \ - RCAR_PCI_INT_RESERR | \ - RCAR_PCI_INT_WIN1ERR | \ - RCAR_PCI_INT_WIN2ERR) - -#define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30) -#define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0) -#define RCAR_AHB_BUS_MMODE_BYTE_BURST (1 << 1) -#define RCAR_AHB_BUS_MMODE_WR_INCR (1 << 2) -#define RCAR_AHB_BUS_MMODE_HBUS_REQ (1 << 7) -#define RCAR_AHB_BUS_SMODE_READYCTR (1 << 17) -#define RCAR_AHB_BUS_MODE (RCAR_AHB_BUS_MMODE_HTRANS | \ - RCAR_AHB_BUS_MMODE_BYTE_BURST | \ - RCAR_AHB_BUS_MMODE_WR_INCR | \ - RCAR_AHB_BUS_MMODE_HBUS_REQ | \ - RCAR_AHB_BUS_SMODE_READYCTR) - -#define RCAR_USBCTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x34) -#define RCAR_USBCTR_USBH_RST (1 << 0) -#define RCAR_USBCTR_PCICLK_MASK (1 << 1) -#define RCAR_USBCTR_PLL_RST (1 << 2) -#define RCAR_USBCTR_DIRPD (1 << 8) -#define RCAR_USBCTR_PCIAHB_WIN2_EN (1 << 9) -#define RCAR_USBCTR_PCIAHB_WIN1_256M (0 << 10) -#define RCAR_USBCTR_PCIAHB_WIN1_512M (1 << 10) -#define RCAR_USBCTR_PCIAHB_WIN1_1G (2 << 10) -#define RCAR_USBCTR_PCIAHB_WIN1_2G (3 << 10) -#define RCAR_USBCTR_PCIAHB_WIN1_MASK (3 << 10) - -#define RCAR_PCI_ARBITER_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x40) -#define RCAR_PCI_ARBITER_PCIREQ0 (1 << 0) -#define RCAR_PCI_ARBITER_PCIREQ1 (1 << 1) -#define RCAR_PCI_ARBITER_PCIBP_MODE (1 << 12) - -#define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48) - -struct rcar_pci_priv { - struct device *dev; - void __iomem *reg; - struct resource mem_res; - struct resource *cfg_res; - unsigned busnr; - int irq; - unsigned long window_size; - unsigned long window_addr; - unsigned long window_pci; -}; - -/* PCI configuration space operations */ -static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, - int where) -{ - struct pci_sys_data *sys = bus->sysdata; - struct rcar_pci_priv *priv = sys->private_data; - int slot, val; - - if (sys->busnr != bus->number || PCI_FUNC(devfn)) - return NULL; - - /* Only one EHCI/OHCI device built-in */ - slot = PCI_SLOT(devfn); - if (slot > 2) - return NULL; - - /* bridge logic only has registers to 0x40 */ - if (slot == 0x0 && where >= 0x40) - return NULL; - - val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG : - RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG; - - iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG); - return priv->reg + (slot >> 1) * 0x100 + where; -} - -/* PCI interrupt mapping */ -static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - struct pci_sys_data *sys = dev->bus->sysdata; - struct rcar_pci_priv *priv = sys->private_data; - int irq; - - irq = of_irq_parse_and_map_pci(dev, slot, pin); - if (!irq) - irq = priv->irq; - - return irq; -} - -#ifdef CONFIG_PCI_DEBUG -/* if debug enabled, then attach an error handler irq to the bridge */ - -static irqreturn_t rcar_pci_err_irq(int irq, void *pw) -{ - struct rcar_pci_priv *priv = pw; - struct device *dev = priv->dev; - u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG); - - if (status & RCAR_PCI_INT_ALLERRORS) { - dev_err(dev, "error irq: status %08x\n", status); - - /* clear the error(s) */ - iowrite32(status & RCAR_PCI_INT_ALLERRORS, - priv->reg + RCAR_PCI_INT_STATUS_REG); - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) -{ - struct device *dev = priv->dev; - int ret; - u32 val; - - ret = devm_request_irq(dev, priv->irq, rcar_pci_err_irq, - IRQF_SHARED, "error irq", priv); - if (ret) { - dev_err(dev, "cannot claim IRQ for error handling\n"); - return; - } - - val = ioread32(priv->reg + RCAR_PCI_INT_ENABLE_REG); - val |= RCAR_PCI_INT_ALLERRORS; - iowrite32(val, priv->reg + RCAR_PCI_INT_ENABLE_REG); -} -#else -static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { } -#endif - -/* PCI host controller setup */ -static int rcar_pci_setup(int nr, struct pci_sys_data *sys) -{ - struct rcar_pci_priv *priv = sys->private_data; - struct device *dev = priv->dev; - void __iomem *reg = priv->reg; - u32 val; - int ret; - - pm_runtime_enable(dev); - pm_runtime_get_sync(dev); - - val = ioread32(reg + RCAR_PCI_UNIT_REV_REG); - dev_info(dev, "PCI: bus%u revision %x\n", sys->busnr, val); - - /* Disable Direct Power Down State and assert reset */ - val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD; - val |= RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST; - iowrite32(val, reg + RCAR_USBCTR_REG); - udelay(4); - - /* De-assert reset and reset PCIAHB window1 size */ - val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK | - RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST); - - /* Setup PCIAHB window1 size */ - switch (priv->window_size) { - case SZ_2G: - val |= RCAR_USBCTR_PCIAHB_WIN1_2G; - break; - case SZ_1G: - val |= RCAR_USBCTR_PCIAHB_WIN1_1G; - break; - case SZ_512M: - val |= RCAR_USBCTR_PCIAHB_WIN1_512M; - break; - default: - pr_warn("unknown window size %ld - defaulting to 256M\n", - priv->window_size); - priv->window_size = SZ_256M; - /* fall-through */ - case SZ_256M: - val |= RCAR_USBCTR_PCIAHB_WIN1_256M; - break; - } - iowrite32(val, reg + RCAR_USBCTR_REG); - - /* Configure AHB master and slave modes */ - iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG); - - /* Configure PCI arbiter */ - val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG); - val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 | - RCAR_PCI_ARBITER_PCIBP_MODE; - iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); - - /* PCI-AHB mapping */ - iowrite32(priv->window_addr | RCAR_PCIAHB_PREFETCH16, - reg + RCAR_PCIAHB_WIN1_CTR_REG); - - /* AHB-PCI mapping: OHCI/EHCI registers */ - val = priv->mem_res.start | RCAR_AHBPCI_WIN_CTR_MEM; - iowrite32(val, reg + RCAR_AHBPCI_WIN2_CTR_REG); - - /* Enable AHB-PCI bridge PCI configuration access */ - iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG, - reg + RCAR_AHBPCI_WIN1_CTR_REG); - /* Set PCI-AHB Window1 address */ - iowrite32(priv->window_pci | PCI_BASE_ADDRESS_MEM_PREFETCH, - reg + PCI_BASE_ADDRESS_1); - /* Set AHB-PCI bridge PCI communication area address */ - val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET; - iowrite32(val, reg + PCI_BASE_ADDRESS_0); - - val = ioread32(reg + PCI_COMMAND); - val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; - iowrite32(val, reg + PCI_COMMAND); - - /* Enable PCI interrupts */ - iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME, - reg + RCAR_PCI_INT_ENABLE_REG); - - if (priv->irq > 0) - rcar_pci_setup_errirq(priv); - - /* Add PCI resources */ - pci_add_resource(&sys->resources, &priv->mem_res); - ret = devm_request_pci_bus_resources(dev, &sys->resources); - if (ret < 0) - return ret; - - /* Setup bus number based on platform device id / of bus-range */ - sys->busnr = priv->busnr; - return 1; -} - -static struct pci_ops rcar_pci_ops = { - .map_bus = rcar_pci_cfg_base, - .read = pci_generic_config_read, - .write = pci_generic_config_write, -}; - -static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci, - struct device_node *np) -{ - struct device *dev = pci->dev; - struct of_pci_range range; - struct of_pci_range_parser parser; - int index = 0; - - /* Failure to parse is ok as we fall back to defaults */ - if (of_pci_dma_range_parser_init(&parser, np)) - return 0; - - /* Get the dma-ranges from DT */ - for_each_of_pci_range(&parser, &range) { - /* Hardware only allows one inbound 32-bit range */ - if (index) - return -EINVAL; - - pci->window_addr = (unsigned long)range.cpu_addr; - pci->window_pci = (unsigned long)range.pci_addr; - pci->window_size = (unsigned long)range.size; - - /* Catch HW limitations */ - if (!(range.flags & IORESOURCE_PREFETCH)) { - dev_err(dev, "window must be prefetchable\n"); - return -EINVAL; - } - if (pci->window_addr) { - u32 lowaddr = 1 << (ffs(pci->window_addr) - 1); - - if (lowaddr < pci->window_size) { - dev_err(dev, "invalid window size/addr\n"); - return -EINVAL; - } - } - index++; - } - - return 0; -} - -static int rcar_pci_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *cfg_res, *mem_res; - struct rcar_pci_priv *priv; - void __iomem *reg; - struct hw_pci hw; - void *hw_private[1]; - - cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg = devm_ioremap_resource(dev, cfg_res); - if (IS_ERR(reg)) - return PTR_ERR(reg); - - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!mem_res || !mem_res->start) - return -ENODEV; - - if (mem_res->start & 0xFFFF) - return -EINVAL; - - priv = devm_kzalloc(dev, sizeof(struct rcar_pci_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->mem_res = *mem_res; - priv->cfg_res = cfg_res; - - priv->irq = platform_get_irq(pdev, 0); - priv->reg = reg; - priv->dev = dev; - - if (priv->irq < 0) { - dev_err(dev, "no valid irq found\n"); - return priv->irq; - } - - /* default window addr and size if not specified in DT */ - priv->window_addr = 0x40000000; - priv->window_pci = 0x40000000; - priv->window_size = SZ_1G; - - if (dev->of_node) { - struct resource busnr; - int ret; - - ret = of_pci_parse_bus_range(dev->of_node, &busnr); - if (ret < 0) { - dev_err(dev, "failed to parse bus-range\n"); - return ret; - } - - priv->busnr = busnr.start; - if (busnr.end != busnr.start) - dev_warn(dev, "only one bus number supported\n"); - - ret = rcar_pci_parse_map_dma_ranges(priv, dev->of_node); - if (ret < 0) { - dev_err(dev, "failed to parse dma-range\n"); - return ret; - } - } else { - priv->busnr = pdev->id; - } - - hw_private[0] = priv; - memset(&hw, 0, sizeof(hw)); - hw.nr_controllers = ARRAY_SIZE(hw_private); - hw.io_optional = 1; - hw.private_data = hw_private; - hw.map_irq = rcar_pci_map_irq; - hw.ops = &rcar_pci_ops; - hw.setup = rcar_pci_setup; - pci_common_init_dev(dev, &hw); - return 0; -} - -static const struct of_device_id rcar_pci_of_match[] = { - { .compatible = "renesas,pci-r8a7790", }, - { .compatible = "renesas,pci-r8a7791", }, - { .compatible = "renesas,pci-r8a7794", }, - { .compatible = "renesas,pci-rcar-gen2", }, - { }, -}; - -static struct platform_driver rcar_pci_driver = { - .driver = { - .name = "pci-rcar-gen2", - .suppress_bind_attrs = true, - .of_match_table = rcar_pci_of_match, - }, - .probe = rcar_pci_probe, -}; -builtin_platform_driver(rcar_pci_driver); diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c deleted file mode 100644 index f4f53d092e00..000000000000 --- a/drivers/pci/host/pci-tegra.c +++ /dev/null @@ -1,2531 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * PCIe host controller driver for Tegra SoCs - * - * Copyright (c) 2010, CompuLab, Ltd. - * Author: Mike Rapoport - * - * Based on NVIDIA PCIe driver - * Copyright (c) 2008-2009, NVIDIA Corporation. - * - * Bits taken from arch/arm/mach-dove/pcie.c - * - * Author: Thierry Reding - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../pci.h" - -#define INT_PCI_MSI_NR (8 * 32) - -/* register definitions */ - -#define AFI_AXI_BAR0_SZ 0x00 -#define AFI_AXI_BAR1_SZ 0x04 -#define AFI_AXI_BAR2_SZ 0x08 -#define AFI_AXI_BAR3_SZ 0x0c -#define AFI_AXI_BAR4_SZ 0x10 -#define AFI_AXI_BAR5_SZ 0x14 - -#define AFI_AXI_BAR0_START 0x18 -#define AFI_AXI_BAR1_START 0x1c -#define AFI_AXI_BAR2_START 0x20 -#define AFI_AXI_BAR3_START 0x24 -#define AFI_AXI_BAR4_START 0x28 -#define AFI_AXI_BAR5_START 0x2c - -#define AFI_FPCI_BAR0 0x30 -#define AFI_FPCI_BAR1 0x34 -#define AFI_FPCI_BAR2 0x38 -#define AFI_FPCI_BAR3 0x3c -#define AFI_FPCI_BAR4 0x40 -#define AFI_FPCI_BAR5 0x44 - -#define AFI_CACHE_BAR0_SZ 0x48 -#define AFI_CACHE_BAR0_ST 0x4c -#define AFI_CACHE_BAR1_SZ 0x50 -#define AFI_CACHE_BAR1_ST 0x54 - -#define AFI_MSI_BAR_SZ 0x60 -#define AFI_MSI_FPCI_BAR_ST 0x64 -#define AFI_MSI_AXI_BAR_ST 0x68 - -#define AFI_MSI_VEC0 0x6c -#define AFI_MSI_VEC1 0x70 -#define AFI_MSI_VEC2 0x74 -#define AFI_MSI_VEC3 0x78 -#define AFI_MSI_VEC4 0x7c -#define AFI_MSI_VEC5 0x80 -#define AFI_MSI_VEC6 0x84 -#define AFI_MSI_VEC7 0x88 - -#define AFI_MSI_EN_VEC0 0x8c -#define AFI_MSI_EN_VEC1 0x90 -#define AFI_MSI_EN_VEC2 0x94 -#define AFI_MSI_EN_VEC3 0x98 -#define AFI_MSI_EN_VEC4 0x9c -#define AFI_MSI_EN_VEC5 0xa0 -#define AFI_MSI_EN_VEC6 0xa4 -#define AFI_MSI_EN_VEC7 0xa8 - -#define AFI_CONFIGURATION 0xac -#define AFI_CONFIGURATION_EN_FPCI (1 << 0) - -#define AFI_FPCI_ERROR_MASKS 0xb0 - -#define AFI_INTR_MASK 0xb4 -#define AFI_INTR_MASK_INT_MASK (1 << 0) -#define AFI_INTR_MASK_MSI_MASK (1 << 8) - -#define AFI_INTR_CODE 0xb8 -#define AFI_INTR_CODE_MASK 0xf -#define AFI_INTR_INI_SLAVE_ERROR 1 -#define AFI_INTR_INI_DECODE_ERROR 2 -#define AFI_INTR_TARGET_ABORT 3 -#define AFI_INTR_MASTER_ABORT 4 -#define AFI_INTR_INVALID_WRITE 5 -#define AFI_INTR_LEGACY 6 -#define AFI_INTR_FPCI_DECODE_ERROR 7 -#define AFI_INTR_AXI_DECODE_ERROR 8 -#define AFI_INTR_FPCI_TIMEOUT 9 -#define AFI_INTR_PE_PRSNT_SENSE 10 -#define AFI_INTR_PE_CLKREQ_SENSE 11 -#define AFI_INTR_CLKCLAMP_SENSE 12 -#define AFI_INTR_RDY4PD_SENSE 13 -#define AFI_INTR_P2P_ERROR 14 - -#define AFI_INTR_SIGNATURE 0xbc -#define AFI_UPPER_FPCI_ADDRESS 0xc0 -#define AFI_SM_INTR_ENABLE 0xc4 -#define AFI_SM_INTR_INTA_ASSERT (1 << 0) -#define AFI_SM_INTR_INTB_ASSERT (1 << 1) -#define AFI_SM_INTR_INTC_ASSERT (1 << 2) -#define AFI_SM_INTR_INTD_ASSERT (1 << 3) -#define AFI_SM_INTR_INTA_DEASSERT (1 << 4) -#define AFI_SM_INTR_INTB_DEASSERT (1 << 5) -#define AFI_SM_INTR_INTC_DEASSERT (1 << 6) -#define AFI_SM_INTR_INTD_DEASSERT (1 << 7) - -#define AFI_AFI_INTR_ENABLE 0xc8 -#define AFI_INTR_EN_INI_SLVERR (1 << 0) -#define AFI_INTR_EN_INI_DECERR (1 << 1) -#define AFI_INTR_EN_TGT_SLVERR (1 << 2) -#define AFI_INTR_EN_TGT_DECERR (1 << 3) -#define AFI_INTR_EN_TGT_WRERR (1 << 4) -#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) -#define AFI_INTR_EN_AXI_DECERR (1 << 6) -#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) -#define AFI_INTR_EN_PRSNT_SENSE (1 << 8) - -#define AFI_PCIE_PME 0xf0 - -#define AFI_PCIE_CONFIG 0x0f8 -#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) -#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_401 (0x0 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211 (0x1 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_111 (0x2 << 20) - -#define AFI_FUSE 0x104 -#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) - -#define AFI_PEX0_CTRL 0x110 -#define AFI_PEX1_CTRL 0x118 -#define AFI_PEX2_CTRL 0x128 -#define AFI_PEX_CTRL_RST (1 << 0) -#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) -#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) -#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) - -#define AFI_PLLE_CONTROL 0x160 -#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) -#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) - -#define AFI_PEXBIAS_CTRL_0 0x168 - -#define RP_VEND_XP 0x00000f00 -#define RP_VEND_XP_DL_UP (1 << 30) - -#define RP_VEND_CTL2 0x00000fa8 -#define RP_VEND_CTL2_PCA_ENABLE (1 << 7) - -#define RP_PRIV_MISC 0x00000fe0 -#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xe << 0) -#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xf << 0) - -#define RP_LINK_CONTROL_STATUS 0x00000090 -#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 -#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 - -#define PADS_CTL_SEL 0x0000009c - -#define PADS_CTL 0x000000a0 -#define PADS_CTL_IDDQ_1L (1 << 0) -#define PADS_CTL_TX_DATA_EN_1L (1 << 6) -#define PADS_CTL_RX_DATA_EN_1L (1 << 10) - -#define PADS_PLL_CTL_TEGRA20 0x000000b8 -#define PADS_PLL_CTL_TEGRA30 0x000000b4 -#define PADS_PLL_CTL_RST_B4SM (1 << 1) -#define PADS_PLL_CTL_LOCKDET (1 << 8) -#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) -#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16) -#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16) -#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16) -#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) -#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20) -#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20) -#define PADS_PLL_CTL_TXCLKREF_BUF_EN (1 << 22) - -#define PADS_REFCLK_CFG0 0x000000c8 -#define PADS_REFCLK_CFG1 0x000000cc -#define PADS_REFCLK_BIAS 0x000000d0 - -/* - * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit - * entries, one entry per PCIe port. These field definitions and desired - * values aren't in the TRM, but do come from NVIDIA. - */ -#define PADS_REFCLK_CFG_TERM_SHIFT 2 /* 6:2 */ -#define PADS_REFCLK_CFG_E_TERM_SHIFT 7 -#define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ -#define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ - -#define PME_ACK_TIMEOUT 10000 - -struct tegra_msi { - struct msi_controller chip; - DECLARE_BITMAP(used, INT_PCI_MSI_NR); - struct irq_domain *domain; - unsigned long pages; - struct mutex lock; - u64 phys; - int irq; -}; - -/* used to differentiate between Tegra SoC generations */ -struct tegra_pcie_port_soc { - struct { - u8 turnoff_bit; - u8 ack_bit; - } pme; -}; - -struct tegra_pcie_soc { - unsigned int num_ports; - const struct tegra_pcie_port_soc *ports; - unsigned int msi_base_shift; - u32 pads_pll_ctl; - u32 tx_ref_sel; - u32 pads_refclk_cfg0; - u32 pads_refclk_cfg1; - bool has_pex_clkreq_en; - bool has_pex_bias_ctrl; - bool has_intr_prsnt_sense; - bool has_cml_clk; - bool has_gen2; - bool force_pca_enable; - bool program_uphy; -}; - -static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip) -{ - return container_of(chip, struct tegra_msi, chip); -} - -struct tegra_pcie { - struct device *dev; - - void __iomem *pads; - void __iomem *afi; - void __iomem *cfg; - int irq; - - struct resource cs; - struct resource io; - struct resource pio; - struct resource mem; - struct resource prefetch; - struct resource busn; - - struct { - resource_size_t mem; - resource_size_t io; - } offset; - - struct clk *pex_clk; - struct clk *afi_clk; - struct clk *pll_e; - struct clk *cml_clk; - - struct reset_control *pex_rst; - struct reset_control *afi_rst; - struct reset_control *pcie_xrst; - - bool legacy_phy; - struct phy *phy; - - struct tegra_msi msi; - - struct list_head ports; - u32 xbar_config; - - struct regulator_bulk_data *supplies; - unsigned int num_supplies; - - const struct tegra_pcie_soc *soc; - struct dentry *debugfs; -}; - -struct tegra_pcie_port { - struct tegra_pcie *pcie; - struct device_node *np; - struct list_head list; - struct resource regs; - void __iomem *base; - unsigned int index; - unsigned int lanes; - - struct phy **phys; -}; - -struct tegra_pcie_bus { - struct list_head list; - unsigned int nr; -}; - -static inline void afi_writel(struct tegra_pcie *pcie, u32 value, - unsigned long offset) -{ - writel(value, pcie->afi + offset); -} - -static inline u32 afi_readl(struct tegra_pcie *pcie, unsigned long offset) -{ - return readl(pcie->afi + offset); -} - -static inline void pads_writel(struct tegra_pcie *pcie, u32 value, - unsigned long offset) -{ - writel(value, pcie->pads + offset); -} - -static inline u32 pads_readl(struct tegra_pcie *pcie, unsigned long offset) -{ - return readl(pcie->pads + offset); -} - -/* - * The configuration space mapping on Tegra is somewhat similar to the ECAM - * defined by PCIe. However it deviates a bit in how the 4 bits for extended - * register accesses are mapped: - * - * [27:24] extended register number - * [23:16] bus number - * [15:11] device number - * [10: 8] function number - * [ 7: 0] register number - * - * Mapping the whole extended configuration space would require 256 MiB of - * virtual address space, only a small part of which will actually be used. - * - * To work around this, a 4 KiB region is used to generate the required - * configuration transaction with relevant B:D:F and register offset values. - * This is achieved by dynamically programming base address and size of - * AFI_AXI_BAR used for end point config space mapping to make sure that the - * address (access to which generates correct config transaction) falls in - * this 4 KiB region. - */ -static unsigned int tegra_pcie_conf_offset(u8 bus, unsigned int devfn, - unsigned int where) -{ - return ((where & 0xf00) << 16) | (bus << 16) | (PCI_SLOT(devfn) << 11) | - (PCI_FUNC(devfn) << 8) | (where & 0xff); -} - -static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, - unsigned int devfn, - int where) -{ - struct tegra_pcie *pcie = bus->sysdata; - void __iomem *addr = NULL; - - if (bus->number == 0) { - unsigned int slot = PCI_SLOT(devfn); - struct tegra_pcie_port *port; - - list_for_each_entry(port, &pcie->ports, list) { - if (port->index + 1 == slot) { - addr = port->base + (where & ~3); - break; - } - } - } else { - unsigned int offset; - u32 base; - - offset = tegra_pcie_conf_offset(bus->number, devfn, where); - - /* move 4 KiB window to offset within the FPCI region */ - base = 0xfe100000 + ((offset & ~(SZ_4K - 1)) >> 8); - afi_writel(pcie, base, AFI_FPCI_BAR0); - - /* move to correct offset within the 4 KiB page */ - addr = pcie->cfg + (offset & (SZ_4K - 1)); - } - - return addr; -} - -static int tegra_pcie_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *value) -{ - if (bus->number == 0) - return pci_generic_config_read32(bus, devfn, where, size, - value); - - return pci_generic_config_read(bus, devfn, where, size, value); -} - -static int tegra_pcie_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 value) -{ - if (bus->number == 0) - return pci_generic_config_write32(bus, devfn, where, size, - value); - - return pci_generic_config_write(bus, devfn, where, size, value); -} - -static struct pci_ops tegra_pcie_ops = { - .map_bus = tegra_pcie_map_bus, - .read = tegra_pcie_config_read, - .write = tegra_pcie_config_write, -}; - -static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) -{ - unsigned long ret = 0; - - switch (port->index) { - case 0: - ret = AFI_PEX0_CTRL; - break; - - case 1: - ret = AFI_PEX1_CTRL; - break; - - case 2: - ret = AFI_PEX2_CTRL; - break; - } - - return ret; -} - -static void tegra_pcie_port_reset(struct tegra_pcie_port *port) -{ - unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); - unsigned long value; - - /* pulse reset signal */ - value = afi_readl(port->pcie, ctrl); - value &= ~AFI_PEX_CTRL_RST; - afi_writel(port->pcie, value, ctrl); - - usleep_range(1000, 2000); - - value = afi_readl(port->pcie, ctrl); - value |= AFI_PEX_CTRL_RST; - afi_writel(port->pcie, value, ctrl); -} - -static void tegra_pcie_port_enable(struct tegra_pcie_port *port) -{ - unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); - const struct tegra_pcie_soc *soc = port->pcie->soc; - unsigned long value; - - /* enable reference clock */ - value = afi_readl(port->pcie, ctrl); - value |= AFI_PEX_CTRL_REFCLK_EN; - - if (soc->has_pex_clkreq_en) - value |= AFI_PEX_CTRL_CLKREQ_EN; - - value |= AFI_PEX_CTRL_OVERRIDE_EN; - - afi_writel(port->pcie, value, ctrl); - - tegra_pcie_port_reset(port); - - if (soc->force_pca_enable) { - value = readl(port->base + RP_VEND_CTL2); - value |= RP_VEND_CTL2_PCA_ENABLE; - writel(value, port->base + RP_VEND_CTL2); - } -} - -static void tegra_pcie_port_disable(struct tegra_pcie_port *port) -{ - unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); - const struct tegra_pcie_soc *soc = port->pcie->soc; - unsigned long value; - - /* assert port reset */ - value = afi_readl(port->pcie, ctrl); - value &= ~AFI_PEX_CTRL_RST; - afi_writel(port->pcie, value, ctrl); - - /* disable reference clock */ - value = afi_readl(port->pcie, ctrl); - - if (soc->has_pex_clkreq_en) - value &= ~AFI_PEX_CTRL_CLKREQ_EN; - - value &= ~AFI_PEX_CTRL_REFCLK_EN; - afi_writel(port->pcie, value, ctrl); -} - -static void tegra_pcie_port_free(struct tegra_pcie_port *port) -{ - struct tegra_pcie *pcie = port->pcie; - struct device *dev = pcie->dev; - - devm_iounmap(dev, port->base); - devm_release_mem_region(dev, port->regs.start, - resource_size(&port->regs)); - list_del(&port->list); - devm_kfree(dev, port); -} - -/* Tegra PCIE root complex wrongly reports device class */ -static void tegra_pcie_fixup_class(struct pci_dev *dev) -{ - dev->class = PCI_CLASS_BRIDGE_PCI << 8; -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_fixup_class); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_fixup_class); - -/* Tegra PCIE requires relaxed ordering */ -static void tegra_pcie_relax_enable(struct pci_dev *dev) -{ - pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); -} -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); - -static int tegra_pcie_request_resources(struct tegra_pcie *pcie) -{ - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct list_head *windows = &host->windows; - struct device *dev = pcie->dev; - int err; - - pci_add_resource_offset(windows, &pcie->pio, pcie->offset.io); - pci_add_resource_offset(windows, &pcie->mem, pcie->offset.mem); - pci_add_resource_offset(windows, &pcie->prefetch, pcie->offset.mem); - pci_add_resource(windows, &pcie->busn); - - err = devm_request_pci_bus_resources(dev, windows); - if (err < 0) { - pci_free_resource_list(windows); - return err; - } - - pci_remap_iospace(&pcie->pio, pcie->io.start); - - return 0; -} - -static void tegra_pcie_free_resources(struct tegra_pcie *pcie) -{ - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct list_head *windows = &host->windows; - - pci_unmap_iospace(&pcie->pio); - pci_free_resource_list(windows); -} - -static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) -{ - struct tegra_pcie *pcie = pdev->bus->sysdata; - int irq; - - tegra_cpuidle_pcie_irqs_in_use(); - - irq = of_irq_parse_and_map_pci(pdev, slot, pin); - if (!irq) - irq = pcie->irq; - - return irq; -} - -static irqreturn_t tegra_pcie_isr(int irq, void *arg) -{ - const char *err_msg[] = { - "Unknown", - "AXI slave error", - "AXI decode error", - "Target abort", - "Master abort", - "Invalid write", - "Legacy interrupt", - "Response decoding error", - "AXI response decoding error", - "Transaction timeout", - "Slot present pin change", - "Slot clock request change", - "TMS clock ramp change", - "TMS ready for power down", - "Peer2Peer error", - }; - struct tegra_pcie *pcie = arg; - struct device *dev = pcie->dev; - u32 code, signature; - - code = afi_readl(pcie, AFI_INTR_CODE) & AFI_INTR_CODE_MASK; - signature = afi_readl(pcie, AFI_INTR_SIGNATURE); - afi_writel(pcie, 0, AFI_INTR_CODE); - - if (code == AFI_INTR_LEGACY) - return IRQ_NONE; - - if (code >= ARRAY_SIZE(err_msg)) - code = 0; - - /* - * do not pollute kernel log with master abort reports since they - * happen a lot during enumeration - */ - if (code == AFI_INTR_MASTER_ABORT) - dev_dbg(dev, "%s, signature: %08x\n", err_msg[code], signature); - else - dev_err(dev, "%s, signature: %08x\n", err_msg[code], signature); - - if (code == AFI_INTR_TARGET_ABORT || code == AFI_INTR_MASTER_ABORT || - code == AFI_INTR_FPCI_DECODE_ERROR) { - u32 fpci = afi_readl(pcie, AFI_UPPER_FPCI_ADDRESS) & 0xff; - u64 address = (u64)fpci << 32 | (signature & 0xfffffffc); - - if (code == AFI_INTR_MASTER_ABORT) - dev_dbg(dev, " FPCI address: %10llx\n", address); - else - dev_err(dev, " FPCI address: %10llx\n", address); - } - - return IRQ_HANDLED; -} - -/* - * FPCI map is as follows: - * - 0xfdfc000000: I/O space - * - 0xfdfe000000: type 0 configuration space - * - 0xfdff000000: type 1 configuration space - * - 0xfe00000000: type 0 extended configuration space - * - 0xfe10000000: type 1 extended configuration space - */ -static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) -{ - u32 fpci_bar, size, axi_address; - - /* Bar 0: type 1 extended configuration space */ - size = resource_size(&pcie->cs); - afi_writel(pcie, pcie->cs.start, AFI_AXI_BAR0_START); - afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); - - /* Bar 1: downstream IO bar */ - fpci_bar = 0xfdfc0000; - size = resource_size(&pcie->io); - axi_address = pcie->io.start; - afi_writel(pcie, axi_address, AFI_AXI_BAR1_START); - afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); - afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1); - - /* Bar 2: prefetchable memory BAR */ - fpci_bar = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1; - size = resource_size(&pcie->prefetch); - axi_address = pcie->prefetch.start; - afi_writel(pcie, axi_address, AFI_AXI_BAR2_START); - afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ); - afi_writel(pcie, fpci_bar, AFI_FPCI_BAR2); - - /* Bar 3: non prefetchable memory BAR */ - fpci_bar = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1; - size = resource_size(&pcie->mem); - axi_address = pcie->mem.start; - afi_writel(pcie, axi_address, AFI_AXI_BAR3_START); - afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ); - afi_writel(pcie, fpci_bar, AFI_FPCI_BAR3); - - /* NULL out the remaining BARs as they are not used */ - afi_writel(pcie, 0, AFI_AXI_BAR4_START); - afi_writel(pcie, 0, AFI_AXI_BAR4_SZ); - afi_writel(pcie, 0, AFI_FPCI_BAR4); - - afi_writel(pcie, 0, AFI_AXI_BAR5_START); - afi_writel(pcie, 0, AFI_AXI_BAR5_SZ); - afi_writel(pcie, 0, AFI_FPCI_BAR5); - - /* map all upstream transactions as uncached */ - afi_writel(pcie, 0, AFI_CACHE_BAR0_ST); - afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); - afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); - afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); - - /* MSI translations are setup only when needed */ - afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST); - afi_writel(pcie, 0, AFI_MSI_BAR_SZ); - afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST); - afi_writel(pcie, 0, AFI_MSI_BAR_SZ); -} - -static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout) -{ - const struct tegra_pcie_soc *soc = pcie->soc; - u32 value; - - timeout = jiffies + msecs_to_jiffies(timeout); - - while (time_before(jiffies, timeout)) { - value = pads_readl(pcie, soc->pads_pll_ctl); - if (value & PADS_PLL_CTL_LOCKDET) - return 0; - } - - return -ETIMEDOUT; -} - -static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; - u32 value; - int err; - - /* initialize internal PHY, enable up to 16 PCIE lanes */ - pads_writel(pcie, 0x0, PADS_CTL_SEL); - - /* override IDDQ to 1 on all 4 lanes */ - value = pads_readl(pcie, PADS_CTL); - value |= PADS_CTL_IDDQ_1L; - pads_writel(pcie, value, PADS_CTL); - - /* - * Set up PHY PLL inputs select PLLE output as refclock, - * set TX ref sel to div10 (not div5). - */ - value = pads_readl(pcie, soc->pads_pll_ctl); - value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); - value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel; - pads_writel(pcie, value, soc->pads_pll_ctl); - - /* reset PLL */ - value = pads_readl(pcie, soc->pads_pll_ctl); - value &= ~PADS_PLL_CTL_RST_B4SM; - pads_writel(pcie, value, soc->pads_pll_ctl); - - usleep_range(20, 100); - - /* take PLL out of reset */ - value = pads_readl(pcie, soc->pads_pll_ctl); - value |= PADS_PLL_CTL_RST_B4SM; - pads_writel(pcie, value, soc->pads_pll_ctl); - - /* wait for the PLL to lock */ - err = tegra_pcie_pll_wait(pcie, 500); - if (err < 0) { - dev_err(dev, "PLL failed to lock: %d\n", err); - return err; - } - - /* turn off IDDQ override */ - value = pads_readl(pcie, PADS_CTL); - value &= ~PADS_CTL_IDDQ_1L; - pads_writel(pcie, value, PADS_CTL); - - /* enable TX/RX data */ - value = pads_readl(pcie, PADS_CTL); - value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L; - pads_writel(pcie, value, PADS_CTL); - - return 0; -} - -static int tegra_pcie_phy_disable(struct tegra_pcie *pcie) -{ - const struct tegra_pcie_soc *soc = pcie->soc; - u32 value; - - /* disable TX/RX data */ - value = pads_readl(pcie, PADS_CTL); - value &= ~(PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L); - pads_writel(pcie, value, PADS_CTL); - - /* override IDDQ */ - value = pads_readl(pcie, PADS_CTL); - value |= PADS_CTL_IDDQ_1L; - pads_writel(pcie, value, PADS_CTL); - - /* reset PLL */ - value = pads_readl(pcie, soc->pads_pll_ctl); - value &= ~PADS_PLL_CTL_RST_B4SM; - pads_writel(pcie, value, soc->pads_pll_ctl); - - usleep_range(20, 100); - - return 0; -} - -static int tegra_pcie_port_phy_power_on(struct tegra_pcie_port *port) -{ - struct device *dev = port->pcie->dev; - unsigned int i; - int err; - - for (i = 0; i < port->lanes; i++) { - err = phy_power_on(port->phys[i]); - if (err < 0) { - dev_err(dev, "failed to power on PHY#%u: %d\n", i, err); - return err; - } - } - - return 0; -} - -static int tegra_pcie_port_phy_power_off(struct tegra_pcie_port *port) -{ - struct device *dev = port->pcie->dev; - unsigned int i; - int err; - - for (i = 0; i < port->lanes; i++) { - err = phy_power_off(port->phys[i]); - if (err < 0) { - dev_err(dev, "failed to power off PHY#%u: %d\n", i, - err); - return err; - } - } - - return 0; -} - -static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; - struct tegra_pcie_port *port; - int err; - - if (pcie->legacy_phy) { - if (pcie->phy) - err = phy_power_on(pcie->phy); - else - err = tegra_pcie_phy_enable(pcie); - - if (err < 0) - dev_err(dev, "failed to power on PHY: %d\n", err); - - return err; - } - - list_for_each_entry(port, &pcie->ports, list) { - err = tegra_pcie_port_phy_power_on(port); - if (err < 0) { - dev_err(dev, - "failed to power on PCIe port %u PHY: %d\n", - port->index, err); - return err; - } - } - - /* Configure the reference clock driver */ - pads_writel(pcie, soc->pads_refclk_cfg0, PADS_REFCLK_CFG0); - - if (soc->num_ports > 2) - pads_writel(pcie, soc->pads_refclk_cfg1, PADS_REFCLK_CFG1); - - return 0; -} - -static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct tegra_pcie_port *port; - int err; - - if (pcie->legacy_phy) { - if (pcie->phy) - err = phy_power_off(pcie->phy); - else - err = tegra_pcie_phy_disable(pcie); - - if (err < 0) - dev_err(dev, "failed to power off PHY: %d\n", err); - - return err; - } - - list_for_each_entry(port, &pcie->ports, list) { - err = tegra_pcie_port_phy_power_off(port); - if (err < 0) { - dev_err(dev, - "failed to power off PCIe port %u PHY: %d\n", - port->index, err); - return err; - } - } - - return 0; -} - -static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; - struct tegra_pcie_port *port; - unsigned long value; - int err; - - /* enable PLL power down */ - if (pcie->phy) { - value = afi_readl(pcie, AFI_PLLE_CONTROL); - value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; - value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; - afi_writel(pcie, value, AFI_PLLE_CONTROL); - } - - /* power down PCIe slot clock bias pad */ - if (soc->has_pex_bias_ctrl) - afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0); - - /* configure mode and disable all ports */ - value = afi_readl(pcie, AFI_PCIE_CONFIG); - value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; - value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config; - - list_for_each_entry(port, &pcie->ports, list) - value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); - - afi_writel(pcie, value, AFI_PCIE_CONFIG); - - if (soc->has_gen2) { - value = afi_readl(pcie, AFI_FUSE); - value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; - afi_writel(pcie, value, AFI_FUSE); - } else { - value = afi_readl(pcie, AFI_FUSE); - value |= AFI_FUSE_PCIE_T0_GEN2_DIS; - afi_writel(pcie, value, AFI_FUSE); - } - - if (soc->program_uphy) { - err = tegra_pcie_phy_power_on(pcie); - if (err < 0) { - dev_err(dev, "failed to power on PHY(s): %d\n", err); - return err; - } - } - - /* take the PCIe interface module out of reset */ - reset_control_deassert(pcie->pcie_xrst); - - /* finally enable PCIe */ - value = afi_readl(pcie, AFI_CONFIGURATION); - value |= AFI_CONFIGURATION_EN_FPCI; - afi_writel(pcie, value, AFI_CONFIGURATION); - - value = AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | - AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR | - AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR; - - if (soc->has_intr_prsnt_sense) - value |= AFI_INTR_EN_PRSNT_SENSE; - - afi_writel(pcie, value, AFI_AFI_INTR_ENABLE); - afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE); - - /* don't enable MSI for now, only when needed */ - afi_writel(pcie, AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); - - /* disable all exceptions */ - afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS); - - return 0; -} - -static void tegra_pcie_disable_controller(struct tegra_pcie *pcie) -{ - int err; - - reset_control_assert(pcie->pcie_xrst); - - if (pcie->soc->program_uphy) { - err = tegra_pcie_phy_power_off(pcie); - if (err < 0) - dev_err(pcie->dev, "failed to power off PHY(s): %d\n", - err); - } -} - -static void tegra_pcie_power_off(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; - int err; - - reset_control_assert(pcie->afi_rst); - reset_control_assert(pcie->pex_rst); - - clk_disable_unprepare(pcie->pll_e); - if (soc->has_cml_clk) - clk_disable_unprepare(pcie->cml_clk); - clk_disable_unprepare(pcie->afi_clk); - clk_disable_unprepare(pcie->pex_clk); - - if (!dev->pm_domain) - tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); - - err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies); - if (err < 0) - dev_warn(dev, "failed to disable regulators: %d\n", err); -} - -static int tegra_pcie_power_on(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; - int err; - - reset_control_assert(pcie->pcie_xrst); - reset_control_assert(pcie->afi_rst); - reset_control_assert(pcie->pex_rst); - - if (!dev->pm_domain) - tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); - - /* enable regulators */ - err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies); - if (err < 0) - dev_err(dev, "failed to enable regulators: %d\n", err); - - if (dev->pm_domain) { - err = clk_prepare_enable(pcie->pex_clk); - if (err) { - dev_err(dev, "failed to enable PEX clock: %d\n", err); - return err; - } - reset_control_deassert(pcie->pex_rst); - } else { - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, - pcie->pex_clk, - pcie->pex_rst); - if (err) { - dev_err(dev, "powerup sequence failed: %d\n", err); - return err; - } - } - - reset_control_deassert(pcie->afi_rst); - - err = clk_prepare_enable(pcie->afi_clk); - if (err < 0) { - dev_err(dev, "failed to enable AFI clock: %d\n", err); - return err; - } - - if (soc->has_cml_clk) { - err = clk_prepare_enable(pcie->cml_clk); - if (err < 0) { - dev_err(dev, "failed to enable CML clock: %d\n", err); - return err; - } - } - - err = clk_prepare_enable(pcie->pll_e); - if (err < 0) { - dev_err(dev, "failed to enable PLLE clock: %d\n", err); - return err; - } - - return 0; -} - -static int tegra_pcie_clocks_get(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; - - pcie->pex_clk = devm_clk_get(dev, "pex"); - if (IS_ERR(pcie->pex_clk)) - return PTR_ERR(pcie->pex_clk); - - pcie->afi_clk = devm_clk_get(dev, "afi"); - if (IS_ERR(pcie->afi_clk)) - return PTR_ERR(pcie->afi_clk); - - pcie->pll_e = devm_clk_get(dev, "pll_e"); - if (IS_ERR(pcie->pll_e)) - return PTR_ERR(pcie->pll_e); - - if (soc->has_cml_clk) { - pcie->cml_clk = devm_clk_get(dev, "cml"); - if (IS_ERR(pcie->cml_clk)) - return PTR_ERR(pcie->cml_clk); - } - - return 0; -} - -static int tegra_pcie_resets_get(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - - pcie->pex_rst = devm_reset_control_get_exclusive(dev, "pex"); - if (IS_ERR(pcie->pex_rst)) - return PTR_ERR(pcie->pex_rst); - - pcie->afi_rst = devm_reset_control_get_exclusive(dev, "afi"); - if (IS_ERR(pcie->afi_rst)) - return PTR_ERR(pcie->afi_rst); - - pcie->pcie_xrst = devm_reset_control_get_exclusive(dev, "pcie_x"); - if (IS_ERR(pcie->pcie_xrst)) - return PTR_ERR(pcie->pcie_xrst); - - return 0; -} - -static int tegra_pcie_phys_get_legacy(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - int err; - - pcie->phy = devm_phy_optional_get(dev, "pcie"); - if (IS_ERR(pcie->phy)) { - err = PTR_ERR(pcie->phy); - dev_err(dev, "failed to get PHY: %d\n", err); - return err; - } - - err = phy_init(pcie->phy); - if (err < 0) { - dev_err(dev, "failed to initialize PHY: %d\n", err); - return err; - } - - pcie->legacy_phy = true; - - return 0; -} - -static struct phy *devm_of_phy_optional_get_index(struct device *dev, - struct device_node *np, - const char *consumer, - unsigned int index) -{ - struct phy *phy; - char *name; - - name = kasprintf(GFP_KERNEL, "%s-%u", consumer, index); - if (!name) - return ERR_PTR(-ENOMEM); - - phy = devm_of_phy_get(dev, np, name); - kfree(name); - - if (IS_ERR(phy) && PTR_ERR(phy) == -ENODEV) - phy = NULL; - - return phy; -} - -static int tegra_pcie_port_get_phys(struct tegra_pcie_port *port) -{ - struct device *dev = port->pcie->dev; - struct phy *phy; - unsigned int i; - int err; - - port->phys = devm_kcalloc(dev, sizeof(phy), port->lanes, GFP_KERNEL); - if (!port->phys) - return -ENOMEM; - - for (i = 0; i < port->lanes; i++) { - phy = devm_of_phy_optional_get_index(dev, port->np, "pcie", i); - if (IS_ERR(phy)) { - dev_err(dev, "failed to get PHY#%u: %ld\n", i, - PTR_ERR(phy)); - return PTR_ERR(phy); - } - - err = phy_init(phy); - if (err < 0) { - dev_err(dev, "failed to initialize PHY#%u: %d\n", i, - err); - return err; - } - - port->phys[i] = phy; - } - - return 0; -} - -static int tegra_pcie_phys_get(struct tegra_pcie *pcie) -{ - const struct tegra_pcie_soc *soc = pcie->soc; - struct device_node *np = pcie->dev->of_node; - struct tegra_pcie_port *port; - int err; - - if (!soc->has_gen2 || of_find_property(np, "phys", NULL) != NULL) - return tegra_pcie_phys_get_legacy(pcie); - - list_for_each_entry(port, &pcie->ports, list) { - err = tegra_pcie_port_get_phys(port); - if (err < 0) - return err; - } - - return 0; -} - -static void tegra_pcie_phys_put(struct tegra_pcie *pcie) -{ - struct tegra_pcie_port *port; - struct device *dev = pcie->dev; - int err, i; - - if (pcie->legacy_phy) { - err = phy_exit(pcie->phy); - if (err < 0) - dev_err(dev, "failed to teardown PHY: %d\n", err); - return; - } - - list_for_each_entry(port, &pcie->ports, list) { - for (i = 0; i < port->lanes; i++) { - err = phy_exit(port->phys[i]); - if (err < 0) - dev_err(dev, "failed to teardown PHY#%u: %d\n", - i, err); - } - } -} - - -static int tegra_pcie_get_resources(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct platform_device *pdev = to_platform_device(dev); - struct resource *pads, *afi, *res; - const struct tegra_pcie_soc *soc = pcie->soc; - int err; - - err = tegra_pcie_clocks_get(pcie); - if (err) { - dev_err(dev, "failed to get clocks: %d\n", err); - return err; - } - - err = tegra_pcie_resets_get(pcie); - if (err) { - dev_err(dev, "failed to get resets: %d\n", err); - return err; - } - - if (soc->program_uphy) { - err = tegra_pcie_phys_get(pcie); - if (err < 0) { - dev_err(dev, "failed to get PHYs: %d\n", err); - return err; - } - } - - pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); - pcie->pads = devm_ioremap_resource(dev, pads); - if (IS_ERR(pcie->pads)) { - err = PTR_ERR(pcie->pads); - goto phys_put; - } - - afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); - pcie->afi = devm_ioremap_resource(dev, afi); - if (IS_ERR(pcie->afi)) { - err = PTR_ERR(pcie->afi); - goto phys_put; - } - - /* request configuration space, but remap later, on demand */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); - if (!res) { - err = -EADDRNOTAVAIL; - goto phys_put; - } - - pcie->cs = *res; - - /* constrain configuration space to 4 KiB */ - pcie->cs.end = pcie->cs.start + SZ_4K - 1; - - pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); - if (IS_ERR(pcie->cfg)) { - err = PTR_ERR(pcie->cfg); - goto phys_put; - } - - /* request interrupt */ - err = platform_get_irq_byname(pdev, "intr"); - if (err < 0) { - dev_err(dev, "failed to get IRQ: %d\n", err); - goto phys_put; - } - - pcie->irq = err; - - err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); - if (err) { - dev_err(dev, "failed to register IRQ: %d\n", err); - goto phys_put; - } - - return 0; - -phys_put: - if (soc->program_uphy) - tegra_pcie_phys_put(pcie); - return err; -} - -static int tegra_pcie_put_resources(struct tegra_pcie *pcie) -{ - const struct tegra_pcie_soc *soc = pcie->soc; - - if (pcie->irq > 0) - free_irq(pcie->irq, pcie); - - if (soc->program_uphy) - tegra_pcie_phys_put(pcie); - - return 0; -} - -static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port) -{ - struct tegra_pcie *pcie = port->pcie; - const struct tegra_pcie_soc *soc = pcie->soc; - int err; - u32 val; - u8 ack_bit; - - val = afi_readl(pcie, AFI_PCIE_PME); - val |= (0x1 << soc->ports[port->index].pme.turnoff_bit); - afi_writel(pcie, val, AFI_PCIE_PME); - - ack_bit = soc->ports[port->index].pme.ack_bit; - err = readl_poll_timeout(pcie->afi + AFI_PCIE_PME, val, - val & (0x1 << ack_bit), 1, PME_ACK_TIMEOUT); - if (err) - dev_err(pcie->dev, "PME Ack is not received on port: %d\n", - port->index); - - usleep_range(10000, 11000); - - val = afi_readl(pcie, AFI_PCIE_PME); - val &= ~(0x1 << soc->ports[port->index].pme.turnoff_bit); - afi_writel(pcie, val, AFI_PCIE_PME); -} - -static int tegra_msi_alloc(struct tegra_msi *chip) -{ - int msi; - - mutex_lock(&chip->lock); - - msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR); - if (msi < INT_PCI_MSI_NR) - set_bit(msi, chip->used); - else - msi = -ENOSPC; - - mutex_unlock(&chip->lock); - - return msi; -} - -static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq) -{ - struct device *dev = chip->chip.dev; - - mutex_lock(&chip->lock); - - if (!test_bit(irq, chip->used)) - dev_err(dev, "trying to free unused MSI#%lu\n", irq); - else - clear_bit(irq, chip->used); - - mutex_unlock(&chip->lock); -} - -static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) -{ - struct tegra_pcie *pcie = data; - struct device *dev = pcie->dev; - struct tegra_msi *msi = &pcie->msi; - unsigned int i, processed = 0; - - for (i = 0; i < 8; i++) { - unsigned long reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); - - while (reg) { - unsigned int offset = find_first_bit(®, 32); - unsigned int index = i * 32 + offset; - unsigned int irq; - - /* clear the interrupt */ - afi_writel(pcie, 1 << offset, AFI_MSI_VEC0 + i * 4); - - irq = irq_find_mapping(msi->domain, index); - if (irq) { - if (test_bit(index, msi->used)) - generic_handle_irq(irq); - else - dev_info(dev, "unhandled MSI\n"); - } else { - /* - * that's weird who triggered this? - * just clear it - */ - dev_info(dev, "unexpected MSI\n"); - } - - /* see if there's any more pending in this vector */ - reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); - - processed++; - } - } - - return processed > 0 ? IRQ_HANDLED : IRQ_NONE; -} - -static int tegra_msi_setup_irq(struct msi_controller *chip, - struct pci_dev *pdev, struct msi_desc *desc) -{ - struct tegra_msi *msi = to_tegra_msi(chip); - struct msi_msg msg; - unsigned int irq; - int hwirq; - - hwirq = tegra_msi_alloc(msi); - if (hwirq < 0) - return hwirq; - - irq = irq_create_mapping(msi->domain, hwirq); - if (!irq) { - tegra_msi_free(msi, hwirq); - return -EINVAL; - } - - irq_set_msi_desc(irq, desc); - - msg.address_lo = lower_32_bits(msi->phys); - msg.address_hi = upper_32_bits(msi->phys); - msg.data = hwirq; - - pci_write_msi_msg(irq, &msg); - - return 0; -} - -static void tegra_msi_teardown_irq(struct msi_controller *chip, - unsigned int irq) -{ - struct tegra_msi *msi = to_tegra_msi(chip); - struct irq_data *d = irq_get_irq_data(irq); - irq_hw_number_t hwirq = irqd_to_hwirq(d); - - irq_dispose_mapping(irq); - tegra_msi_free(msi, hwirq); -} - -static struct irq_chip tegra_msi_irq_chip = { - .name = "Tegra PCIe MSI", - .irq_enable = pci_msi_unmask_irq, - .irq_disable = pci_msi_mask_irq, - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; - -static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - - tegra_cpuidle_pcie_irqs_in_use(); - - return 0; -} - -static const struct irq_domain_ops msi_domain_ops = { - .map = tegra_msi_map, -}; - -static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) -{ - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct platform_device *pdev = to_platform_device(pcie->dev); - struct tegra_msi *msi = &pcie->msi; - struct device *dev = pcie->dev; - int err; - - mutex_init(&msi->lock); - - msi->chip.dev = dev; - msi->chip.setup_irq = tegra_msi_setup_irq; - msi->chip.teardown_irq = tegra_msi_teardown_irq; - - msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR, - &msi_domain_ops, &msi->chip); - if (!msi->domain) { - dev_err(dev, "failed to create IRQ domain\n"); - return -ENOMEM; - } - - err = platform_get_irq_byname(pdev, "msi"); - if (err < 0) { - dev_err(dev, "failed to get IRQ: %d\n", err); - goto err; - } - - msi->irq = err; - - err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD, - tegra_msi_irq_chip.name, pcie); - if (err < 0) { - dev_err(dev, "failed to request IRQ: %d\n", err); - goto err; - } - - /* setup AFI/FPCI range */ - msi->pages = __get_free_pages(GFP_KERNEL, 0); - msi->phys = virt_to_phys((void *)msi->pages); - host->msi = &msi->chip; - - return 0; - -err: - irq_domain_remove(msi->domain); - return err; -} - -static void tegra_pcie_enable_msi(struct tegra_pcie *pcie) -{ - const struct tegra_pcie_soc *soc = pcie->soc; - struct tegra_msi *msi = &pcie->msi; - u32 reg; - - afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); - afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); - /* this register is in 4K increments */ - afi_writel(pcie, 1, AFI_MSI_BAR_SZ); - - /* enable all MSI vectors */ - afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC0); - afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC1); - afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC2); - afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC3); - afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC4); - afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC5); - afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC6); - afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC7); - - /* and unmask the MSI interrupt */ - reg = afi_readl(pcie, AFI_INTR_MASK); - reg |= AFI_INTR_MASK_MSI_MASK; - afi_writel(pcie, reg, AFI_INTR_MASK); -} - -static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie) -{ - struct tegra_msi *msi = &pcie->msi; - unsigned int i, irq; - - free_pages(msi->pages, 0); - - if (msi->irq > 0) - free_irq(msi->irq, pcie); - - for (i = 0; i < INT_PCI_MSI_NR; i++) { - irq = irq_find_mapping(msi->domain, i); - if (irq > 0) - irq_dispose_mapping(irq); - } - - irq_domain_remove(msi->domain); -} - -static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) -{ - u32 value; - - /* mask the MSI interrupt */ - value = afi_readl(pcie, AFI_INTR_MASK); - value &= ~AFI_INTR_MASK_MSI_MASK; - afi_writel(pcie, value, AFI_INTR_MASK); - - /* disable all MSI vectors */ - afi_writel(pcie, 0, AFI_MSI_EN_VEC0); - afi_writel(pcie, 0, AFI_MSI_EN_VEC1); - afi_writel(pcie, 0, AFI_MSI_EN_VEC2); - afi_writel(pcie, 0, AFI_MSI_EN_VEC3); - afi_writel(pcie, 0, AFI_MSI_EN_VEC4); - afi_writel(pcie, 0, AFI_MSI_EN_VEC5); - afi_writel(pcie, 0, AFI_MSI_EN_VEC6); - afi_writel(pcie, 0, AFI_MSI_EN_VEC7); - - return 0; -} - -static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, - u32 *xbar) -{ - struct device *dev = pcie->dev; - struct device_node *np = dev->of_node; - - if (of_device_is_compatible(np, "nvidia,tegra186-pcie")) { - switch (lanes) { - case 0x010004: - dev_info(dev, "4x1, 1x1 configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_401; - return 0; - - case 0x010102: - dev_info(dev, "2x1, 1X1, 1x1 configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211; - return 0; - - case 0x010101: - dev_info(dev, "1x1, 1x1, 1x1 configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_111; - return 0; - - default: - dev_info(dev, "wrong configuration updated in DT, " - "switching to default 2x1, 1x1, 1x1 " - "configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211; - return 0; - } - } else if (of_device_is_compatible(np, "nvidia,tegra124-pcie") || - of_device_is_compatible(np, "nvidia,tegra210-pcie")) { - switch (lanes) { - case 0x0000104: - dev_info(dev, "4x1, 1x1 configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1; - return 0; - - case 0x0000102: - dev_info(dev, "2x1, 1x1 configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1; - return 0; - } - } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { - switch (lanes) { - case 0x00000204: - dev_info(dev, "4x1, 2x1 configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420; - return 0; - - case 0x00020202: - dev_info(dev, "2x3 configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222; - return 0; - - case 0x00010104: - dev_info(dev, "4x1, 1x2 configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411; - return 0; - } - } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) { - switch (lanes) { - case 0x00000004: - dev_info(dev, "single-mode configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE; - return 0; - - case 0x00000202: - dev_info(dev, "dual-mode configuration\n"); - *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; - return 0; - } - } - - return -EINVAL; -} - -/* - * Check whether a given set of supplies is available in a device tree node. - * This is used to check whether the new or the legacy device tree bindings - * should be used. - */ -static bool of_regulator_bulk_available(struct device_node *np, - struct regulator_bulk_data *supplies, - unsigned int num_supplies) -{ - char property[32]; - unsigned int i; - - for (i = 0; i < num_supplies; i++) { - snprintf(property, 32, "%s-supply", supplies[i].supply); - - if (of_find_property(np, property, NULL) == NULL) - return false; - } - - return true; -} - -/* - * Old versions of the device tree binding for this device used a set of power - * supplies that didn't match the hardware inputs. This happened to work for a - * number of cases but is not future proof. However to preserve backwards- - * compatibility with old device trees, this function will try to use the old - * set of supplies. - */ -static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct device_node *np = dev->of_node; - - if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) - pcie->num_supplies = 3; - else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) - pcie->num_supplies = 2; - - if (pcie->num_supplies == 0) { - dev_err(dev, "device %pOF not supported in legacy mode\n", np); - return -ENODEV; - } - - pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, - sizeof(*pcie->supplies), - GFP_KERNEL); - if (!pcie->supplies) - return -ENOMEM; - - pcie->supplies[0].supply = "pex-clk"; - pcie->supplies[1].supply = "vdd"; - - if (pcie->num_supplies > 2) - pcie->supplies[2].supply = "avdd"; - - return devm_regulator_bulk_get(dev, pcie->num_supplies, pcie->supplies); -} - -/* - * Obtains the list of regulators required for a particular generation of the - * IP block. - * - * This would've been nice to do simply by providing static tables for use - * with the regulator_bulk_*() API, but unfortunately Tegra30 is a bit quirky - * in that it has two pairs or AVDD_PEX and VDD_PEX supplies (PEXA and PEXB) - * and either seems to be optional depending on which ports are being used. - */ -static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) -{ - struct device *dev = pcie->dev; - struct device_node *np = dev->of_node; - unsigned int i = 0; - - if (of_device_is_compatible(np, "nvidia,tegra186-pcie")) { - pcie->num_supplies = 4; - - pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, - sizeof(*pcie->supplies), - GFP_KERNEL); - if (!pcie->supplies) - return -ENOMEM; - - pcie->supplies[i++].supply = "dvdd-pex"; - pcie->supplies[i++].supply = "hvdd-pex-pll"; - pcie->supplies[i++].supply = "hvdd-pex"; - pcie->supplies[i++].supply = "vddio-pexctl-aud"; - } else if (of_device_is_compatible(np, "nvidia,tegra210-pcie")) { - pcie->num_supplies = 6; - - pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, - sizeof(*pcie->supplies), - GFP_KERNEL); - if (!pcie->supplies) - return -ENOMEM; - - pcie->supplies[i++].supply = "avdd-pll-uerefe"; - pcie->supplies[i++].supply = "hvddio-pex"; - pcie->supplies[i++].supply = "dvddio-pex"; - pcie->supplies[i++].supply = "dvdd-pex-pll"; - pcie->supplies[i++].supply = "hvdd-pex-pll-e"; - pcie->supplies[i++].supply = "vddio-pex-ctl"; - } else if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { - pcie->num_supplies = 7; - - pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, - sizeof(*pcie->supplies), - GFP_KERNEL); - if (!pcie->supplies) - return -ENOMEM; - - pcie->supplies[i++].supply = "avddio-pex"; - pcie->supplies[i++].supply = "dvddio-pex"; - pcie->supplies[i++].supply = "avdd-pex-pll"; - pcie->supplies[i++].supply = "hvdd-pex"; - pcie->supplies[i++].supply = "hvdd-pex-pll-e"; - pcie->supplies[i++].supply = "vddio-pex-ctl"; - pcie->supplies[i++].supply = "avdd-pll-erefe"; - } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { - bool need_pexa = false, need_pexb = false; - - /* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */ - if (lane_mask & 0x0f) - need_pexa = true; - - /* VDD_PEXB and AVDD_PEXB supply lanes 4 to 5 */ - if (lane_mask & 0x30) - need_pexb = true; - - pcie->num_supplies = 4 + (need_pexa ? 2 : 0) + - (need_pexb ? 2 : 0); - - pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, - sizeof(*pcie->supplies), - GFP_KERNEL); - if (!pcie->supplies) - return -ENOMEM; - - pcie->supplies[i++].supply = "avdd-pex-pll"; - pcie->supplies[i++].supply = "hvdd-pex"; - pcie->supplies[i++].supply = "vddio-pex-ctl"; - pcie->supplies[i++].supply = "avdd-plle"; - - if (need_pexa) { - pcie->supplies[i++].supply = "avdd-pexa"; - pcie->supplies[i++].supply = "vdd-pexa"; - } - - if (need_pexb) { - pcie->supplies[i++].supply = "avdd-pexb"; - pcie->supplies[i++].supply = "vdd-pexb"; - } - } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) { - pcie->num_supplies = 5; - - pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, - sizeof(*pcie->supplies), - GFP_KERNEL); - if (!pcie->supplies) - return -ENOMEM; - - pcie->supplies[0].supply = "avdd-pex"; - pcie->supplies[1].supply = "vdd-pex"; - pcie->supplies[2].supply = "avdd-pex-pll"; - pcie->supplies[3].supply = "avdd-plle"; - pcie->supplies[4].supply = "vddio-pex-clk"; - } - - if (of_regulator_bulk_available(dev->of_node, pcie->supplies, - pcie->num_supplies)) - return devm_regulator_bulk_get(dev, pcie->num_supplies, - pcie->supplies); - - /* - * If not all regulators are available for this new scheme, assume - * that the device tree complies with an older version of the device - * tree binding. - */ - dev_info(dev, "using legacy DT binding for power supplies\n"); - - devm_kfree(dev, pcie->supplies); - pcie->num_supplies = 0; - - return tegra_pcie_get_legacy_regulators(pcie); -} - -static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct device_node *np = dev->of_node, *port; - const struct tegra_pcie_soc *soc = pcie->soc; - struct of_pci_range_parser parser; - struct of_pci_range range; - u32 lanes = 0, mask = 0; - unsigned int lane = 0; - struct resource res; - int err; - - if (of_pci_range_parser_init(&parser, np)) { - dev_err(dev, "missing \"ranges\" property\n"); - return -EINVAL; - } - - for_each_of_pci_range(&parser, &range) { - err = of_pci_range_to_resource(&range, np, &res); - if (err < 0) - return err; - - switch (res.flags & IORESOURCE_TYPE_BITS) { - case IORESOURCE_IO: - /* Track the bus -> CPU I/O mapping offset. */ - pcie->offset.io = res.start - range.pci_addr; - - memcpy(&pcie->pio, &res, sizeof(res)); - pcie->pio.name = np->full_name; - - /* - * The Tegra PCIe host bridge uses this to program the - * mapping of the I/O space to the physical address, - * so we override the .start and .end fields here that - * of_pci_range_to_resource() converted to I/O space. - * We also set the IORESOURCE_MEM type to clarify that - * the resource is in the physical memory space. - */ - pcie->io.start = range.cpu_addr; - pcie->io.end = range.cpu_addr + range.size - 1; - pcie->io.flags = IORESOURCE_MEM; - pcie->io.name = "I/O"; - - memcpy(&res, &pcie->io, sizeof(res)); - break; - - case IORESOURCE_MEM: - /* - * Track the bus -> CPU memory mapping offset. This - * assumes that the prefetchable and non-prefetchable - * regions will be the last of type IORESOURCE_MEM in - * the ranges property. - * */ - pcie->offset.mem = res.start - range.pci_addr; - - if (res.flags & IORESOURCE_PREFETCH) { - memcpy(&pcie->prefetch, &res, sizeof(res)); - pcie->prefetch.name = "prefetchable"; - } else { - memcpy(&pcie->mem, &res, sizeof(res)); - pcie->mem.name = "non-prefetchable"; - } - break; - } - } - - err = of_pci_parse_bus_range(np, &pcie->busn); - if (err < 0) { - dev_err(dev, "failed to parse ranges property: %d\n", err); - pcie->busn.name = np->name; - pcie->busn.start = 0; - pcie->busn.end = 0xff; - pcie->busn.flags = IORESOURCE_BUS; - } - - /* parse root ports */ - for_each_child_of_node(np, port) { - struct tegra_pcie_port *rp; - unsigned int index; - u32 value; - - err = of_pci_get_devfn(port); - if (err < 0) { - dev_err(dev, "failed to parse address: %d\n", err); - return err; - } - - index = PCI_SLOT(err); - - if (index < 1 || index > soc->num_ports) { - dev_err(dev, "invalid port number: %d\n", index); - return -EINVAL; - } - - index--; - - err = of_property_read_u32(port, "nvidia,num-lanes", &value); - if (err < 0) { - dev_err(dev, "failed to parse # of lanes: %d\n", - err); - return err; - } - - if (value > 16) { - dev_err(dev, "invalid # of lanes: %u\n", value); - return -EINVAL; - } - - lanes |= value << (index << 3); - - if (!of_device_is_available(port)) { - lane += value; - continue; - } - - mask |= ((1 << value) - 1) << lane; - lane += value; - - rp = devm_kzalloc(dev, sizeof(*rp), GFP_KERNEL); - if (!rp) - return -ENOMEM; - - err = of_address_to_resource(port, 0, &rp->regs); - if (err < 0) { - dev_err(dev, "failed to parse address: %d\n", err); - return err; - } - - INIT_LIST_HEAD(&rp->list); - rp->index = index; - rp->lanes = value; - rp->pcie = pcie; - rp->np = port; - - rp->base = devm_pci_remap_cfg_resource(dev, &rp->regs); - if (IS_ERR(rp->base)) - return PTR_ERR(rp->base); - - list_add_tail(&rp->list, &pcie->ports); - } - - err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config); - if (err < 0) { - dev_err(dev, "invalid lane configuration\n"); - return err; - } - - err = tegra_pcie_get_regulators(pcie, mask); - if (err < 0) - return err; - - return 0; -} - -/* - * FIXME: If there are no PCIe cards attached, then calling this function - * can result in the increase of the bootup time as there are big timeout - * loops. - */ -#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ -static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) -{ - struct device *dev = port->pcie->dev; - unsigned int retries = 3; - unsigned long value; - - /* override presence detection */ - value = readl(port->base + RP_PRIV_MISC); - value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; - value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; - writel(value, port->base + RP_PRIV_MISC); - - do { - unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT; - - do { - value = readl(port->base + RP_VEND_XP); - - if (value & RP_VEND_XP_DL_UP) - break; - - usleep_range(1000, 2000); - } while (--timeout); - - if (!timeout) { - dev_err(dev, "link %u down, retrying\n", port->index); - goto retry; - } - - timeout = TEGRA_PCIE_LINKUP_TIMEOUT; - - do { - value = readl(port->base + RP_LINK_CONTROL_STATUS); - - if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) - return true; - - usleep_range(1000, 2000); - } while (--timeout); - -retry: - tegra_pcie_port_reset(port); - } while (--retries); - - return false; -} - -static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct tegra_pcie_port *port, *tmp; - - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { - dev_info(dev, "probing port %u, using %u lanes\n", - port->index, port->lanes); - - tegra_pcie_port_enable(port); - - if (tegra_pcie_port_check_link(port)) - continue; - - dev_info(dev, "link %u down, ignoring\n", port->index); - - tegra_pcie_port_disable(port); - tegra_pcie_port_free(port); - } -} - -static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) -{ - struct tegra_pcie_port *port, *tmp; - - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - tegra_pcie_port_disable(port); -} - -static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { - { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, - { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, -}; - -static const struct tegra_pcie_soc tegra20_pcie = { - .num_ports = 2, - .ports = tegra20_pcie_ports, - .msi_base_shift = 0, - .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, - .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, - .pads_refclk_cfg0 = 0xfa5cfa5c, - .has_pex_clkreq_en = false, - .has_pex_bias_ctrl = false, - .has_intr_prsnt_sense = false, - .has_cml_clk = false, - .has_gen2 = false, - .force_pca_enable = false, - .program_uphy = true, -}; - -static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = { - { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, - { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, - { .pme.turnoff_bit = 16, .pme.ack_bit = 18 }, -}; - -static const struct tegra_pcie_soc tegra30_pcie = { - .num_ports = 3, - .ports = tegra30_pcie_ports, - .msi_base_shift = 8, - .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, - .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, - .pads_refclk_cfg0 = 0xfa5cfa5c, - .pads_refclk_cfg1 = 0xfa5cfa5c, - .has_pex_clkreq_en = true, - .has_pex_bias_ctrl = true, - .has_intr_prsnt_sense = true, - .has_cml_clk = true, - .has_gen2 = false, - .force_pca_enable = false, - .program_uphy = true, -}; - -static const struct tegra_pcie_soc tegra124_pcie = { - .num_ports = 2, - .ports = tegra20_pcie_ports, - .msi_base_shift = 8, - .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, - .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, - .pads_refclk_cfg0 = 0x44ac44ac, - .has_pex_clkreq_en = true, - .has_pex_bias_ctrl = true, - .has_intr_prsnt_sense = true, - .has_cml_clk = true, - .has_gen2 = true, - .force_pca_enable = false, - .program_uphy = true, -}; - -static const struct tegra_pcie_soc tegra210_pcie = { - .num_ports = 2, - .ports = tegra20_pcie_ports, - .msi_base_shift = 8, - .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, - .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, - .pads_refclk_cfg0 = 0x90b890b8, - .has_pex_clkreq_en = true, - .has_pex_bias_ctrl = true, - .has_intr_prsnt_sense = true, - .has_cml_clk = true, - .has_gen2 = true, - .force_pca_enable = true, - .program_uphy = true, -}; - -static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = { - { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, - { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, - { .pme.turnoff_bit = 12, .pme.ack_bit = 14 }, -}; - -static const struct tegra_pcie_soc tegra186_pcie = { - .num_ports = 3, - .ports = tegra186_pcie_ports, - .msi_base_shift = 8, - .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, - .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, - .pads_refclk_cfg0 = 0x80b880b8, - .pads_refclk_cfg1 = 0x000480b8, - .has_pex_clkreq_en = true, - .has_pex_bias_ctrl = true, - .has_intr_prsnt_sense = true, - .has_cml_clk = false, - .has_gen2 = true, - .force_pca_enable = false, - .program_uphy = false, -}; - -static const struct of_device_id tegra_pcie_of_match[] = { - { .compatible = "nvidia,tegra186-pcie", .data = &tegra186_pcie }, - { .compatible = "nvidia,tegra210-pcie", .data = &tegra210_pcie }, - { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie }, - { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie }, - { .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie }, - { }, -}; - -static void *tegra_pcie_ports_seq_start(struct seq_file *s, loff_t *pos) -{ - struct tegra_pcie *pcie = s->private; - - if (list_empty(&pcie->ports)) - return NULL; - - seq_printf(s, "Index Status\n"); - - return seq_list_start(&pcie->ports, *pos); -} - -static void *tegra_pcie_ports_seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - struct tegra_pcie *pcie = s->private; - - return seq_list_next(v, &pcie->ports, pos); -} - -static void tegra_pcie_ports_seq_stop(struct seq_file *s, void *v) -{ -} - -static int tegra_pcie_ports_seq_show(struct seq_file *s, void *v) -{ - bool up = false, active = false; - struct tegra_pcie_port *port; - unsigned int value; - - port = list_entry(v, struct tegra_pcie_port, list); - - value = readl(port->base + RP_VEND_XP); - - if (value & RP_VEND_XP_DL_UP) - up = true; - - value = readl(port->base + RP_LINK_CONTROL_STATUS); - - if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) - active = true; - - seq_printf(s, "%2u ", port->index); - - if (up) - seq_printf(s, "up"); - - if (active) { - if (up) - seq_printf(s, ", "); - - seq_printf(s, "active"); - } - - seq_printf(s, "\n"); - return 0; -} - -static const struct seq_operations tegra_pcie_ports_seq_ops = { - .start = tegra_pcie_ports_seq_start, - .next = tegra_pcie_ports_seq_next, - .stop = tegra_pcie_ports_seq_stop, - .show = tegra_pcie_ports_seq_show, -}; - -static int tegra_pcie_ports_open(struct inode *inode, struct file *file) -{ - struct tegra_pcie *pcie = inode->i_private; - struct seq_file *s; - int err; - - err = seq_open(file, &tegra_pcie_ports_seq_ops); - if (err) - return err; - - s = file->private_data; - s->private = pcie; - - return 0; -} - -static const struct file_operations tegra_pcie_ports_ops = { - .owner = THIS_MODULE, - .open = tegra_pcie_ports_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) -{ - debugfs_remove_recursive(pcie->debugfs); - pcie->debugfs = NULL; -} - -static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) -{ - struct dentry *file; - - pcie->debugfs = debugfs_create_dir("pcie", NULL); - if (!pcie->debugfs) - return -ENOMEM; - - file = debugfs_create_file("ports", S_IFREG | S_IRUGO, pcie->debugfs, - pcie, &tegra_pcie_ports_ops); - if (!file) - goto remove; - - return 0; - -remove: - tegra_pcie_debugfs_exit(pcie); - return -ENOMEM; -} - -static int tegra_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct pci_host_bridge *host; - struct tegra_pcie *pcie; - struct pci_bus *child; - int err; - - host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); - if (!host) - return -ENOMEM; - - pcie = pci_host_bridge_priv(host); - host->sysdata = pcie; - platform_set_drvdata(pdev, pcie); - - pcie->soc = of_device_get_match_data(dev); - INIT_LIST_HEAD(&pcie->ports); - pcie->dev = dev; - - err = tegra_pcie_parse_dt(pcie); - if (err < 0) - return err; - - err = tegra_pcie_get_resources(pcie); - if (err < 0) { - dev_err(dev, "failed to request resources: %d\n", err); - return err; - } - - err = tegra_pcie_msi_setup(pcie); - if (err < 0) { - dev_err(dev, "failed to enable MSI support: %d\n", err); - goto put_resources; - } - - pm_runtime_enable(pcie->dev); - err = pm_runtime_get_sync(pcie->dev); - if (err) { - dev_err(dev, "fail to enable pcie controller: %d\n", err); - goto teardown_msi; - } - - err = tegra_pcie_request_resources(pcie); - if (err) - goto pm_runtime_put; - - host->busnr = pcie->busn.start; - host->dev.parent = &pdev->dev; - host->ops = &tegra_pcie_ops; - host->map_irq = tegra_pcie_map_irq; - host->swizzle_irq = pci_common_swizzle; - - err = pci_scan_root_bus_bridge(host); - if (err < 0) { - dev_err(dev, "failed to register host: %d\n", err); - goto free_resources; - } - - pci_bus_size_bridges(host->bus); - pci_bus_assign_resources(host->bus); - - list_for_each_entry(child, &host->bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(host->bus); - - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_pcie_debugfs_init(pcie); - if (err < 0) - dev_err(dev, "failed to setup debugfs: %d\n", err); - } - - return 0; - -free_resources: - tegra_pcie_free_resources(pcie); -pm_runtime_put: - pm_runtime_put_sync(pcie->dev); - pm_runtime_disable(pcie->dev); -teardown_msi: - tegra_pcie_msi_teardown(pcie); -put_resources: - tegra_pcie_put_resources(pcie); - return err; -} - -static int tegra_pcie_remove(struct platform_device *pdev) -{ - struct tegra_pcie *pcie = platform_get_drvdata(pdev); - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct tegra_pcie_port *port, *tmp; - - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_pcie_debugfs_exit(pcie); - - pci_stop_root_bus(host->bus); - pci_remove_root_bus(host->bus); - tegra_pcie_free_resources(pcie); - pm_runtime_put_sync(pcie->dev); - pm_runtime_disable(pcie->dev); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - tegra_pcie_msi_teardown(pcie); - - tegra_pcie_put_resources(pcie); - - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - tegra_pcie_port_free(port); - - return 0; -} - -static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) -{ - struct tegra_pcie *pcie = dev_get_drvdata(dev); - struct tegra_pcie_port *port; - - list_for_each_entry(port, &pcie->ports, list) - tegra_pcie_pme_turnoff(port); - - tegra_pcie_disable_ports(pcie); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - tegra_pcie_disable_msi(pcie); - - tegra_pcie_disable_controller(pcie); - tegra_pcie_power_off(pcie); - - return 0; -} - -static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) -{ - struct tegra_pcie *pcie = dev_get_drvdata(dev); - int err; - - err = tegra_pcie_power_on(pcie); - if (err) { - dev_err(dev, "tegra pcie power on fail: %d\n", err); - return err; - } - err = tegra_pcie_enable_controller(pcie); - if (err) { - dev_err(dev, "tegra pcie controller enable fail: %d\n", err); - goto poweroff; - } - tegra_pcie_setup_translations(pcie); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - tegra_pcie_enable_msi(pcie); - - tegra_pcie_enable_ports(pcie); - - return 0; - -poweroff: - tegra_pcie_power_off(pcie); - - return err; -} - -static const struct dev_pm_ops tegra_pcie_pm_ops = { - SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL) - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend, - tegra_pcie_pm_resume) -}; - -static struct platform_driver tegra_pcie_driver = { - .driver = { - .name = "tegra-pcie", - .of_match_table = tegra_pcie_of_match, - .suppress_bind_attrs = true, - .pm = &tegra_pcie_pm_ops, - }, - .probe = tegra_pcie_probe, - .remove = tegra_pcie_remove, -}; -module_platform_driver(tegra_pcie_driver); -MODULE_LICENSE("GPL"); diff --git a/drivers/pci/host/pci-thunder-ecam.c b/drivers/pci/host/pci-thunder-ecam.c deleted file mode 100644 index 32d1d7b81ef4..000000000000 --- a/drivers/pci/host/pci-thunder-ecam.c +++ /dev/null @@ -1,380 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2015, 2016 Cavium, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_PCI_HOST_THUNDER_ECAM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) - -static void set_val(u32 v, int where, int size, u32 *val) -{ - int shift = (where & 3) * 8; - - pr_debug("set_val %04x: %08x\n", (unsigned)(where & ~3), v); - v >>= shift; - if (size == 1) - v &= 0xff; - else if (size == 2) - v &= 0xffff; - *val = v; -} - -static int handle_ea_bar(u32 e0, int bar, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 *val) -{ - void __iomem *addr; - u32 v; - - /* Entries are 16-byte aligned; bits[2,3] select word in entry */ - int where_a = where & 0xc; - - if (where_a == 0) { - set_val(e0, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0x4) { - addr = bus->ops->map_bus(bus, devfn, bar); /* BAR 0 */ - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - v = readl(addr); - v &= ~0xf; - v |= 2; /* EA entry-1. Base-L */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0x8) { - u32 barl_orig; - u32 barl_rb; - - addr = bus->ops->map_bus(bus, devfn, bar); /* BAR 0 */ - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - barl_orig = readl(addr + 0); - writel(0xffffffff, addr + 0); - barl_rb = readl(addr + 0); - writel(barl_orig, addr + 0); - /* zeros in unsettable bits */ - v = ~barl_rb & ~3; - v |= 0xc; /* EA entry-2. Offset-L */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xc) { - addr = bus->ops->map_bus(bus, devfn, bar + 4); /* BAR 1 */ - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - v = readl(addr); /* EA entry-3. Base-H */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct pci_config_window *cfg = bus->sysdata; - int where_a = where & ~3; - void __iomem *addr; - u32 node_bits; - u32 v; - - /* EA Base[63:32] may be missing some bits ... */ - switch (where_a) { - case 0xa8: - case 0xbc: - case 0xd0: - case 0xe4: - break; - default: - return pci_generic_config_read(bus, devfn, where, size, val); - } - - addr = bus->ops->map_bus(bus, devfn, where_a); - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - v = readl(addr); - - /* - * Bit 44 of the 64-bit Base must match the same bit in - * the config space access window. Since we are working with - * the high-order 32 bits, shift everything down by 32 bits. - */ - node_bits = (cfg->res.start >> 32) & (1 << 12); - - v |= node_bits; - set_val(v, where, size, val); - - return PCIBIOS_SUCCESSFUL; -} - -static int thunder_ecam_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - u32 v; - u32 vendor_device; - u32 class_rev; - void __iomem *addr; - int cfg_type; - int where_a = where & ~3; - - addr = bus->ops->map_bus(bus, devfn, 0xc); - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - v = readl(addr); - - /* Check for non type-00 header */ - cfg_type = (v >> 16) & 0x7f; - - addr = bus->ops->map_bus(bus, devfn, 8); - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - class_rev = readl(addr); - if (class_rev == 0xffffffff) - goto no_emulation; - - if ((class_rev & 0xff) >= 8) { - /* Pass-2 handling */ - if (cfg_type) - goto no_emulation; - return thunder_ecam_p2_config_read(bus, devfn, where, - size, val); - } - - /* - * All BARs have fixed addresses specified by the EA - * capability; they must return zero on read. - */ - if (cfg_type == 0 && - ((where >= 0x10 && where < 0x2c) || - (where >= 0x1a4 && where < 0x1bc))) { - /* BAR or SR-IOV BAR */ - *val = 0; - return PCIBIOS_SUCCESSFUL; - } - - addr = bus->ops->map_bus(bus, devfn, 0); - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - vendor_device = readl(addr); - if (vendor_device == 0xffffffff) - goto no_emulation; - - pr_debug("%04x:%04x - Fix pass#: %08x, where: %03x, devfn: %03x\n", - vendor_device & 0xffff, vendor_device >> 16, class_rev, - (unsigned) where, devfn); - - /* Check for non type-00 header */ - if (cfg_type == 0) { - bool has_msix; - bool is_nic = (vendor_device == 0xa01e177d); - bool is_tns = (vendor_device == 0xa01f177d); - - addr = bus->ops->map_bus(bus, devfn, 0x70); - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - /* E_CAP */ - v = readl(addr); - has_msix = (v & 0xff00) != 0; - - if (!has_msix && where_a == 0x70) { - v |= 0xbc00; /* next capability is EA at 0xbc */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xb0) { - addr = bus->ops->map_bus(bus, devfn, where_a); - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - v = readl(addr); - if (v & 0xff00) - pr_err("Bad MSIX cap header: %08x\n", v); - v |= 0xbc00; /* next capability is EA at 0xbc */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xbc) { - if (is_nic) - v = 0x40014; /* EA last in chain, 4 entries */ - else if (is_tns) - v = 0x30014; /* EA last in chain, 3 entries */ - else if (has_msix) - v = 0x20014; /* EA last in chain, 2 entries */ - else - v = 0x10014; /* EA last in chain, 1 entry */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a >= 0xc0 && where_a < 0xd0) - /* EA entry-0. PP=0, BAR0 Size:3 */ - return handle_ea_bar(0x80ff0003, - 0x10, bus, devfn, where, - size, val); - if (where_a >= 0xd0 && where_a < 0xe0 && has_msix) - /* EA entry-1. PP=0, BAR4 Size:3 */ - return handle_ea_bar(0x80ff0043, - 0x20, bus, devfn, where, - size, val); - if (where_a >= 0xe0 && where_a < 0xf0 && is_tns) - /* EA entry-2. PP=0, BAR2, Size:3 */ - return handle_ea_bar(0x80ff0023, - 0x18, bus, devfn, where, - size, val); - if (where_a >= 0xe0 && where_a < 0xf0 && is_nic) - /* EA entry-2. PP=4, VF_BAR0 (9), Size:3 */ - return handle_ea_bar(0x80ff0493, - 0x1a4, bus, devfn, where, - size, val); - if (where_a >= 0xf0 && where_a < 0x100 && is_nic) - /* EA entry-3. PP=4, VF_BAR4 (d), Size:3 */ - return handle_ea_bar(0x80ff04d3, - 0x1b4, bus, devfn, where, - size, val); - } else if (cfg_type == 1) { - bool is_rsl_bridge = devfn == 0x08; - bool is_rad_bridge = devfn == 0xa0; - bool is_zip_bridge = devfn == 0xa8; - bool is_dfa_bridge = devfn == 0xb0; - bool is_nic_bridge = devfn == 0x10; - - if (where_a == 0x70) { - addr = bus->ops->map_bus(bus, devfn, where_a); - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - v = readl(addr); - if (v & 0xff00) - pr_err("Bad PCIe cap header: %08x\n", v); - v |= 0xbc00; /* next capability is EA at 0xbc */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xbc) { - if (is_nic_bridge) - v = 0x10014; /* EA last in chain, 1 entry */ - else - v = 0x00014; /* EA last in chain, no entries */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xc0) { - if (is_rsl_bridge || is_nic_bridge) - v = 0x0101; /* subordinate:secondary = 1:1 */ - else if (is_rad_bridge) - v = 0x0202; /* subordinate:secondary = 2:2 */ - else if (is_zip_bridge) - v = 0x0303; /* subordinate:secondary = 3:3 */ - else if (is_dfa_bridge) - v = 0x0404; /* subordinate:secondary = 4:4 */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xc4 && is_nic_bridge) { - /* Enabled, not-Write, SP=ff, PP=05, BEI=6, ES=4 */ - v = 0x80ff0564; - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xc8 && is_nic_bridge) { - v = 0x00000002; /* Base-L 64-bit */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xcc && is_nic_bridge) { - v = 0xfffffffe; /* MaxOffset-L 64-bit */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xd0 && is_nic_bridge) { - v = 0x00008430; /* NIC Base-H */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - if (where_a == 0xd4 && is_nic_bridge) { - v = 0x0000000f; /* MaxOffset-H */ - set_val(v, where, size, val); - return PCIBIOS_SUCCESSFUL; - } - } -no_emulation: - return pci_generic_config_read(bus, devfn, where, size, val); -} - -static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - /* - * All BARs have fixed addresses; ignore BAR writes so they - * don't get corrupted. - */ - if ((where >= 0x10 && where < 0x2c) || - (where >= 0x1a4 && where < 0x1bc)) - /* BAR or SR-IOV BAR */ - return PCIBIOS_SUCCESSFUL; - - return pci_generic_config_write(bus, devfn, where, size, val); -} - -struct pci_ecam_ops pci_thunder_ecam_ops = { - .bus_shift = 20, - .pci_ops = { - .map_bus = pci_ecam_map_bus, - .read = thunder_ecam_config_read, - .write = thunder_ecam_config_write, - } -}; - -#ifdef CONFIG_PCI_HOST_THUNDER_ECAM - -static const struct of_device_id thunder_ecam_of_match[] = { - { .compatible = "cavium,pci-host-thunder-ecam" }, - { }, -}; - -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, -}; -builtin_platform_driver(thunder_ecam_driver); - -#endif -#endif diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c deleted file mode 100644 index f127ce8bd4ef..000000000000 --- a/drivers/pci/host/pci-thunder-pem.c +++ /dev/null @@ -1,473 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2015 - 2016 Cavium, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "../pci.h" - -#if defined(CONFIG_PCI_HOST_THUNDER_PEM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) - -#define PEM_CFG_WR 0x28 -#define PEM_CFG_RD 0x30 - -struct thunder_pem_pci { - u32 ea_entry[3]; - void __iomem *pem_reg_base; -}; - -static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - u64 read_val, tmp_val; - struct pci_config_window *cfg = bus->sysdata; - struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv; - - if (devfn != 0 || where >= 2048) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - /* - * 32-bit accesses only. Write the address to the low order - * bits of PEM_CFG_RD, then trigger the read by reading back. - * The config data lands in the upper 32-bits of PEM_CFG_RD. - */ - read_val = where & ~3ull; - writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); - read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); - read_val >>= 32; - - /* - * The config space contains some garbage, fix it up. Also - * synthesize an EA capability for the BAR used by MSI-X. - */ - switch (where & ~3) { - case 0x40: - read_val &= 0xffff00ff; - read_val |= 0x00007000; /* Skip MSI CAP */ - break; - case 0x70: /* Express Cap */ - /* - * Change PME interrupt to vector 2 on T88 where it - * reads as 0, else leave it alone. - */ - if (!(read_val & (0x1f << 25))) - read_val |= (2u << 25); - break; - case 0xb0: /* MSI-X Cap */ - /* TableSize=2 or 4, Next Cap is EA */ - read_val &= 0xc00000ff; - /* - * If Express Cap(0x70) raw PME vector reads as 0 we are on - * T88 and TableSize is reported as 4, else TableSize - * is 2. - */ - writeq(0x70, pem_pci->pem_reg_base + PEM_CFG_RD); - tmp_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); - tmp_val >>= 32; - if (!(tmp_val & (0x1f << 25))) - read_val |= 0x0003bc00; - else - read_val |= 0x0001bc00; - break; - case 0xb4: - /* Table offset=0, BIR=0 */ - read_val = 0x00000000; - break; - case 0xb8: - /* BPA offset=0xf0000, BIR=0 */ - read_val = 0x000f0000; - break; - case 0xbc: - /* EA, 1 entry, no next Cap */ - read_val = 0x00010014; - break; - case 0xc0: - /* DW2 for type-1 */ - read_val = 0x00000000; - break; - case 0xc4: - /* Entry BEI=0, PP=0x00, SP=0xff, ES=3 */ - read_val = 0x80ff0003; - break; - case 0xc8: - read_val = pem_pci->ea_entry[0]; - break; - case 0xcc: - read_val = pem_pci->ea_entry[1]; - break; - case 0xd0: - read_val = pem_pci->ea_entry[2]; - break; - default: - break; - } - read_val >>= (8 * (where & 3)); - switch (size) { - case 1: - read_val &= 0xff; - break; - case 2: - read_val &= 0xffff; - break; - default: - break; - } - *val = read_val; - return PCIBIOS_SUCCESSFUL; -} - -static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct pci_config_window *cfg = bus->sysdata; - - if (bus->number < cfg->busr.start || - bus->number > cfg->busr.end) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* - * The first device on the bus is the PEM PCIe bridge. - * Special case its config access. - */ - if (bus->number == cfg->busr.start) - return thunder_pem_bridge_read(bus, devfn, where, size, val); - - return pci_generic_config_read(bus, devfn, where, size, val); -} - -/* - * Some of the w1c_bits below also include read-only or non-writable - * reserved bits, this makes the code simpler and is OK as the bits - * are not affected by writing zeros to them. - */ -static u32 thunder_pem_bridge_w1c_bits(u64 where_aligned) -{ - u32 w1c_bits = 0; - - switch (where_aligned) { - case 0x04: /* Command/Status */ - case 0x1c: /* Base and I/O Limit/Secondary Status */ - w1c_bits = 0xff000000; - break; - case 0x44: /* Power Management Control and Status */ - w1c_bits = 0xfffffe00; - break; - case 0x78: /* Device Control/Device Status */ - case 0x80: /* Link Control/Link Status */ - case 0x88: /* Slot Control/Slot Status */ - case 0x90: /* Root Status */ - case 0xa0: /* Link Control 2 Registers/Link Status 2 */ - w1c_bits = 0xffff0000; - break; - case 0x104: /* Uncorrectable Error Status */ - case 0x110: /* Correctable Error Status */ - case 0x130: /* Error Status */ - case 0x160: /* Link Control 4 */ - w1c_bits = 0xffffffff; - break; - default: - break; - } - return w1c_bits; -} - -/* Some bits must be written to one so they appear to be read-only. */ -static u32 thunder_pem_bridge_w1_bits(u64 where_aligned) -{ - u32 w1_bits; - - switch (where_aligned) { - case 0x1c: /* I/O Base / I/O Limit, Secondary Status */ - /* Force 32-bit I/O addressing. */ - w1_bits = 0x0101; - break; - case 0x24: /* Prefetchable Memory Base / Prefetchable Memory Limit */ - /* Force 64-bit addressing */ - w1_bits = 0x00010001; - break; - default: - w1_bits = 0; - break; - } - return w1_bits; -} - -static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct pci_config_window *cfg = bus->sysdata; - struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv; - u64 write_val, read_val; - u64 where_aligned = where & ~3ull; - u32 mask = 0; - - - if (devfn != 0 || where >= 2048) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* - * 32-bit accesses only. If the write is for a size smaller - * than 32-bits, we must first read the 32-bit value and merge - * in the desired bits and then write the whole 32-bits back - * out. - */ - switch (size) { - case 1: - writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD); - read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); - read_val >>= 32; - mask = ~(0xff << (8 * (where & 3))); - read_val &= mask; - val = (val & 0xff) << (8 * (where & 3)); - val |= (u32)read_val; - break; - case 2: - writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD); - read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); - read_val >>= 32; - mask = ~(0xffff << (8 * (where & 3))); - read_val &= mask; - val = (val & 0xffff) << (8 * (where & 3)); - val |= (u32)read_val; - break; - default: - break; - } - - /* - * By expanding the write width to 32 bits, we may - * inadvertently hit some W1C bits that were not intended to - * be written. Calculate the mask that must be applied to the - * data to be written to avoid these cases. - */ - if (mask) { - u32 w1c_bits = thunder_pem_bridge_w1c_bits(where); - - if (w1c_bits) { - mask &= w1c_bits; - val &= ~mask; - } - } - - /* - * Some bits must be read-only with value of one. Since the - * access method allows these to be cleared if a zero is - * written, force them to one before writing. - */ - val |= thunder_pem_bridge_w1_bits(where_aligned); - - /* - * Low order bits are the config address, the high order 32 - * bits are the data to be written. - */ - write_val = (((u64)val) << 32) | where_aligned; - writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR); - return PCIBIOS_SUCCESSFUL; -} - -static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct pci_config_window *cfg = bus->sysdata; - - if (bus->number < cfg->busr.start || - bus->number > cfg->busr.end) - return PCIBIOS_DEVICE_NOT_FOUND; - /* - * The first device on the bus is the PEM PCIe bridge. - * Special case its config access. - */ - if (bus->number == cfg->busr.start) - return thunder_pem_bridge_write(bus, devfn, where, size, val); - - - return pci_generic_config_write(bus, devfn, where, size, val); -} - -static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg, - struct resource *res_pem) -{ - struct thunder_pem_pci *pem_pci; - resource_size_t bar4_start; - - pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL); - if (!pem_pci) - return -ENOMEM; - - pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000); - if (!pem_pci->pem_reg_base) - return -ENOMEM; - - /* - * The MSI-X BAR for the PEM and AER interrupts is located at - * a fixed offset from the PEM register base. Generate a - * fragment of the synthesized Enhanced Allocation capability - * structure here for the BAR. - */ - bar4_start = res_pem->start + 0xf00000; - pem_pci->ea_entry[0] = (u32)bar4_start | 2; - pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u; - pem_pci->ea_entry[2] = (u32)(bar4_start >> 32); - - cfg->priv = pem_pci; - return 0; -} - -#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) - -#define PEM_RES_BASE 0x87e0c0000000UL -#define PEM_NODE_MASK GENMASK(45, 44) -#define PEM_INDX_MASK GENMASK(26, 24) -#define PEM_MIN_DOM_IN_NODE 4 -#define PEM_MAX_DOM_IN_NODE 10 - -static void thunder_pem_reserve_range(struct device *dev, int seg, - struct resource *r) -{ - resource_size_t start = r->start, end = r->end; - struct resource *res; - const char *regionid; - - regionid = kasprintf(GFP_KERNEL, "PEM RC:%d", seg); - if (!regionid) - return; - - res = request_mem_region(start, end - start + 1, regionid); - if (res) - res->flags &= ~IORESOURCE_BUSY; - else - kfree(regionid); - - dev_info(dev, "%pR %s reserved\n", r, - res ? "has been" : "could not be"); -} - -static void thunder_pem_legacy_fw(struct acpi_pci_root *root, - struct resource *res_pem) -{ - int node = acpi_get_node(root->device->handle); - int index; - - if (node == NUMA_NO_NODE) - node = 0; - - index = root->segment - PEM_MIN_DOM_IN_NODE; - index -= node * PEM_MAX_DOM_IN_NODE; - res_pem->start = PEM_RES_BASE | FIELD_PREP(PEM_NODE_MASK, node) | - FIELD_PREP(PEM_INDX_MASK, index); - res_pem->flags = IORESOURCE_MEM; -} - -static int thunder_pem_acpi_init(struct pci_config_window *cfg) -{ - struct device *dev = cfg->parent; - struct acpi_device *adev = to_acpi_device(dev); - struct acpi_pci_root *root = acpi_driver_data(adev); - struct resource *res_pem; - int ret; - - res_pem = devm_kzalloc(&adev->dev, sizeof(*res_pem), GFP_KERNEL); - if (!res_pem) - return -ENOMEM; - - ret = acpi_get_rc_resources(dev, "CAVA02B", root->segment, res_pem); - - /* - * If we fail to gather resources it means that we run with old - * FW where we need to calculate PEM-specific resources manually. - */ - if (ret) { - thunder_pem_legacy_fw(root, res_pem); - /* - * Reserve 64K size PEM specific resources. The full 16M range - * size is required for thunder_pem_init() call. - */ - res_pem->end = res_pem->start + SZ_64K - 1; - thunder_pem_reserve_range(dev, root->segment, res_pem); - res_pem->end = res_pem->start + SZ_16M - 1; - - /* Reserve PCI configuration space as well. */ - thunder_pem_reserve_range(dev, root->segment, &cfg->res); - } - - return thunder_pem_init(dev, cfg, res_pem); -} - -struct pci_ecam_ops thunder_pem_ecam_ops = { - .bus_shift = 24, - .init = thunder_pem_acpi_init, - .pci_ops = { - .map_bus = pci_ecam_map_bus, - .read = thunder_pem_config_read, - .write = thunder_pem_config_write, - } -}; - -#endif - -#ifdef CONFIG_PCI_HOST_THUNDER_PEM - -static int thunder_pem_platform_init(struct pci_config_window *cfg) -{ - struct device *dev = cfg->parent; - struct platform_device *pdev = to_platform_device(dev); - struct resource *res_pem; - - if (!dev->of_node) - return -EINVAL; - - /* - * The second register range is the PEM bridge to the PCIe - * bus. It has a different config access method than those - * devices behind the bridge. - */ - res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res_pem) { - dev_err(dev, "missing \"reg[1]\"property\n"); - return -EINVAL; - } - - return thunder_pem_init(dev, cfg, res_pem); -} - -static struct pci_ecam_ops pci_thunder_pem_ops = { - .bus_shift = 24, - .init = thunder_pem_platform_init, - .pci_ops = { - .map_bus = pci_ecam_map_bus, - .read = thunder_pem_config_read, - .write = thunder_pem_config_write, - } -}; - -static const struct of_device_id thunder_pem_of_match[] = { - { .compatible = "cavium,pci-host-thunder-pem" }, - { }, -}; - -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, -}; -builtin_platform_driver(thunder_pem_driver); - -#endif -#endif diff --git a/drivers/pci/host/pci-v3-semi.c b/drivers/pci/host/pci-v3-semi.c deleted file mode 100644 index 68b8bfbdb867..000000000000 --- a/drivers/pci/host/pci-v3-semi.c +++ /dev/null @@ -1,963 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Support for V3 Semiconductor PCI Local Bus to PCI Bridge - * Copyright (C) 2017 Linus Walleij - * - * Based on the code from arch/arm/mach-integrator/pci_v3.c - * Copyright (C) 1999 ARM Limited - * Copyright (C) 2000-2001 Deep Blue Solutions Ltd - * - * Contributors to the old driver include: - * Russell King - * David A. Rusling (uHAL, ARM Firmware suite) - * Rob Herring - * Liviu Dudau - * Grant Likely - * Arnd Bergmann - * Bjorn Helgaas - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -#define V3_PCI_VENDOR 0x00000000 -#define V3_PCI_DEVICE 0x00000002 -#define V3_PCI_CMD 0x00000004 -#define V3_PCI_STAT 0x00000006 -#define V3_PCI_CC_REV 0x00000008 -#define V3_PCI_HDR_CFG 0x0000000C -#define V3_PCI_IO_BASE 0x00000010 -#define V3_PCI_BASE0 0x00000014 -#define V3_PCI_BASE1 0x00000018 -#define V3_PCI_SUB_VENDOR 0x0000002C -#define V3_PCI_SUB_ID 0x0000002E -#define V3_PCI_ROM 0x00000030 -#define V3_PCI_BPARAM 0x0000003C -#define V3_PCI_MAP0 0x00000040 -#define V3_PCI_MAP1 0x00000044 -#define V3_PCI_INT_STAT 0x00000048 -#define V3_PCI_INT_CFG 0x0000004C -#define V3_LB_BASE0 0x00000054 -#define V3_LB_BASE1 0x00000058 -#define V3_LB_MAP0 0x0000005E -#define V3_LB_MAP1 0x00000062 -#define V3_LB_BASE2 0x00000064 -#define V3_LB_MAP2 0x00000066 -#define V3_LB_SIZE 0x00000068 -#define V3_LB_IO_BASE 0x0000006E -#define V3_FIFO_CFG 0x00000070 -#define V3_FIFO_PRIORITY 0x00000072 -#define V3_FIFO_STAT 0x00000074 -#define V3_LB_ISTAT 0x00000076 -#define V3_LB_IMASK 0x00000077 -#define V3_SYSTEM 0x00000078 -#define V3_LB_CFG 0x0000007A -#define V3_PCI_CFG 0x0000007C -#define V3_DMA_PCI_ADR0 0x00000080 -#define V3_DMA_PCI_ADR1 0x00000090 -#define V3_DMA_LOCAL_ADR0 0x00000084 -#define V3_DMA_LOCAL_ADR1 0x00000094 -#define V3_DMA_LENGTH0 0x00000088 -#define V3_DMA_LENGTH1 0x00000098 -#define V3_DMA_CSR0 0x0000008B -#define V3_DMA_CSR1 0x0000009B -#define V3_DMA_CTLB_ADR0 0x0000008C -#define V3_DMA_CTLB_ADR1 0x0000009C -#define V3_DMA_DELAY 0x000000E0 -#define V3_MAIL_DATA 0x000000C0 -#define V3_PCI_MAIL_IEWR 0x000000D0 -#define V3_PCI_MAIL_IERD 0x000000D2 -#define V3_LB_MAIL_IEWR 0x000000D4 -#define V3_LB_MAIL_IERD 0x000000D6 -#define V3_MAIL_WR_STAT 0x000000D8 -#define V3_MAIL_RD_STAT 0x000000DA -#define V3_QBA_MAP 0x000000DC - -/* PCI STATUS bits */ -#define V3_PCI_STAT_PAR_ERR BIT(15) -#define V3_PCI_STAT_SYS_ERR BIT(14) -#define V3_PCI_STAT_M_ABORT_ERR BIT(13) -#define V3_PCI_STAT_T_ABORT_ERR BIT(12) - -/* LB ISTAT bits */ -#define V3_LB_ISTAT_MAILBOX BIT(7) -#define V3_LB_ISTAT_PCI_RD BIT(6) -#define V3_LB_ISTAT_PCI_WR BIT(5) -#define V3_LB_ISTAT_PCI_INT BIT(4) -#define V3_LB_ISTAT_PCI_PERR BIT(3) -#define V3_LB_ISTAT_I2O_QWR BIT(2) -#define V3_LB_ISTAT_DMA1 BIT(1) -#define V3_LB_ISTAT_DMA0 BIT(0) - -/* PCI COMMAND bits */ -#define V3_COMMAND_M_FBB_EN BIT(9) -#define V3_COMMAND_M_SERR_EN BIT(8) -#define V3_COMMAND_M_PAR_EN BIT(6) -#define V3_COMMAND_M_MASTER_EN BIT(2) -#define V3_COMMAND_M_MEM_EN BIT(1) -#define V3_COMMAND_M_IO_EN BIT(0) - -/* SYSTEM bits */ -#define V3_SYSTEM_M_RST_OUT BIT(15) -#define V3_SYSTEM_M_LOCK BIT(14) -#define V3_SYSTEM_UNLOCK 0xa05f - -/* PCI CFG bits */ -#define V3_PCI_CFG_M_I2O_EN BIT(15) -#define V3_PCI_CFG_M_IO_REG_DIS BIT(14) -#define V3_PCI_CFG_M_IO_DIS BIT(13) -#define V3_PCI_CFG_M_EN3V BIT(12) -#define V3_PCI_CFG_M_RETRY_EN BIT(10) -#define V3_PCI_CFG_M_AD_LOW1 BIT(9) -#define V3_PCI_CFG_M_AD_LOW0 BIT(8) -/* - * This is the value applied to C/BE[3:1], with bit 0 always held 0 - * during DMA access. - */ -#define V3_PCI_CFG_M_RTYPE_SHIFT 5 -#define V3_PCI_CFG_M_WTYPE_SHIFT 1 -#define V3_PCI_CFG_TYPE_DEFAULT 0x3 - -/* PCI BASE bits (PCI -> Local Bus) */ -#define V3_PCI_BASE_M_ADR_BASE 0xFFF00000U -#define V3_PCI_BASE_M_ADR_BASEL 0x000FFF00U -#define V3_PCI_BASE_M_PREFETCH BIT(3) -#define V3_PCI_BASE_M_TYPE (3 << 1) -#define V3_PCI_BASE_M_IO BIT(0) - -/* PCI MAP bits (PCI -> Local bus) */ -#define V3_PCI_MAP_M_MAP_ADR 0xFFF00000U -#define V3_PCI_MAP_M_RD_POST_INH BIT(15) -#define V3_PCI_MAP_M_ROM_SIZE (3 << 10) -#define V3_PCI_MAP_M_SWAP (3 << 8) -#define V3_PCI_MAP_M_ADR_SIZE 0x000000F0U -#define V3_PCI_MAP_M_REG_EN BIT(1) -#define V3_PCI_MAP_M_ENABLE BIT(0) - -/* LB_BASE0,1 bits (Local bus -> PCI) */ -#define V3_LB_BASE_ADR_BASE 0xfff00000U -#define V3_LB_BASE_SWAP (3 << 8) -#define V3_LB_BASE_ADR_SIZE (15 << 4) -#define V3_LB_BASE_PREFETCH BIT(3) -#define V3_LB_BASE_ENABLE BIT(0) - -#define V3_LB_BASE_ADR_SIZE_1MB (0 << 4) -#define V3_LB_BASE_ADR_SIZE_2MB (1 << 4) -#define V3_LB_BASE_ADR_SIZE_4MB (2 << 4) -#define V3_LB_BASE_ADR_SIZE_8MB (3 << 4) -#define V3_LB_BASE_ADR_SIZE_16MB (4 << 4) -#define V3_LB_BASE_ADR_SIZE_32MB (5 << 4) -#define V3_LB_BASE_ADR_SIZE_64MB (6 << 4) -#define V3_LB_BASE_ADR_SIZE_128MB (7 << 4) -#define V3_LB_BASE_ADR_SIZE_256MB (8 << 4) -#define V3_LB_BASE_ADR_SIZE_512MB (9 << 4) -#define V3_LB_BASE_ADR_SIZE_1GB (10 << 4) -#define V3_LB_BASE_ADR_SIZE_2GB (11 << 4) - -#define v3_addr_to_lb_base(a) ((a) & V3_LB_BASE_ADR_BASE) - -/* LB_MAP0,1 bits (Local bus -> PCI) */ -#define V3_LB_MAP_MAP_ADR 0xfff0U -#define V3_LB_MAP_TYPE (7 << 1) -#define V3_LB_MAP_AD_LOW_EN BIT(0) - -#define V3_LB_MAP_TYPE_IACK (0 << 1) -#define V3_LB_MAP_TYPE_IO (1 << 1) -#define V3_LB_MAP_TYPE_MEM (3 << 1) -#define V3_LB_MAP_TYPE_CONFIG (5 << 1) -#define V3_LB_MAP_TYPE_MEM_MULTIPLE (6 << 1) - -#define v3_addr_to_lb_map(a) (((a) >> 16) & V3_LB_MAP_MAP_ADR) - -/* LB_BASE2 bits (Local bus -> PCI IO) */ -#define V3_LB_BASE2_ADR_BASE 0xff00U -#define V3_LB_BASE2_SWAP_AUTO (3 << 6) -#define V3_LB_BASE2_ENABLE BIT(0) - -#define v3_addr_to_lb_base2(a) (((a) >> 16) & V3_LB_BASE2_ADR_BASE) - -/* LB_MAP2 bits (Local bus -> PCI IO) */ -#define V3_LB_MAP2_MAP_ADR 0xff00U - -#define v3_addr_to_lb_map2(a) (((a) >> 16) & V3_LB_MAP2_MAP_ADR) - -/* FIFO priority bits */ -#define V3_FIFO_PRIO_LOCAL BIT(12) -#define V3_FIFO_PRIO_LB_RD1_FLUSH_EOB BIT(10) -#define V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 BIT(11) -#define V3_FIFO_PRIO_LB_RD1_FLUSH_ANY (BIT(10)|BIT(11)) -#define V3_FIFO_PRIO_LB_RD0_FLUSH_EOB BIT(8) -#define V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 BIT(9) -#define V3_FIFO_PRIO_LB_RD0_FLUSH_ANY (BIT(8)|BIT(9)) -#define V3_FIFO_PRIO_PCI BIT(4) -#define V3_FIFO_PRIO_PCI_RD1_FLUSH_EOB BIT(2) -#define V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 BIT(3) -#define V3_FIFO_PRIO_PCI_RD1_FLUSH_ANY (BIT(2)|BIT(3)) -#define V3_FIFO_PRIO_PCI_RD0_FLUSH_EOB BIT(0) -#define V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1 BIT(1) -#define V3_FIFO_PRIO_PCI_RD0_FLUSH_ANY (BIT(0)|BIT(1)) - -/* Local bus configuration bits */ -#define V3_LB_CFG_LB_TO_64_CYCLES 0x0000 -#define V3_LB_CFG_LB_TO_256_CYCLES BIT(13) -#define V3_LB_CFG_LB_TO_512_CYCLES BIT(14) -#define V3_LB_CFG_LB_TO_1024_CYCLES (BIT(13)|BIT(14)) -#define V3_LB_CFG_LB_RST BIT(12) -#define V3_LB_CFG_LB_PPC_RDY BIT(11) -#define V3_LB_CFG_LB_LB_INT BIT(10) -#define V3_LB_CFG_LB_ERR_EN BIT(9) -#define V3_LB_CFG_LB_RDY_EN BIT(8) -#define V3_LB_CFG_LB_BE_IMODE BIT(7) -#define V3_LB_CFG_LB_BE_OMODE BIT(6) -#define V3_LB_CFG_LB_ENDIAN BIT(5) -#define V3_LB_CFG_LB_PARK_EN BIT(4) -#define V3_LB_CFG_LB_FBB_DIS BIT(2) - -/* ARM Integrator-specific extended control registers */ -#define INTEGRATOR_SC_PCI_OFFSET 0x18 -#define INTEGRATOR_SC_PCI_ENABLE BIT(0) -#define INTEGRATOR_SC_PCI_INTCLR BIT(1) -#define INTEGRATOR_SC_LBFADDR_OFFSET 0x20 -#define INTEGRATOR_SC_LBFCODE_OFFSET 0x24 - -struct v3_pci { - struct device *dev; - void __iomem *base; - void __iomem *config_base; - struct pci_bus *bus; - u32 config_mem; - u32 io_mem; - u32 non_pre_mem; - u32 pre_mem; - phys_addr_t io_bus_addr; - phys_addr_t non_pre_bus_addr; - phys_addr_t pre_bus_addr; - struct regmap *map; -}; - -/* - * The V3 PCI interface chip in Integrator provides several windows from - * local bus memory into the PCI memory areas. Unfortunately, there - * are not really enough windows for our usage, therefore we reuse - * one of the windows for access to PCI configuration space. On the - * Integrator/AP, the memory map is as follows: - * - * Local Bus Memory Usage - * - * 40000000 - 4FFFFFFF PCI memory. 256M non-prefetchable - * 50000000 - 5FFFFFFF PCI memory. 256M prefetchable - * 60000000 - 60FFFFFF PCI IO. 16M - * 61000000 - 61FFFFFF PCI Configuration. 16M - * - * There are three V3 windows, each described by a pair of V3 registers. - * These are LB_BASE0/LB_MAP0, LB_BASE1/LB_MAP1 and LB_BASE2/LB_MAP2. - * Base0 and Base1 can be used for any type of PCI memory access. Base2 - * can be used either for PCI I/O or for I20 accesses. By default, uHAL - * uses this only for PCI IO space. - * - * Normally these spaces are mapped using the following base registers: - * - * Usage Local Bus Memory Base/Map registers used - * - * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 - * Mem 50000000 - 5FFFFFFF LB_BASE1/LB_MAP1 - * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 - * Cfg 61000000 - 61FFFFFF - * - * This means that I20 and PCI configuration space accesses will fail. - * When PCI configuration accesses are needed (via the uHAL PCI - * configuration space primitives) we must remap the spaces as follows: - * - * Usage Local Bus Memory Base/Map registers used - * - * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 - * Mem 50000000 - 5FFFFFFF LB_BASE0/LB_MAP0 - * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 - * Cfg 61000000 - 61FFFFFF LB_BASE1/LB_MAP1 - * - * To make this work, the code depends on overlapping windows working. - * The V3 chip translates an address by checking its range within - * each of the BASE/MAP pairs in turn (in ascending register number - * order). It will use the first matching pair. So, for example, - * if the same address is mapped by both LB_BASE0/LB_MAP0 and - * LB_BASE1/LB_MAP1, the V3 will use the translation from - * LB_BASE0/LB_MAP0. - * - * To allow PCI Configuration space access, the code enlarges the - * window mapped by LB_BASE0/LB_MAP0 from 256M to 512M. This occludes - * the windows currently mapped by LB_BASE1/LB_MAP1 so that it can - * be remapped for use by configuration cycles. - * - * At the end of the PCI Configuration space accesses, - * LB_BASE1/LB_MAP1 is reset to map PCI Memory. Finally the window - * mapped by LB_BASE0/LB_MAP0 is reduced in size from 512M to 256M to - * reveal the now restored LB_BASE1/LB_MAP1 window. - * - * NOTE: We do not set up I2O mapping. I suspect that this is only - * for an intelligent (target) device. Using I2O disables most of - * the mappings into PCI memory. - */ -static void __iomem *v3_map_bus(struct pci_bus *bus, - unsigned int devfn, int offset) -{ - struct v3_pci *v3 = bus->sysdata; - unsigned int address, mapaddress, busnr; - - busnr = bus->number; - if (busnr == 0) { - int slot = PCI_SLOT(devfn); - - /* - * local bus segment so need a type 0 config cycle - * - * build the PCI configuration "address" with one-hot in - * A31-A11 - * - * mapaddress: - * 3:1 = config cycle (101) - * 0 = PCI A1 & A0 are 0 (0) - */ - address = PCI_FUNC(devfn) << 8; - mapaddress = V3_LB_MAP_TYPE_CONFIG; - - if (slot > 12) - /* - * high order bits are handled by the MAP register - */ - mapaddress |= BIT(slot - 5); - else - /* - * low order bits handled directly in the address - */ - address |= BIT(slot + 11); - } else { - /* - * not the local bus segment so need a type 1 config cycle - * - * address: - * 23:16 = bus number - * 15:11 = slot number (7:3 of devfn) - * 10:8 = func number (2:0 of devfn) - * - * mapaddress: - * 3:1 = config cycle (101) - * 0 = PCI A1 & A0 from host bus (1) - */ - mapaddress = V3_LB_MAP_TYPE_CONFIG | V3_LB_MAP_AD_LOW_EN; - address = (busnr << 16) | (devfn << 8); - } - - /* - * Set up base0 to see all 512Mbytes of memory space (not - * prefetchable), this frees up base1 for re-use by - * configuration memory - */ - writel(v3_addr_to_lb_base(v3->non_pre_mem) | - V3_LB_BASE_ADR_SIZE_512MB | V3_LB_BASE_ENABLE, - v3->base + V3_LB_BASE0); - - /* - * Set up base1/map1 to point into configuration space. - * The config mem is always 16MB. - */ - writel(v3_addr_to_lb_base(v3->config_mem) | - V3_LB_BASE_ADR_SIZE_16MB | V3_LB_BASE_ENABLE, - v3->base + V3_LB_BASE1); - writew(mapaddress, v3->base + V3_LB_MAP1); - - return v3->config_base + address + offset; -} - -static void v3_unmap_bus(struct v3_pci *v3) -{ - /* - * Reassign base1 for use by prefetchable PCI memory - */ - writel(v3_addr_to_lb_base(v3->pre_mem) | - V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_PREFETCH | - V3_LB_BASE_ENABLE, - v3->base + V3_LB_BASE1); - writew(v3_addr_to_lb_map(v3->pre_bus_addr) | - V3_LB_MAP_TYPE_MEM, /* was V3_LB_MAP_TYPE_MEM_MULTIPLE */ - v3->base + V3_LB_MAP1); - - /* - * And shrink base0 back to a 256M window (NOTE: MAP0 already correct) - */ - writel(v3_addr_to_lb_base(v3->non_pre_mem) | - V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_ENABLE, - v3->base + V3_LB_BASE0); -} - -static int v3_pci_read_config(struct pci_bus *bus, unsigned int fn, - int config, int size, u32 *value) -{ - struct v3_pci *v3 = bus->sysdata; - int ret; - - dev_dbg(&bus->dev, - "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", - PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); - ret = pci_generic_config_read(bus, fn, config, size, value); - v3_unmap_bus(v3); - return ret; -} - -static int v3_pci_write_config(struct pci_bus *bus, unsigned int fn, - int config, int size, u32 value) -{ - struct v3_pci *v3 = bus->sysdata; - int ret; - - dev_dbg(&bus->dev, - "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", - PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); - ret = pci_generic_config_write(bus, fn, config, size, value); - v3_unmap_bus(v3); - return ret; -} - -static struct pci_ops v3_pci_ops = { - .map_bus = v3_map_bus, - .read = v3_pci_read_config, - .write = v3_pci_write_config, -}; - -static irqreturn_t v3_irq(int irq, void *data) -{ - struct v3_pci *v3 = data; - struct device *dev = v3->dev; - u32 status; - - status = readw(v3->base + V3_PCI_STAT); - if (status & V3_PCI_STAT_PAR_ERR) - dev_err(dev, "parity error interrupt\n"); - if (status & V3_PCI_STAT_SYS_ERR) - dev_err(dev, "system error interrupt\n"); - if (status & V3_PCI_STAT_M_ABORT_ERR) - dev_err(dev, "master abort error interrupt\n"); - if (status & V3_PCI_STAT_T_ABORT_ERR) - dev_err(dev, "target abort error interrupt\n"); - writew(status, v3->base + V3_PCI_STAT); - - status = readb(v3->base + V3_LB_ISTAT); - if (status & V3_LB_ISTAT_MAILBOX) - dev_info(dev, "PCI mailbox interrupt\n"); - if (status & V3_LB_ISTAT_PCI_RD) - dev_err(dev, "PCI target LB->PCI READ abort interrupt\n"); - if (status & V3_LB_ISTAT_PCI_WR) - dev_err(dev, "PCI target LB->PCI WRITE abort interrupt\n"); - if (status & V3_LB_ISTAT_PCI_INT) - dev_info(dev, "PCI pin interrupt\n"); - if (status & V3_LB_ISTAT_PCI_PERR) - dev_err(dev, "PCI parity error interrupt\n"); - if (status & V3_LB_ISTAT_I2O_QWR) - dev_info(dev, "I2O inbound post queue interrupt\n"); - if (status & V3_LB_ISTAT_DMA1) - dev_info(dev, "DMA channel 1 interrupt\n"); - if (status & V3_LB_ISTAT_DMA0) - dev_info(dev, "DMA channel 0 interrupt\n"); - /* Clear all possible interrupts on the local bus */ - writeb(0, v3->base + V3_LB_ISTAT); - if (v3->map) - regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET, - INTEGRATOR_SC_PCI_ENABLE | - INTEGRATOR_SC_PCI_INTCLR); - - return IRQ_HANDLED; -} - -static int v3_integrator_init(struct v3_pci *v3) -{ - unsigned int val; - - v3->map = - syscon_regmap_lookup_by_compatible("arm,integrator-ap-syscon"); - if (IS_ERR(v3->map)) { - dev_err(v3->dev, "no syscon\n"); - return -ENODEV; - } - - regmap_read(v3->map, INTEGRATOR_SC_PCI_OFFSET, &val); - /* Take the PCI bridge out of reset, clear IRQs */ - regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET, - INTEGRATOR_SC_PCI_ENABLE | - INTEGRATOR_SC_PCI_INTCLR); - - if (!(val & INTEGRATOR_SC_PCI_ENABLE)) { - /* If we were in reset we need to sleep a bit */ - msleep(230); - - /* Set the physical base for the controller itself */ - writel(0x6200, v3->base + V3_LB_IO_BASE); - - /* Wait for the mailbox to settle after reset */ - do { - writeb(0xaa, v3->base + V3_MAIL_DATA); - writeb(0x55, v3->base + V3_MAIL_DATA + 4); - } while (readb(v3->base + V3_MAIL_DATA) != 0xaa && - readb(v3->base + V3_MAIL_DATA) != 0x55); - } - - dev_info(v3->dev, "initialized PCI V3 Integrator/AP integration\n"); - - return 0; -} - -static int v3_pci_setup_resource(struct v3_pci *v3, - resource_size_t io_base, - struct pci_host_bridge *host, - struct resource_entry *win) -{ - struct device *dev = v3->dev; - struct resource *mem; - struct resource *io; - int ret; - - switch (resource_type(win->res)) { - case IORESOURCE_IO: - io = win->res; - io->name = "V3 PCI I/O"; - v3->io_mem = io_base; - v3->io_bus_addr = io->start - win->offset; - dev_dbg(dev, "I/O window %pR, bus addr %pap\n", - io, &v3->io_bus_addr); - ret = pci_remap_iospace(io, io_base); - if (ret) { - dev_warn(dev, - "error %d: failed to map resource %pR\n", - ret, io); - return ret; - } - /* Setup window 2 - PCI I/O */ - writel(v3_addr_to_lb_base2(v3->io_mem) | - V3_LB_BASE2_ENABLE, - v3->base + V3_LB_BASE2); - writew(v3_addr_to_lb_map2(v3->io_bus_addr), - v3->base + V3_LB_MAP2); - break; - case IORESOURCE_MEM: - mem = win->res; - if (mem->flags & IORESOURCE_PREFETCH) { - mem->name = "V3 PCI PRE-MEM"; - v3->pre_mem = mem->start; - v3->pre_bus_addr = mem->start - win->offset; - dev_dbg(dev, "PREFETCHABLE MEM window %pR, bus addr %pap\n", - mem, &v3->pre_bus_addr); - if (resource_size(mem) != SZ_256M) { - dev_err(dev, "prefetchable memory range is not 256MB\n"); - return -EINVAL; - } - if (v3->non_pre_mem && - (mem->start != v3->non_pre_mem + SZ_256M)) { - dev_err(dev, - "prefetchable memory is not adjacent to non-prefetchable memory\n"); - return -EINVAL; - } - /* Setup window 1 - PCI prefetchable memory */ - writel(v3_addr_to_lb_base(v3->pre_mem) | - V3_LB_BASE_ADR_SIZE_256MB | - V3_LB_BASE_PREFETCH | - V3_LB_BASE_ENABLE, - v3->base + V3_LB_BASE1); - writew(v3_addr_to_lb_map(v3->pre_bus_addr) | - V3_LB_MAP_TYPE_MEM, /* Was V3_LB_MAP_TYPE_MEM_MULTIPLE */ - v3->base + V3_LB_MAP1); - } else { - mem->name = "V3 PCI NON-PRE-MEM"; - v3->non_pre_mem = mem->start; - v3->non_pre_bus_addr = mem->start - win->offset; - dev_dbg(dev, "NON-PREFETCHABLE MEM window %pR, bus addr %pap\n", - mem, &v3->non_pre_bus_addr); - if (resource_size(mem) != SZ_256M) { - dev_err(dev, - "non-prefetchable memory range is not 256MB\n"); - return -EINVAL; - } - /* Setup window 0 - PCI non-prefetchable memory */ - writel(v3_addr_to_lb_base(v3->non_pre_mem) | - V3_LB_BASE_ADR_SIZE_256MB | - V3_LB_BASE_ENABLE, - v3->base + V3_LB_BASE0); - writew(v3_addr_to_lb_map(v3->non_pre_bus_addr) | - V3_LB_MAP_TYPE_MEM, - v3->base + V3_LB_MAP0); - } - break; - case IORESOURCE_BUS: - dev_dbg(dev, "BUS %pR\n", win->res); - host->busnr = win->res->start; - break; - default: - dev_info(dev, "Unknown resource type %lu\n", - resource_type(win->res)); - break; - } - - return 0; -} - -static int v3_get_dma_range_config(struct v3_pci *v3, - struct of_pci_range *range, - u32 *pci_base, u32 *pci_map) -{ - struct device *dev = v3->dev; - u64 cpu_end = range->cpu_addr + range->size - 1; - u64 pci_end = range->pci_addr + range->size - 1; - u32 val; - - if (range->pci_addr & ~V3_PCI_BASE_M_ADR_BASE) { - dev_err(dev, "illegal range, only PCI bits 31..20 allowed\n"); - return -EINVAL; - } - val = ((u32)range->pci_addr) & V3_PCI_BASE_M_ADR_BASE; - *pci_base = val; - - if (range->cpu_addr & ~V3_PCI_MAP_M_MAP_ADR) { - dev_err(dev, "illegal range, only CPU bits 31..20 allowed\n"); - return -EINVAL; - } - val = ((u32)range->cpu_addr) & V3_PCI_MAP_M_MAP_ADR; - - switch (range->size) { - case SZ_1M: - val |= V3_LB_BASE_ADR_SIZE_1MB; - break; - case SZ_2M: - val |= V3_LB_BASE_ADR_SIZE_2MB; - break; - case SZ_4M: - val |= V3_LB_BASE_ADR_SIZE_4MB; - break; - case SZ_8M: - val |= V3_LB_BASE_ADR_SIZE_8MB; - break; - case SZ_16M: - val |= V3_LB_BASE_ADR_SIZE_16MB; - break; - case SZ_32M: - val |= V3_LB_BASE_ADR_SIZE_32MB; - break; - case SZ_64M: - val |= V3_LB_BASE_ADR_SIZE_64MB; - break; - case SZ_128M: - val |= V3_LB_BASE_ADR_SIZE_128MB; - break; - case SZ_256M: - val |= V3_LB_BASE_ADR_SIZE_256MB; - break; - case SZ_512M: - val |= V3_LB_BASE_ADR_SIZE_512MB; - break; - case SZ_1G: - val |= V3_LB_BASE_ADR_SIZE_1GB; - break; - case SZ_2G: - val |= V3_LB_BASE_ADR_SIZE_2GB; - break; - default: - dev_err(v3->dev, "illegal dma memory chunk size\n"); - return -EINVAL; - break; - } - val |= V3_PCI_MAP_M_REG_EN | V3_PCI_MAP_M_ENABLE; - *pci_map = val; - - dev_dbg(dev, - "DMA MEM CPU: 0x%016llx -> 0x%016llx => " - "PCI: 0x%016llx -> 0x%016llx base %08x map %08x\n", - range->cpu_addr, cpu_end, - range->pci_addr, pci_end, - *pci_base, *pci_map); - - return 0; -} - -static int v3_pci_parse_map_dma_ranges(struct v3_pci *v3, - struct device_node *np) -{ - struct of_pci_range range; - struct of_pci_range_parser parser; - struct device *dev = v3->dev; - int i = 0; - - if (of_pci_dma_range_parser_init(&parser, np)) { - dev_err(dev, "missing dma-ranges property\n"); - return -EINVAL; - } - - /* - * Get the dma-ranges from the device tree - */ - for_each_of_pci_range(&parser, &range) { - int ret; - u32 pci_base, pci_map; - - ret = v3_get_dma_range_config(v3, &range, &pci_base, &pci_map); - if (ret) - return ret; - - if (i == 0) { - writel(pci_base, v3->base + V3_PCI_BASE0); - writel(pci_map, v3->base + V3_PCI_MAP0); - } else if (i == 1) { - writel(pci_base, v3->base + V3_PCI_BASE1); - writel(pci_map, v3->base + V3_PCI_MAP1); - } else { - dev_err(dev, "too many ranges, only two supported\n"); - dev_err(dev, "range %d ignored\n", i); - } - i++; - } - return 0; -} - -static int v3_pci_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - resource_size_t io_base; - struct resource *regs; - struct resource_entry *win; - struct v3_pci *v3; - struct pci_host_bridge *host; - struct clk *clk; - u16 val; - int irq; - int ret; - LIST_HEAD(res); - - host = pci_alloc_host_bridge(sizeof(*v3)); - if (!host) - return -ENOMEM; - - host->dev.parent = dev; - host->ops = &v3_pci_ops; - host->busnr = 0; - host->msi = NULL; - host->map_irq = of_irq_parse_and_map_pci; - host->swizzle_irq = pci_common_swizzle; - v3 = pci_host_bridge_priv(host); - host->sysdata = v3; - v3->dev = dev; - - /* Get and enable host clock */ - clk = devm_clk_get(dev, NULL); - if (IS_ERR(clk)) { - dev_err(dev, "clock not found\n"); - return PTR_ERR(clk); - } - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "unable to enable clock\n"); - return ret; - } - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - v3->base = devm_ioremap_resource(dev, regs); - if (IS_ERR(v3->base)) - return PTR_ERR(v3->base); - /* - * The hardware has a register with the physical base address - * of the V3 controller itself, verify that this is the same - * as the physical memory we've remapped it from. - */ - if (readl(v3->base + V3_LB_IO_BASE) != (regs->start >> 16)) - dev_err(dev, "V3_LB_IO_BASE = %08x but device is @%pR\n", - readl(v3->base + V3_LB_IO_BASE), regs); - - /* Configuration space is 16MB directly mapped */ - regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (resource_size(regs) != SZ_16M) { - dev_err(dev, "config mem is not 16MB!\n"); - return -EINVAL; - } - v3->config_mem = regs->start; - v3->config_base = devm_ioremap_resource(dev, regs); - if (IS_ERR(v3->config_base)) - return PTR_ERR(v3->config_base); - - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, - &io_base); - if (ret) - return ret; - - ret = devm_request_pci_bus_resources(dev, &res); - if (ret) - return ret; - - /* Get and request error IRQ resource */ - irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "unable to obtain PCIv3 error IRQ\n"); - return -ENODEV; - } - ret = devm_request_irq(dev, irq, v3_irq, 0, - "PCIv3 error", v3); - if (ret < 0) { - dev_err(dev, - "unable to request PCIv3 error IRQ %d (%d)\n", - irq, ret); - return ret; - } - - /* - * Unlock V3 registers, but only if they were previously locked. - */ - if (readw(v3->base + V3_SYSTEM) & V3_SYSTEM_M_LOCK) - writew(V3_SYSTEM_UNLOCK, v3->base + V3_SYSTEM); - - /* Disable all slave access while we set up the windows */ - val = readw(v3->base + V3_PCI_CMD); - val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - writew(val, v3->base + V3_PCI_CMD); - - /* Put the PCI bus into reset */ - val = readw(v3->base + V3_SYSTEM); - val &= ~V3_SYSTEM_M_RST_OUT; - writew(val, v3->base + V3_SYSTEM); - - /* Retry until we're ready */ - val = readw(v3->base + V3_PCI_CFG); - val |= V3_PCI_CFG_M_RETRY_EN; - writew(val, v3->base + V3_PCI_CFG); - - /* Set up the local bus protocol */ - val = readw(v3->base + V3_LB_CFG); - val |= V3_LB_CFG_LB_BE_IMODE; /* Byte enable input */ - val |= V3_LB_CFG_LB_BE_OMODE; /* Byte enable output */ - val &= ~V3_LB_CFG_LB_ENDIAN; /* Little endian */ - val &= ~V3_LB_CFG_LB_PPC_RDY; /* TODO: when using on PPC403Gx, set to 1 */ - writew(val, v3->base + V3_LB_CFG); - - /* Enable the PCI bus master */ - val = readw(v3->base + V3_PCI_CMD); - val |= PCI_COMMAND_MASTER; - writew(val, v3->base + V3_PCI_CMD); - - /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry(win, &res) { - ret = v3_pci_setup_resource(v3, io_base, host, win); - if (ret) { - dev_err(dev, "error setting up resources\n"); - return ret; - } - } - ret = v3_pci_parse_map_dma_ranges(v3, np); - if (ret) - return ret; - - /* - * Disable PCI to host IO cycles, enable I/O buffers @3.3V, - * set AD_LOW0 to 1 if one of the LB_MAP registers choose - * to use this (should be unused). - */ - writel(0x00000000, v3->base + V3_PCI_IO_BASE); - val = V3_PCI_CFG_M_IO_REG_DIS | V3_PCI_CFG_M_IO_DIS | - V3_PCI_CFG_M_EN3V | V3_PCI_CFG_M_AD_LOW0; - /* - * DMA read and write from PCI bus commands types - */ - val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_RTYPE_SHIFT; - val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_WTYPE_SHIFT; - writew(val, v3->base + V3_PCI_CFG); - - /* - * Set the V3 FIFO such that writes have higher priority than - * reads, and local bus write causes local bus read fifo flush - * on aperture 1. Same for PCI. - */ - writew(V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 | - V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 | - V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 | - V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1, - v3->base + V3_FIFO_PRIORITY); - - - /* - * Clear any error interrupts, and enable parity and write error - * interrupts - */ - writeb(0, v3->base + V3_LB_ISTAT); - val = readw(v3->base + V3_LB_CFG); - val |= V3_LB_CFG_LB_LB_INT; - writew(val, v3->base + V3_LB_CFG); - writeb(V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR, - v3->base + V3_LB_IMASK); - - /* Special Integrator initialization */ - if (of_device_is_compatible(np, "arm,integrator-ap-pci")) { - ret = v3_integrator_init(v3); - if (ret) - return ret; - } - - /* Post-init: enable PCI memory and invalidate (master already on) */ - val = readw(v3->base + V3_PCI_CMD); - val |= PCI_COMMAND_MEMORY | PCI_COMMAND_INVALIDATE; - writew(val, v3->base + V3_PCI_CMD); - - /* Clear pending interrupts */ - writeb(0, v3->base + V3_LB_ISTAT); - /* Read or write errors and parity errors cause interrupts */ - writeb(V3_LB_ISTAT_PCI_RD | V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR, - v3->base + V3_LB_IMASK); - - /* Take the PCI bus out of reset so devices can initialize */ - val = readw(v3->base + V3_SYSTEM); - val |= V3_SYSTEM_M_RST_OUT; - writew(val, v3->base + V3_SYSTEM); - - /* - * Re-lock the system register. - */ - val = readw(v3->base + V3_SYSTEM); - val |= V3_SYSTEM_M_LOCK; - writew(val, v3->base + V3_SYSTEM); - - list_splice_init(&res, &host->windows); - ret = pci_scan_root_bus_bridge(host); - if (ret) { - dev_err(dev, "failed to register host: %d\n", ret); - return ret; - } - v3->bus = host->bus; - - pci_bus_assign_resources(v3->bus); - pci_bus_add_devices(v3->bus); - - return 0; -} - -static const struct of_device_id v3_pci_of_match[] = { - { - .compatible = "v3,v360epc-pci", - }, - {}, -}; - -static struct platform_driver v3_pci_driver = { - .driver = { - .name = "pci-v3-semi", - .of_match_table = of_match_ptr(v3_pci_of_match), - .suppress_bind_attrs = true, - }, - .probe = v3_pci_probe, -}; -builtin_platform_driver(v3_pci_driver); diff --git a/drivers/pci/host/pci-versatile.c b/drivers/pci/host/pci-versatile.c deleted file mode 100644 index 994f32061b32..000000000000 --- a/drivers/pci/host/pci-versatile.c +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright 2004 Koninklijke Philips Electronics NV - * - * Conversion to platform driver and DT: - * Copyright 2014 Linaro Ltd. - * - * 14/04/2005 Initial version, colin.king@philips.com - */ -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -static void __iomem *versatile_pci_base; -static void __iomem *versatile_cfg_base[2]; - -#define PCI_IMAP(m) (versatile_pci_base + ((m) * 4)) -#define PCI_SMAP(m) (versatile_pci_base + 0x14 + ((m) * 4)) -#define PCI_SELFID (versatile_pci_base + 0xc) - -#define VP_PCI_DEVICE_ID 0x030010ee -#define VP_PCI_CLASS_ID 0x0b400000 - -static u32 pci_slot_ignore; - -static int __init versatile_pci_slot_ignore(char *str) -{ - int retval; - int slot; - - while ((retval = get_option(&str, &slot))) { - if ((slot < 0) || (slot > 31)) - pr_err("Illegal slot value: %d\n", slot); - else - pci_slot_ignore |= (1 << slot); - } - return 1; -} -__setup("pci_slot_ignore=", versatile_pci_slot_ignore); - - -static void __iomem *versatile_map_bus(struct pci_bus *bus, - unsigned int devfn, int offset) -{ - unsigned int busnr = bus->number; - - if (pci_slot_ignore & (1 << PCI_SLOT(devfn))) - return NULL; - - return versatile_cfg_base[1] + ((busnr << 16) | (devfn << 8) | offset); -} - -static struct pci_ops pci_versatile_ops = { - .map_bus = versatile_map_bus, - .read = pci_generic_config_read32, - .write = pci_generic_config_write, -}; - -static int versatile_pci_parse_request_of_pci_ranges(struct device *dev, - struct list_head *res) -{ - int err, mem = 1, res_valid = 0; - resource_size_t iobase; - struct resource_entry *win, *tmp; - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, res, &iobase); - if (err) - return err; - - err = devm_request_pci_bus_resources(dev, res); - if (err) - goto out_release_res; - - resource_list_for_each_entry_safe(win, tmp, res) { - struct resource *res = win->res; - - switch (resource_type(res)) { - case IORESOURCE_IO: - err = pci_remap_iospace(res, iobase); - if (err) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - err, res); - resource_list_destroy_entry(win); - } - break; - case IORESOURCE_MEM: - res_valid |= !(res->flags & IORESOURCE_PREFETCH); - - writel(res->start >> 28, PCI_IMAP(mem)); - writel(PHYS_OFFSET >> 28, PCI_SMAP(mem)); - mem++; - - break; - } - } - - if (res_valid) - return 0; - - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; - -out_release_res: - pci_free_resource_list(res); - return err; -} - -static int versatile_pci_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *res; - int ret, i, myslot = -1; - u32 val; - void __iomem *local_pci_cfg_base; - struct pci_bus *bus, *child; - struct pci_host_bridge *bridge; - LIST_HEAD(pci_res); - - bridge = devm_pci_alloc_host_bridge(dev, 0); - if (!bridge) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - versatile_pci_base = devm_ioremap_resource(dev, res); - if (IS_ERR(versatile_pci_base)) - return PTR_ERR(versatile_pci_base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - versatile_cfg_base[0] = devm_ioremap_resource(dev, res); - if (IS_ERR(versatile_cfg_base[0])) - return PTR_ERR(versatile_cfg_base[0]); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - versatile_cfg_base[1] = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(versatile_cfg_base[1])) - return PTR_ERR(versatile_cfg_base[1]); - - ret = versatile_pci_parse_request_of_pci_ranges(dev, &pci_res); - if (ret) - return ret; - - /* - * We need to discover the PCI core first to configure itself - * before the main PCI probing is performed - */ - for (i = 0; i < 32; i++) { - if ((readl(versatile_cfg_base[0] + (i << 11) + PCI_VENDOR_ID) == VP_PCI_DEVICE_ID) && - (readl(versatile_cfg_base[0] + (i << 11) + PCI_CLASS_REVISION) == VP_PCI_CLASS_ID)) { - myslot = i; - break; - } - } - if (myslot == -1) { - dev_err(dev, "Cannot find PCI core!\n"); - return -EIO; - } - /* - * Do not to map Versatile FPGA PCI device into memory space - */ - pci_slot_ignore |= (1 << myslot); - - dev_info(dev, "PCI core found (slot %d)\n", myslot); - - writel(myslot, PCI_SELFID); - local_pci_cfg_base = versatile_cfg_base[1] + (myslot << 11); - - val = readl(local_pci_cfg_base + PCI_COMMAND); - val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; - writel(val, local_pci_cfg_base + PCI_COMMAND); - - /* - * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM - */ - writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0); - writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1); - writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2); - - /* - * For many years the kernel and QEMU were symbiotically buggy - * in that they both assumed the same broken IRQ mapping. - * QEMU therefore attempts to auto-detect old broken kernels - * so that they still work on newer QEMU as they did on old - * QEMU. Since we now use the correct (ie matching-hardware) - * IRQ mapping we write a definitely different value to a - * PCI_INTERRUPT_LINE register to tell QEMU that we expect - * real hardware behaviour and it need not be backwards - * compatible for us. This write is harmless on real hardware. - */ - writel(0, versatile_cfg_base[0] + PCI_INTERRUPT_LINE); - - pci_add_flags(PCI_ENABLE_PROC_DOMAINS); - pci_add_flags(PCI_REASSIGN_ALL_BUS); - - list_splice_init(&pci_res, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = NULL; - bridge->busnr = 0; - bridge->ops = &pci_versatile_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) - return ret; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - - return 0; -} - -static const struct of_device_id versatile_pci_of_match[] = { - { .compatible = "arm,versatile-pci", }, - { }, -}; -MODULE_DEVICE_TABLE(of, versatile_pci_of_match); - -static struct platform_driver versatile_pci_driver = { - .driver = { - .name = "versatile-pci", - .of_match_table = versatile_pci_of_match, - .suppress_bind_attrs = true, - }, - .probe = versatile_pci_probe, -}; -module_platform_driver(versatile_pci_driver); - -MODULE_DESCRIPTION("Versatile PCI driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c deleted file mode 100644 index f4c02da84e59..000000000000 --- a/drivers/pci/host/pci-xgene-msi.c +++ /dev/null @@ -1,543 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * APM X-Gene MSI Driver - * - * Copyright (c) 2014, Applied Micro Circuits Corporation - * Author: Tanmay Inamdar - * Duc Dang - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MSI_IR0 0x000000 -#define MSI_INT0 0x800000 -#define IDX_PER_GROUP 8 -#define IRQS_PER_IDX 16 -#define NR_HW_IRQS 16 -#define NR_MSI_VEC (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS) - -struct xgene_msi_group { - struct xgene_msi *msi; - int gic_irq; - u32 msi_grp; -}; - -struct xgene_msi { - struct device_node *node; - struct irq_domain *inner_domain; - struct irq_domain *msi_domain; - u64 msi_addr; - void __iomem *msi_regs; - unsigned long *bitmap; - struct mutex bitmap_lock; - struct xgene_msi_group *msi_groups; - int num_cpus; -}; - -/* Global data */ -static struct xgene_msi xgene_msi_ctrl; - -static struct irq_chip xgene_msi_top_irq_chip = { - .name = "X-Gene1 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 struct msi_domain_info xgene_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX), - .chip = &xgene_msi_top_irq_chip, -}; - -/* - * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where - * n is group number (0..F), x is index of registers in each group (0..7) - * The register layout is as follows: - * MSI0IR0 base_addr - * MSI0IR1 base_addr + 0x10000 - * ... ... - * MSI0IR6 base_addr + 0x60000 - * MSI0IR7 base_addr + 0x70000 - * MSI1IR0 base_addr + 0x80000 - * MSI1IR1 base_addr + 0x90000 - * ... ... - * MSI1IR7 base_addr + 0xF0000 - * MSI2IR0 base_addr + 0x100000 - * ... ... - * MSIFIR0 base_addr + 0x780000 - * MSIFIR1 base_addr + 0x790000 - * ... ... - * MSIFIR7 base_addr + 0x7F0000 - * MSIINT0 base_addr + 0x800000 - * MSIINT1 base_addr + 0x810000 - * ... ... - * MSIINTF base_addr + 0x8F0000 - * - * Each index register supports 16 MSI vectors (0..15) to generate interrupt. - * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination - * registers. - * - * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate - * the MSI pending status caused by 1 of its 8 index registers. - */ - -/* MSInIRx read helper */ -static u32 xgene_msi_ir_read(struct xgene_msi *msi, - u32 msi_grp, u32 msir_idx) -{ - return readl_relaxed(msi->msi_regs + MSI_IR0 + - (msi_grp << 19) + (msir_idx << 16)); -} - -/* MSIINTn read helper */ -static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp) -{ - return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16)); -} - -/* - * With 2048 MSI vectors supported, the MSI message can be constructed using - * following scheme: - * - Divide into 8 256-vector groups - * Group 0: 0-255 - * Group 1: 256-511 - * Group 2: 512-767 - * ... - * Group 7: 1792-2047 - * - Each 256-vector group is divided into 16 16-vector groups - * As an example: 16 16-vector groups for 256-vector group 0-255 is - * Group 0: 0-15 - * Group 1: 16-32 - * ... - * Group 15: 240-255 - * - The termination address of MSI vector in 256-vector group n and 16-vector - * group x is the address of MSIxIRn - * - The data for MSI vector in 16-vector group x is x - */ -static u32 hwirq_to_reg_set(unsigned long hwirq) -{ - return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX)); -} - -static u32 hwirq_to_group(unsigned long hwirq) -{ - return (hwirq % NR_HW_IRQS); -} - -static u32 hwirq_to_msi_data(unsigned long hwirq) -{ - return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX); -} - -static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct xgene_msi *msi = irq_data_get_irq_chip_data(data); - u32 reg_set = hwirq_to_reg_set(data->hwirq); - u32 group = hwirq_to_group(data->hwirq); - u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16); - - msg->address_hi = upper_32_bits(target_addr); - msg->address_lo = lower_32_bits(target_addr); - msg->data = hwirq_to_msi_data(data->hwirq); -} - -/* - * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors. To maintain - * the expected behaviour of .set_affinity for each MSI interrupt, the 16 - * MSI GIC IRQs are statically allocated to 8 X-Gene v1 cores (2 GIC IRQs - * for each core). The MSI vector is moved fom 1 MSI GIC IRQ to another - * MSI GIC IRQ to steer its MSI interrupt to correct X-Gene v1 core. As a - * consequence, the total MSI vectors that X-Gene v1 supports will be - * reduced to 256 (2048/8) vectors. - */ -static int hwirq_to_cpu(unsigned long hwirq) -{ - return (hwirq % xgene_msi_ctrl.num_cpus); -} - -static unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq) -{ - return (hwirq - hwirq_to_cpu(hwirq)); -} - -static int xgene_msi_set_affinity(struct irq_data *irqdata, - const struct cpumask *mask, bool force) -{ - int target_cpu = cpumask_first(mask); - int curr_cpu; - - curr_cpu = hwirq_to_cpu(irqdata->hwirq); - if (curr_cpu == target_cpu) - return IRQ_SET_MASK_OK_DONE; - - /* Update MSI number to target the new CPU */ - irqdata->hwirq = hwirq_to_canonical_hwirq(irqdata->hwirq) + target_cpu; - - return IRQ_SET_MASK_OK; -} - -static struct irq_chip xgene_msi_bottom_irq_chip = { - .name = "MSI", - .irq_set_affinity = xgene_msi_set_affinity, - .irq_compose_msi_msg = xgene_compose_msi_msg, -}; - -static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *args) -{ - struct xgene_msi *msi = domain->host_data; - int msi_irq; - - mutex_lock(&msi->bitmap_lock); - - msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0, - msi->num_cpus, 0); - if (msi_irq < NR_MSI_VEC) - bitmap_set(msi->bitmap, msi_irq, msi->num_cpus); - else - msi_irq = -ENOSPC; - - mutex_unlock(&msi->bitmap_lock); - - if (msi_irq < 0) - return msi_irq; - - irq_domain_set_info(domain, virq, msi_irq, - &xgene_msi_bottom_irq_chip, domain->host_data, - handle_simple_irq, NULL, NULL); - - return 0; -} - -static void xgene_irq_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) -{ - struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct xgene_msi *msi = irq_data_get_irq_chip_data(d); - u32 hwirq; - - mutex_lock(&msi->bitmap_lock); - - hwirq = hwirq_to_canonical_hwirq(d->hwirq); - bitmap_clear(msi->bitmap, hwirq, msi->num_cpus); - - mutex_unlock(&msi->bitmap_lock); - - irq_domain_free_irqs_parent(domain, virq, nr_irqs); -} - -static const struct irq_domain_ops msi_domain_ops = { - .alloc = xgene_irq_domain_alloc, - .free = xgene_irq_domain_free, -}; - -static int xgene_allocate_domains(struct xgene_msi *msi) -{ - msi->inner_domain = irq_domain_add_linear(NULL, NR_MSI_VEC, - &msi_domain_ops, msi); - if (!msi->inner_domain) - return -ENOMEM; - - msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(msi->node), - &xgene_msi_domain_info, - msi->inner_domain); - - if (!msi->msi_domain) { - irq_domain_remove(msi->inner_domain); - return -ENOMEM; - } - - return 0; -} - -static void xgene_free_domains(struct xgene_msi *msi) -{ - if (msi->msi_domain) - irq_domain_remove(msi->msi_domain); - if (msi->inner_domain) - irq_domain_remove(msi->inner_domain); -} - -static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi) -{ - int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long); - - xgene_msi->bitmap = kzalloc(size, GFP_KERNEL); - if (!xgene_msi->bitmap) - return -ENOMEM; - - mutex_init(&xgene_msi->bitmap_lock); - - xgene_msi->msi_groups = kcalloc(NR_HW_IRQS, - sizeof(struct xgene_msi_group), - GFP_KERNEL); - if (!xgene_msi->msi_groups) - return -ENOMEM; - - return 0; -} - -static void xgene_msi_isr(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct xgene_msi_group *msi_groups; - struct xgene_msi *xgene_msi; - unsigned int virq; - int msir_index, msir_val, hw_irq; - u32 intr_index, grp_select, msi_grp; - - chained_irq_enter(chip, desc); - - msi_groups = irq_desc_get_handler_data(desc); - xgene_msi = msi_groups->msi; - msi_grp = msi_groups->msi_grp; - - /* - * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt - * If bit x of this register is set (x is 0..7), one or more interupts - * corresponding to MSInIRx is set. - */ - grp_select = xgene_msi_int_read(xgene_msi, msi_grp); - while (grp_select) { - msir_index = ffs(grp_select) - 1; - /* - * Calculate MSInIRx address to read to check for interrupts - * (refer to termination address and data assignment - * described in xgene_compose_msi_msg() ) - */ - msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index); - while (msir_val) { - intr_index = ffs(msir_val) - 1; - /* - * Calculate MSI vector number (refer to the termination - * address and data assignment described in - * xgene_compose_msi_msg function) - */ - hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) * - NR_HW_IRQS) + msi_grp; - /* - * As we have multiple hw_irq that maps to single MSI, - * always look up the virq using the hw_irq as seen from - * CPU0 - */ - hw_irq = hwirq_to_canonical_hwirq(hw_irq); - virq = irq_find_mapping(xgene_msi->inner_domain, hw_irq); - WARN_ON(!virq); - if (virq != 0) - generic_handle_irq(virq); - msir_val &= ~(1 << intr_index); - } - grp_select &= ~(1 << msir_index); - - if (!grp_select) { - /* - * We handled all interrupts happened in this group, - * resample this group MSI_INTx register in case - * something else has been made pending in the meantime - */ - grp_select = xgene_msi_int_read(xgene_msi, msi_grp); - } - } - - chained_irq_exit(chip, desc); -} - -static enum cpuhp_state pci_xgene_online; - -static int xgene_msi_remove(struct platform_device *pdev) -{ - struct xgene_msi *msi = platform_get_drvdata(pdev); - - if (pci_xgene_online) - cpuhp_remove_state(pci_xgene_online); - cpuhp_remove_state(CPUHP_PCI_XGENE_DEAD); - - kfree(msi->msi_groups); - - kfree(msi->bitmap); - msi->bitmap = NULL; - - xgene_free_domains(msi); - - return 0; -} - -static int xgene_msi_hwirq_alloc(unsigned int cpu) -{ - struct xgene_msi *msi = &xgene_msi_ctrl; - struct xgene_msi_group *msi_group; - cpumask_var_t mask; - int i; - int err; - - for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) { - msi_group = &msi->msi_groups[i]; - if (!msi_group->gic_irq) - continue; - - irq_set_chained_handler(msi_group->gic_irq, - xgene_msi_isr); - err = irq_set_handler_data(msi_group->gic_irq, msi_group); - if (err) { - pr_err("failed to register GIC IRQ handler\n"); - return -EINVAL; - } - /* - * Statically allocate MSI GIC IRQs to each CPU core. - * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated - * to each core. - */ - if (alloc_cpumask_var(&mask, GFP_KERNEL)) { - cpumask_clear(mask); - cpumask_set_cpu(cpu, mask); - err = irq_set_affinity(msi_group->gic_irq, mask); - if (err) - pr_err("failed to set affinity for GIC IRQ"); - free_cpumask_var(mask); - } else { - pr_err("failed to alloc CPU mask for affinity\n"); - err = -EINVAL; - } - - if (err) { - irq_set_chained_handler_and_data(msi_group->gic_irq, - NULL, NULL); - return err; - } - } - - return 0; -} - -static int xgene_msi_hwirq_free(unsigned int cpu) -{ - struct xgene_msi *msi = &xgene_msi_ctrl; - struct xgene_msi_group *msi_group; - int i; - - for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) { - msi_group = &msi->msi_groups[i]; - if (!msi_group->gic_irq) - continue; - - irq_set_chained_handler_and_data(msi_group->gic_irq, NULL, - NULL); - } - return 0; -} - -static const struct of_device_id xgene_msi_match_table[] = { - {.compatible = "apm,xgene1-msi"}, - {}, -}; - -static int xgene_msi_probe(struct platform_device *pdev) -{ - struct resource *res; - int rc, irq_index; - struct xgene_msi *xgene_msi; - int virt_msir; - u32 msi_val, msi_idx; - - xgene_msi = &xgene_msi_ctrl; - - platform_set_drvdata(pdev, xgene_msi); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(xgene_msi->msi_regs)) { - dev_err(&pdev->dev, "no reg space\n"); - rc = PTR_ERR(xgene_msi->msi_regs); - goto error; - } - xgene_msi->msi_addr = res->start; - xgene_msi->node = pdev->dev.of_node; - xgene_msi->num_cpus = num_possible_cpus(); - - rc = xgene_msi_init_allocator(xgene_msi); - if (rc) { - dev_err(&pdev->dev, "Error allocating MSI bitmap\n"); - goto error; - } - - rc = xgene_allocate_domains(xgene_msi); - if (rc) { - dev_err(&pdev->dev, "Failed to allocate MSI domain\n"); - goto error; - } - - for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { - virt_msir = platform_get_irq(pdev, irq_index); - if (virt_msir < 0) { - dev_err(&pdev->dev, "Cannot translate IRQ index %d\n", - irq_index); - rc = virt_msir; - goto error; - } - xgene_msi->msi_groups[irq_index].gic_irq = virt_msir; - xgene_msi->msi_groups[irq_index].msi_grp = irq_index; - xgene_msi->msi_groups[irq_index].msi = xgene_msi; - } - - /* - * MSInIRx registers are read-to-clear; before registering - * interrupt handlers, read all of them to clear spurious - * interrupts that may occur before the driver is probed. - */ - for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { - for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) - msi_val = xgene_msi_ir_read(xgene_msi, irq_index, - msi_idx); - /* Read MSIINTn to confirm */ - msi_val = xgene_msi_int_read(xgene_msi, irq_index); - if (msi_val) { - dev_err(&pdev->dev, "Failed to clear spurious IRQ\n"); - rc = -EINVAL; - goto error; - } - } - - rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "pci/xgene:online", - xgene_msi_hwirq_alloc, NULL); - if (rc < 0) - goto err_cpuhp; - pci_xgene_online = rc; - rc = cpuhp_setup_state(CPUHP_PCI_XGENE_DEAD, "pci/xgene:dead", NULL, - xgene_msi_hwirq_free); - if (rc) - goto err_cpuhp; - - dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n"); - - return 0; - -err_cpuhp: - dev_err(&pdev->dev, "failed to add CPU MSI notifier\n"); -error: - xgene_msi_remove(pdev); - return rc; -} - -static struct platform_driver xgene_msi_driver = { - .driver = { - .name = "xgene-msi", - .of_match_table = xgene_msi_match_table, - }, - .probe = xgene_msi_probe, - .remove = xgene_msi_remove, -}; - -static int __init xgene_pcie_msi_init(void) -{ - return platform_driver_register(&xgene_msi_driver); -} -subsys_initcall(xgene_pcie_msi_init); diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c deleted file mode 100644 index d854d67e873c..000000000000 --- a/drivers/pci/host/pci-xgene.c +++ /dev/null @@ -1,689 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/** - * APM X-Gene PCIe Driver - * - * Copyright (c) 2014 Applied Micro Circuits Corporation. - * - * Author: Tanmay Inamdar . - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -#define PCIECORE_CTLANDSTATUS 0x50 -#define PIM1_1L 0x80 -#define IBAR2 0x98 -#define IR2MSK 0x9c -#define PIM2_1L 0xa0 -#define IBAR3L 0xb4 -#define IR3MSKL 0xbc -#define PIM3_1L 0xc4 -#define OMR1BARL 0x100 -#define OMR2BARL 0x118 -#define OMR3BARL 0x130 -#define CFGBARL 0x154 -#define CFGBARH 0x158 -#define CFGCTL 0x15c -#define RTDID 0x160 -#define BRIDGE_CFG_0 0x2000 -#define BRIDGE_CFG_4 0x2010 -#define BRIDGE_STATUS_0 0x2600 - -#define LINK_UP_MASK 0x00000100 -#define AXI_EP_CFG_ACCESS 0x10000 -#define EN_COHERENCY 0xF0000000 -#define EN_REG 0x00000001 -#define OB_LO_IO 0x00000002 -#define XGENE_PCIE_VENDORID 0x10E8 -#define XGENE_PCIE_DEVICEID 0xE004 -#define SZ_1T (SZ_1G*1024ULL) -#define PIPE_PHY_RATE_RD(src) ((0xc000 & (u32)(src)) >> 0xe) - -#define XGENE_V1_PCI_EXP_CAP 0x40 - -/* PCIe IP version */ -#define XGENE_PCIE_IP_VER_UNKN 0 -#define XGENE_PCIE_IP_VER_1 1 -#define XGENE_PCIE_IP_VER_2 2 - -#if defined(CONFIG_PCI_XGENE) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) -struct xgene_pcie_port { - struct device_node *node; - struct device *dev; - struct clk *clk; - void __iomem *csr_base; - void __iomem *cfg_base; - unsigned long cfg_addr; - bool link_up; - u32 version; -}; - -static u32 xgene_pcie_readl(struct xgene_pcie_port *port, u32 reg) -{ - return readl(port->csr_base + reg); -} - -static void xgene_pcie_writel(struct xgene_pcie_port *port, u32 reg, u32 val) -{ - writel(val, port->csr_base + reg); -} - -static inline u32 pcie_bar_low_val(u32 addr, u32 flags) -{ - return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags; -} - -static inline struct xgene_pcie_port *pcie_bus_to_port(struct pci_bus *bus) -{ - struct pci_config_window *cfg; - - if (acpi_disabled) - return (struct xgene_pcie_port *)(bus->sysdata); - - cfg = bus->sysdata; - return (struct xgene_pcie_port *)(cfg->priv); -} - -/* - * When the address bit [17:16] is 2'b01, the Configuration access will be - * treated as Type 1 and it will be forwarded to external PCIe device. - */ -static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus) -{ - struct xgene_pcie_port *port = pcie_bus_to_port(bus); - - if (bus->number >= (bus->primary + 1)) - return port->cfg_base + AXI_EP_CFG_ACCESS; - - return port->cfg_base; -} - -/* - * For Configuration request, RTDID register is used as Bus Number, - * Device Number and Function number of the header fields. - */ -static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn) -{ - struct xgene_pcie_port *port = pcie_bus_to_port(bus); - unsigned int b, d, f; - u32 rtdid_val = 0; - - b = bus->number; - d = PCI_SLOT(devfn); - f = PCI_FUNC(devfn); - - if (!pci_is_root_bus(bus)) - rtdid_val = (b << 8) | (d << 3) | f; - - xgene_pcie_writel(port, RTDID, rtdid_val); - /* read the register back to ensure flush */ - xgene_pcie_readl(port, RTDID); -} - -/* - * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as - * the translation from PCI bus to native BUS. Entire DDR region - * is mapped into PCIe space using these registers, so it can be - * reached by DMA from EP devices. The BAR0/1 of bridge should be - * hidden during enumeration to avoid the sizing and resource allocation - * by PCIe core. - */ -static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) -{ - if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) || - (offset == PCI_BASE_ADDRESS_1))) - return true; - - return false; -} - -static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, - int offset) -{ - if ((pci_is_root_bus(bus) && devfn != 0) || - xgene_pcie_hide_rc_bars(bus, offset)) - return NULL; - - xgene_pcie_set_rtdid_reg(bus, devfn); - return xgene_pcie_get_cfg_base(bus) + offset; -} - -static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct xgene_pcie_port *port = pcie_bus_to_port(bus); - - if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) != - PCIBIOS_SUCCESSFUL) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* - * The v1 controller has a bug in its Configuration Request - * Retry Status (CRS) logic: when CRS is enabled and we read the - * Vendor and Device ID of a non-existent device, the controller - * fabricates return data of 0xFFFF0001 ("device exists but is not - * ready") instead of 0xFFFFFFFF ("device does not exist"). This - * causes the PCI core to retry the read until it times out. - * Avoid this by not claiming to support CRS. - */ - if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) && - ((where & ~0x3) == XGENE_V1_PCI_EXP_CAP + PCI_EXP_RTCTL)) - *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); - - if (size <= 2) - *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); - - return PCIBIOS_SUCCESSFUL; -} -#endif - -#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) -static int xgene_get_csr_resource(struct acpi_device *adev, - struct resource *res) -{ - struct device *dev = &adev->dev; - struct resource_entry *entry; - struct list_head list; - unsigned long flags; - int ret; - - INIT_LIST_HEAD(&list); - flags = IORESOURCE_MEM; - ret = acpi_dev_get_resources(adev, &list, - acpi_dev_filter_resource_type_cb, - (void *) flags); - if (ret < 0) { - dev_err(dev, "failed to parse _CRS method, error code %d\n", - ret); - return ret; - } - - if (ret == 0) { - dev_err(dev, "no IO and memory resources present in _CRS\n"); - return -EINVAL; - } - - entry = list_first_entry(&list, struct resource_entry, node); - *res = *entry->res; - acpi_dev_free_resource_list(&list); - return 0; -} - -static int xgene_pcie_ecam_init(struct pci_config_window *cfg, u32 ipversion) -{ - struct device *dev = cfg->parent; - struct acpi_device *adev = to_acpi_device(dev); - struct xgene_pcie_port *port; - struct resource csr; - int ret; - - port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); - if (!port) - return -ENOMEM; - - ret = xgene_get_csr_resource(adev, &csr); - if (ret) { - dev_err(dev, "can't get CSR resource\n"); - return ret; - } - port->csr_base = devm_pci_remap_cfg_resource(dev, &csr); - if (IS_ERR(port->csr_base)) - return PTR_ERR(port->csr_base); - - port->cfg_base = cfg->win; - port->version = ipversion; - - cfg->priv = port; - return 0; -} - -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 = { - .bus_shift = 16, - .init = xgene_v1_pcie_ecam_init, - .pci_ops = { - .map_bus = xgene_pcie_map_bus, - .read = xgene_pcie_config_read32, - .write = pci_generic_config_write, - } -}; - -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 = { - .bus_shift = 16, - .init = xgene_v2_pcie_ecam_init, - .pci_ops = { - .map_bus = xgene_pcie_map_bus, - .read = xgene_pcie_config_read32, - .write = pci_generic_config_write, - } -}; -#endif - -#if defined(CONFIG_PCI_XGENE) -static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr, - u32 flags, u64 size) -{ - u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags; - u32 val32 = 0; - u32 val; - - val32 = xgene_pcie_readl(port, addr); - val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16); - xgene_pcie_writel(port, addr, val); - - val32 = xgene_pcie_readl(port, addr + 0x04); - val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16); - xgene_pcie_writel(port, addr + 0x04, val); - - val32 = xgene_pcie_readl(port, addr + 0x04); - val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16); - xgene_pcie_writel(port, addr + 0x04, val); - - val32 = xgene_pcie_readl(port, addr + 0x08); - val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16); - xgene_pcie_writel(port, addr + 0x08, val); - - return mask; -} - -static void xgene_pcie_linkup(struct xgene_pcie_port *port, - u32 *lanes, u32 *speed) -{ - u32 val32; - - port->link_up = false; - val32 = xgene_pcie_readl(port, PCIECORE_CTLANDSTATUS); - if (val32 & LINK_UP_MASK) { - port->link_up = true; - *speed = PIPE_PHY_RATE_RD(val32); - val32 = xgene_pcie_readl(port, BRIDGE_STATUS_0); - *lanes = val32 >> 26; - } -} - -static int xgene_pcie_init_port(struct xgene_pcie_port *port) -{ - struct device *dev = port->dev; - int rc; - - port->clk = clk_get(dev, NULL); - if (IS_ERR(port->clk)) { - dev_err(dev, "clock not available\n"); - return -ENODEV; - } - - rc = clk_prepare_enable(port->clk); - if (rc) { - dev_err(dev, "clock enable failed\n"); - return rc; - } - - return 0; -} - -static int xgene_pcie_map_reg(struct xgene_pcie_port *port, - struct platform_device *pdev) -{ - struct device *dev = port->dev; - struct resource *res; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); - port->csr_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(port->csr_base)) - return PTR_ERR(port->csr_base); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); - port->cfg_base = devm_ioremap_resource(dev, res); - if (IS_ERR(port->cfg_base)) - return PTR_ERR(port->cfg_base); - port->cfg_addr = res->start; - - return 0; -} - -static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port, - struct resource *res, u32 offset, - u64 cpu_addr, u64 pci_addr) -{ - struct device *dev = port->dev; - resource_size_t size = resource_size(res); - u64 restype = resource_type(res); - u64 mask = 0; - u32 min_size; - u32 flag = EN_REG; - - if (restype == IORESOURCE_MEM) { - min_size = SZ_128M; - } else { - min_size = 128; - flag |= OB_LO_IO; - } - - if (size >= min_size) - mask = ~(size - 1) | flag; - else - dev_warn(dev, "res size 0x%llx less than minimum 0x%x\n", - (u64)size, min_size); - - xgene_pcie_writel(port, offset, lower_32_bits(cpu_addr)); - xgene_pcie_writel(port, offset + 0x04, upper_32_bits(cpu_addr)); - xgene_pcie_writel(port, offset + 0x08, lower_32_bits(mask)); - xgene_pcie_writel(port, offset + 0x0c, upper_32_bits(mask)); - xgene_pcie_writel(port, offset + 0x10, lower_32_bits(pci_addr)); - xgene_pcie_writel(port, offset + 0x14, upper_32_bits(pci_addr)); -} - -static void xgene_pcie_setup_cfg_reg(struct xgene_pcie_port *port) -{ - u64 addr = port->cfg_addr; - - xgene_pcie_writel(port, CFGBARL, lower_32_bits(addr)); - xgene_pcie_writel(port, CFGBARH, upper_32_bits(addr)); - xgene_pcie_writel(port, CFGCTL, EN_REG); -} - -static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, - struct list_head *res, - resource_size_t io_base) -{ - struct resource_entry *window; - struct device *dev = port->dev; - int ret; - - resource_list_for_each_entry(window, res) { - struct resource *res = window->res; - u64 restype = resource_type(res); - - dev_dbg(dev, "%pR\n", res); - - switch (restype) { - case IORESOURCE_IO: - xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base, - res->start - window->offset); - ret = pci_remap_iospace(res, io_base); - if (ret < 0) - return ret; - break; - case IORESOURCE_MEM: - if (res->flags & IORESOURCE_PREFETCH) - xgene_pcie_setup_ob_reg(port, res, OMR2BARL, - res->start, - res->start - - window->offset); - else - xgene_pcie_setup_ob_reg(port, res, OMR1BARL, - res->start, - res->start - - window->offset); - break; - case IORESOURCE_BUS: - break; - default: - dev_err(dev, "invalid resource %pR\n", res); - return -EINVAL; - } - } - xgene_pcie_setup_cfg_reg(port); - return 0; -} - -static void xgene_pcie_setup_pims(struct xgene_pcie_port *port, u32 pim_reg, - u64 pim, u64 size) -{ - xgene_pcie_writel(port, pim_reg, lower_32_bits(pim)); - xgene_pcie_writel(port, pim_reg + 0x04, - upper_32_bits(pim) | EN_COHERENCY); - xgene_pcie_writel(port, pim_reg + 0x10, lower_32_bits(size)); - xgene_pcie_writel(port, pim_reg + 0x14, upper_32_bits(size)); -} - -/* - * X-Gene PCIe support maximum 3 inbound memory regions - * This function helps to select a region based on size of region - */ -static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size) -{ - if ((size > 4) && (size < SZ_16M) && !(*ib_reg_mask & (1 << 1))) { - *ib_reg_mask |= (1 << 1); - return 1; - } - - if ((size > SZ_1K) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 0))) { - *ib_reg_mask |= (1 << 0); - return 0; - } - - if ((size > SZ_1M) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 2))) { - *ib_reg_mask |= (1 << 2); - return 2; - } - - return -EINVAL; -} - -static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, - struct of_pci_range *range, u8 *ib_reg_mask) -{ - void __iomem *cfg_base = port->cfg_base; - struct device *dev = port->dev; - void *bar_addr; - u32 pim_reg; - u64 cpu_addr = range->cpu_addr; - u64 pci_addr = range->pci_addr; - u64 size = range->size; - u64 mask = ~(size - 1) | EN_REG; - u32 flags = PCI_BASE_ADDRESS_MEM_TYPE_64; - u32 bar_low; - int region; - - region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size); - if (region < 0) { - dev_warn(dev, "invalid pcie dma-range config\n"); - return; - } - - if (range->flags & IORESOURCE_PREFETCH) - flags |= PCI_BASE_ADDRESS_MEM_PREFETCH; - - bar_low = pcie_bar_low_val((u32)cpu_addr, flags); - switch (region) { - case 0: - xgene_pcie_set_ib_mask(port, BRIDGE_CFG_4, flags, size); - bar_addr = cfg_base + PCI_BASE_ADDRESS_0; - writel(bar_low, bar_addr); - writel(upper_32_bits(cpu_addr), bar_addr + 0x4); - pim_reg = PIM1_1L; - break; - case 1: - xgene_pcie_writel(port, IBAR2, bar_low); - xgene_pcie_writel(port, IR2MSK, lower_32_bits(mask)); - pim_reg = PIM2_1L; - break; - case 2: - xgene_pcie_writel(port, IBAR3L, bar_low); - xgene_pcie_writel(port, IBAR3L + 0x4, upper_32_bits(cpu_addr)); - xgene_pcie_writel(port, IR3MSKL, lower_32_bits(mask)); - xgene_pcie_writel(port, IR3MSKL + 0x4, upper_32_bits(mask)); - pim_reg = PIM3_1L; - break; - } - - xgene_pcie_setup_pims(port, pim_reg, pci_addr, ~(size - 1)); -} - -static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port) -{ - struct device_node *np = port->node; - struct of_pci_range range; - struct of_pci_range_parser parser; - struct device *dev = port->dev; - u8 ib_reg_mask = 0; - - if (of_pci_dma_range_parser_init(&parser, np)) { - dev_err(dev, "missing dma-ranges property\n"); - return -EINVAL; - } - - /* Get the dma-ranges from DT */ - for_each_of_pci_range(&parser, &range) { - u64 end = range.cpu_addr + range.size - 1; - - dev_dbg(dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", - range.flags, range.cpu_addr, end, range.pci_addr); - xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask); - } - return 0; -} - -/* clear BAR configuration which was done by firmware */ -static void xgene_pcie_clear_config(struct xgene_pcie_port *port) -{ - int i; - - for (i = PIM1_1L; i <= CFGCTL; i += 4) - xgene_pcie_writel(port, i, 0); -} - -static int xgene_pcie_setup(struct xgene_pcie_port *port, struct list_head *res, - resource_size_t io_base) -{ - struct device *dev = port->dev; - u32 val, lanes = 0, speed = 0; - int ret; - - xgene_pcie_clear_config(port); - - /* setup the vendor and device IDs correctly */ - val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID; - xgene_pcie_writel(port, BRIDGE_CFG_0, val); - - ret = xgene_pcie_map_ranges(port, res, io_base); - if (ret) - return ret; - - ret = xgene_pcie_parse_map_dma_ranges(port); - if (ret) - return ret; - - xgene_pcie_linkup(port, &lanes, &speed); - if (!port->link_up) - dev_info(dev, "(rc) link down\n"); - else - dev_info(dev, "(rc) x%d gen-%d link up\n", lanes, speed + 1); - return 0; -} - -static struct pci_ops xgene_pcie_ops = { - .map_bus = xgene_pcie_map_bus, - .read = xgene_pcie_config_read32, - .write = pci_generic_config_write32, -}; - -static int xgene_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *dn = dev->of_node; - struct xgene_pcie_port *port; - resource_size_t iobase = 0; - struct pci_bus *bus, *child; - struct pci_host_bridge *bridge; - int ret; - LIST_HEAD(res); - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port)); - if (!bridge) - return -ENOMEM; - - port = pci_host_bridge_priv(bridge); - - port->node = of_node_get(dn); - port->dev = dev; - - port->version = XGENE_PCIE_IP_VER_UNKN; - if (of_device_is_compatible(port->node, "apm,xgene-pcie")) - port->version = XGENE_PCIE_IP_VER_1; - - ret = xgene_pcie_map_reg(port, pdev); - if (ret) - return ret; - - ret = xgene_pcie_init_port(port); - if (ret) - return ret; - - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, - &iobase); - if (ret) - return ret; - - ret = devm_request_pci_bus_resources(dev, &res); - if (ret) - goto error; - - ret = xgene_pcie_setup(port, &res, iobase); - if (ret) - goto error; - - list_splice_init(&res, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = port; - bridge->busnr = 0; - bridge->ops = &xgene_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) - goto error; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - return 0; - -error: - pci_free_resource_list(&res); - return ret; -} - -static const struct of_device_id xgene_pcie_match_table[] = { - {.compatible = "apm,xgene-pcie",}, - {}, -}; - -static struct platform_driver xgene_pcie_driver = { - .driver = { - .name = "xgene-pcie", - .of_match_table = of_match_ptr(xgene_pcie_match_table), - .suppress_bind_attrs = true, - }, - .probe = xgene_pcie_probe, -}; -builtin_platform_driver(xgene_pcie_driver); -#endif diff --git a/drivers/pci/host/pcie-altera-msi.c b/drivers/pci/host/pcie-altera-msi.c deleted file mode 100644 index 025ef7d9a046..000000000000 --- a/drivers/pci/host/pcie-altera-msi.c +++ /dev/null @@ -1,291 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Altera PCIe MSI support - * - * Author: Ley Foon Tan - * - * Copyright Altera Corporation (C) 2013-2015. All rights reserved - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MSI_STATUS 0x0 -#define MSI_ERROR 0x4 -#define MSI_INTMASK 0x8 - -#define MAX_MSI_VECTORS 32 - -struct altera_msi { - DECLARE_BITMAP(used, MAX_MSI_VECTORS); - struct mutex lock; /* protect "used" bitmap */ - struct platform_device *pdev; - struct irq_domain *msi_domain; - struct irq_domain *inner_domain; - void __iomem *csr_base; - void __iomem *vector_base; - phys_addr_t vector_phy; - u32 num_of_vectors; - int irq; -}; - -static inline void msi_writel(struct altera_msi *msi, const u32 value, - const u32 reg) -{ - writel_relaxed(value, msi->csr_base + reg); -} - -static inline u32 msi_readl(struct altera_msi *msi, const u32 reg) -{ - return readl_relaxed(msi->csr_base + reg); -} - -static void altera_msi_isr(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct altera_msi *msi; - unsigned long status; - u32 bit; - u32 virq; - - chained_irq_enter(chip, desc); - msi = irq_desc_get_handler_data(desc); - - while ((status = msi_readl(msi, MSI_STATUS)) != 0) { - for_each_set_bit(bit, &status, msi->num_of_vectors) { - /* Dummy read from vector to clear the interrupt */ - readl_relaxed(msi->vector_base + (bit * sizeof(u32))); - - virq = irq_find_mapping(msi->inner_domain, bit); - if (virq) - generic_handle_irq(virq); - else - dev_err(&msi->pdev->dev, "unexpected MSI\n"); - } - } - - chained_irq_exit(chip, desc); -} - -static struct irq_chip altera_msi_irq_chip = { - .name = "Altera PCIe MSI", - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; - -static struct msi_domain_info altera_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX), - .chip = &altera_msi_irq_chip, -}; - -static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct altera_msi *msi = irq_data_get_irq_chip_data(data); - phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32)); - - msg->address_lo = lower_32_bits(addr); - msg->address_hi = upper_32_bits(addr); - msg->data = data->hwirq; - - dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n", - (int)data->hwirq, msg->address_hi, msg->address_lo); -} - -static int altera_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) -{ - return -EINVAL; -} - -static struct irq_chip altera_msi_bottom_irq_chip = { - .name = "Altera MSI", - .irq_compose_msi_msg = altera_compose_msi_msg, - .irq_set_affinity = altera_msi_set_affinity, -}; - -static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *args) -{ - struct altera_msi *msi = domain->host_data; - unsigned long bit; - u32 mask; - - WARN_ON(nr_irqs != 1); - mutex_lock(&msi->lock); - - bit = find_first_zero_bit(msi->used, msi->num_of_vectors); - if (bit >= msi->num_of_vectors) { - mutex_unlock(&msi->lock); - return -ENOSPC; - } - - set_bit(bit, msi->used); - - mutex_unlock(&msi->lock); - - irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip, - domain->host_data, handle_simple_irq, - NULL, NULL); - - mask = msi_readl(msi, MSI_INTMASK); - mask |= 1 << bit; - msi_writel(msi, mask, MSI_INTMASK); - - return 0; -} - -static void altera_irq_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) -{ - struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct altera_msi *msi = irq_data_get_irq_chip_data(d); - u32 mask; - - mutex_lock(&msi->lock); - - if (!test_bit(d->hwirq, msi->used)) { - dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n", - d->hwirq); - } else { - __clear_bit(d->hwirq, msi->used); - mask = msi_readl(msi, MSI_INTMASK); - mask &= ~(1 << d->hwirq); - msi_writel(msi, mask, MSI_INTMASK); - } - - mutex_unlock(&msi->lock); -} - -static const struct irq_domain_ops msi_domain_ops = { - .alloc = altera_irq_domain_alloc, - .free = altera_irq_domain_free, -}; - -static int altera_allocate_domains(struct altera_msi *msi) -{ - struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node); - - msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors, - &msi_domain_ops, msi); - if (!msi->inner_domain) { - dev_err(&msi->pdev->dev, "failed to create IRQ domain\n"); - return -ENOMEM; - } - - msi->msi_domain = pci_msi_create_irq_domain(fwnode, - &altera_msi_domain_info, msi->inner_domain); - if (!msi->msi_domain) { - dev_err(&msi->pdev->dev, "failed to create MSI domain\n"); - irq_domain_remove(msi->inner_domain); - return -ENOMEM; - } - - return 0; -} - -static void altera_free_domains(struct altera_msi *msi) -{ - irq_domain_remove(msi->msi_domain); - irq_domain_remove(msi->inner_domain); -} - -static int altera_msi_remove(struct platform_device *pdev) -{ - struct altera_msi *msi = platform_get_drvdata(pdev); - - msi_writel(msi, 0, MSI_INTMASK); - irq_set_chained_handler(msi->irq, NULL); - irq_set_handler_data(msi->irq, NULL); - - altera_free_domains(msi); - - platform_set_drvdata(pdev, NULL); - return 0; -} - -static int altera_msi_probe(struct platform_device *pdev) -{ - struct altera_msi *msi; - struct device_node *np = pdev->dev.of_node; - struct resource *res; - int ret; - - msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi), - GFP_KERNEL); - if (!msi) - return -ENOMEM; - - mutex_init(&msi->lock); - msi->pdev = pdev; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); - msi->csr_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(msi->csr_base)) { - dev_err(&pdev->dev, "failed to map csr memory\n"); - return PTR_ERR(msi->csr_base); - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "vector_slave"); - msi->vector_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(msi->vector_base)) { - dev_err(&pdev->dev, "failed to map vector_slave memory\n"); - return PTR_ERR(msi->vector_base); - } - - msi->vector_phy = res->start; - - if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) { - dev_err(&pdev->dev, "failed to parse the number of vectors\n"); - return -EINVAL; - } - - ret = altera_allocate_domains(msi); - if (ret) - return ret; - - msi->irq = platform_get_irq(pdev, 0); - if (msi->irq < 0) { - dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq); - ret = msi->irq; - goto err; - } - - irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi); - platform_set_drvdata(pdev, msi); - - return 0; - -err: - altera_msi_remove(pdev); - return ret; -} - -static const struct of_device_id altera_msi_of_match[] = { - { .compatible = "altr,msi-1.0", NULL }, - { }, -}; - -static struct platform_driver altera_msi_driver = { - .driver = { - .name = "altera-msi", - .of_match_table = altera_msi_of_match, - }, - .probe = altera_msi_probe, - .remove = altera_msi_remove, -}; - -static int __init altera_msi_init(void) -{ - return platform_driver_register(&altera_msi_driver); -} -subsys_initcall(altera_msi_init); diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c deleted file mode 100644 index 7d05e51205b3..000000000000 --- a/drivers/pci/host/pcie-altera.c +++ /dev/null @@ -1,645 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright Altera Corporation (C) 2013-2015. All rights reserved - * - * Author: Ley Foon Tan - * Description: Altera PCIe host controller driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -#define RP_TX_REG0 0x2000 -#define RP_TX_REG1 0x2004 -#define RP_TX_CNTRL 0x2008 -#define RP_TX_EOP 0x2 -#define RP_TX_SOP 0x1 -#define RP_RXCPL_STATUS 0x2010 -#define RP_RXCPL_EOP 0x2 -#define RP_RXCPL_SOP 0x1 -#define RP_RXCPL_REG0 0x2014 -#define RP_RXCPL_REG1 0x2018 -#define P2A_INT_STATUS 0x3060 -#define P2A_INT_STS_ALL 0xf -#define P2A_INT_ENABLE 0x3070 -#define P2A_INT_ENA_ALL 0xf -#define RP_LTSSM 0x3c64 -#define RP_LTSSM_MASK 0x1f -#define LTSSM_L0 0xf - -#define PCIE_CAP_OFFSET 0x80 -/* TLP configuration type 0 and 1 */ -#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ -#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ -#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */ -#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */ -#define TLP_PAYLOAD_SIZE 0x01 -#define TLP_READ_TAG 0x1d -#define TLP_WRITE_TAG 0x10 -#define RP_DEVFN 0 -#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) -#define TLP_CFGRD_DW0(pcie, bus) \ - ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \ - : TLP_FMTTYPE_CFGRD1) << 24) | \ - TLP_PAYLOAD_SIZE) -#define TLP_CFGWR_DW0(pcie, bus) \ - ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGWR0 \ - : TLP_FMTTYPE_CFGWR1) << 24) | \ - TLP_PAYLOAD_SIZE) -#define TLP_CFG_DW1(pcie, tag, be) \ - (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) -#define TLP_CFG_DW2(bus, devfn, offset) \ - (((bus) << 24) | ((devfn) << 16) | (offset)) -#define TLP_COMP_STATUS(s) (((s) >> 13) & 7) -#define TLP_HDR_SIZE 3 -#define TLP_LOOP 500 - -#define LINK_UP_TIMEOUT HZ -#define LINK_RETRAIN_TIMEOUT HZ - -#define DWORD_MASK 3 - -struct altera_pcie { - struct platform_device *pdev; - void __iomem *cra_base; /* DT Cra */ - int irq; - u8 root_bus_nr; - struct irq_domain *irq_domain; - struct resource bus_range; - struct list_head resources; -}; - -struct tlp_rp_regpair_t { - u32 ctrl; - u32 reg0; - u32 reg1; -}; - -static inline void cra_writel(struct altera_pcie *pcie, const u32 value, - const u32 reg) -{ - writel_relaxed(value, pcie->cra_base + reg); -} - -static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg) -{ - return readl_relaxed(pcie->cra_base + reg); -} - -static bool altera_pcie_link_up(struct altera_pcie *pcie) -{ - return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); -} - -/* - * Altera PCIe port uses BAR0 of RC's configuration space as the translation - * from PCI bus to native BUS. Entire DDR region is mapped into PCIe space - * using these registers, so it can be reached by DMA from EP devices. - * This BAR0 will also access to MSI vector when receiving MSI/MSIX interrupt - * from EP devices, eventually trigger interrupt to GIC. The BAR0 of bridge - * should be hidden during enumeration to avoid the sizing and resource - * allocation by PCIe core. - */ -static bool altera_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn, - int offset) -{ - if (pci_is_root_bus(bus) && (devfn == 0) && - (offset == PCI_BASE_ADDRESS_0)) - return true; - - return false; -} - -static void tlp_write_tx(struct altera_pcie *pcie, - struct tlp_rp_regpair_t *tlp_rp_regdata) -{ - cra_writel(pcie, tlp_rp_regdata->reg0, RP_TX_REG0); - cra_writel(pcie, tlp_rp_regdata->reg1, RP_TX_REG1); - cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL); -} - -static bool altera_pcie_valid_device(struct altera_pcie *pcie, - struct pci_bus *bus, int dev) -{ - /* If there is no link, then there is no device */ - if (bus->number != pcie->root_bus_nr) { - if (!altera_pcie_link_up(pcie)) - return false; - } - - /* access only one slot on each root port */ - if (bus->number == pcie->root_bus_nr && dev > 0) - return false; - - return true; -} - -static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) -{ - int i; - bool sop = false; - u32 ctrl; - u32 reg0, reg1; - u32 comp_status = 1; - - /* - * Minimum 2 loops to read TLP headers and 1 loop to read data - * payload. - */ - for (i = 0; i < TLP_LOOP; i++) { - ctrl = cra_readl(pcie, RP_RXCPL_STATUS); - if ((ctrl & RP_RXCPL_SOP) || (ctrl & RP_RXCPL_EOP) || sop) { - reg0 = cra_readl(pcie, RP_RXCPL_REG0); - reg1 = cra_readl(pcie, RP_RXCPL_REG1); - - if (ctrl & RP_RXCPL_SOP) { - sop = true; - comp_status = TLP_COMP_STATUS(reg1); - } - - if (ctrl & RP_RXCPL_EOP) { - if (comp_status) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (value) - *value = reg0; - - return PCIBIOS_SUCCESSFUL; - } - } - udelay(5); - } - - return PCIBIOS_DEVICE_NOT_FOUND; -} - -static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers, - u32 data, bool align) -{ - struct tlp_rp_regpair_t tlp_rp_regdata; - - tlp_rp_regdata.reg0 = headers[0]; - tlp_rp_regdata.reg1 = headers[1]; - tlp_rp_regdata.ctrl = RP_TX_SOP; - tlp_write_tx(pcie, &tlp_rp_regdata); - - if (align) { - tlp_rp_regdata.reg0 = headers[2]; - tlp_rp_regdata.reg1 = 0; - tlp_rp_regdata.ctrl = 0; - tlp_write_tx(pcie, &tlp_rp_regdata); - - tlp_rp_regdata.reg0 = data; - tlp_rp_regdata.reg1 = 0; - } else { - tlp_rp_regdata.reg0 = headers[2]; - tlp_rp_regdata.reg1 = data; - } - - tlp_rp_regdata.ctrl = RP_TX_EOP; - tlp_write_tx(pcie, &tlp_rp_regdata); -} - -static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, - int where, u8 byte_en, u32 *value) -{ - u32 headers[TLP_HDR_SIZE]; - - headers[0] = TLP_CFGRD_DW0(pcie, bus); - headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); - headers[2] = TLP_CFG_DW2(bus, devfn, where); - - tlp_write_packet(pcie, headers, 0, false); - - return tlp_read_packet(pcie, value); -} - -static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, - int where, u8 byte_en, u32 value) -{ - u32 headers[TLP_HDR_SIZE]; - int ret; - - headers[0] = TLP_CFGWR_DW0(pcie, bus); - headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en); - headers[2] = TLP_CFG_DW2(bus, devfn, where); - - /* check alignment to Qword */ - if ((where & 0x7) == 0) - tlp_write_packet(pcie, headers, value, true); - else - tlp_write_packet(pcie, headers, value, false); - - ret = tlp_read_packet(pcie, NULL); - if (ret != PCIBIOS_SUCCESSFUL) - return ret; - - /* - * Monitor changes to PCI_PRIMARY_BUS register on root port - * and update local copy of root bus number accordingly. - */ - if ((bus == pcie->root_bus_nr) && (where == PCI_PRIMARY_BUS)) - pcie->root_bus_nr = (u8)(value); - - return PCIBIOS_SUCCESSFUL; -} - -static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno, - unsigned int devfn, int where, int size, - u32 *value) -{ - int ret; - u32 data; - u8 byte_en; - - switch (size) { - case 1: - byte_en = 1 << (where & 3); - break; - case 2: - byte_en = 3 << (where & 3); - break; - default: - byte_en = 0xf; - break; - } - - ret = tlp_cfg_dword_read(pcie, busno, devfn, - (where & ~DWORD_MASK), byte_en, &data); - if (ret != PCIBIOS_SUCCESSFUL) - return ret; - - switch (size) { - case 1: - *value = (data >> (8 * (where & 0x3))) & 0xff; - break; - case 2: - *value = (data >> (8 * (where & 0x2))) & 0xffff; - break; - default: - *value = data; - break; - } - - return PCIBIOS_SUCCESSFUL; -} - -static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno, - unsigned int devfn, int where, int size, - u32 value) -{ - u32 data32; - u32 shift = 8 * (where & 3); - u8 byte_en; - - switch (size) { - case 1: - data32 = (value & 0xff) << shift; - byte_en = 1 << (where & 3); - break; - case 2: - data32 = (value & 0xffff) << shift; - byte_en = 3 << (where & 3); - break; - default: - data32 = value; - byte_en = 0xf; - break; - } - - return tlp_cfg_dword_write(pcie, busno, devfn, (where & ~DWORD_MASK), - byte_en, data32); -} - -static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *value) -{ - struct altera_pcie *pcie = bus->sysdata; - - if (altera_pcie_hide_rc_bar(bus, devfn, where)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn))) { - *value = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - return _altera_pcie_cfg_read(pcie, bus->number, devfn, where, size, - value); -} - -static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 value) -{ - struct altera_pcie *pcie = bus->sysdata; - - if (altera_pcie_hide_rc_bar(bus, devfn, where)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn))) - return PCIBIOS_DEVICE_NOT_FOUND; - - return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size, - value); -} - -static struct pci_ops altera_pcie_ops = { - .read = altera_pcie_cfg_read, - .write = altera_pcie_cfg_write, -}; - -static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno, - unsigned int devfn, int offset, u16 *value) -{ - u32 data; - int ret; - - ret = _altera_pcie_cfg_read(pcie, busno, devfn, - PCIE_CAP_OFFSET + offset, sizeof(*value), - &data); - *value = data; - return ret; -} - -static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno, - unsigned int devfn, int offset, u16 value) -{ - return _altera_pcie_cfg_write(pcie, busno, devfn, - PCIE_CAP_OFFSET + offset, sizeof(value), - value); -} - -static void altera_wait_link_retrain(struct altera_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - u16 reg16; - unsigned long start_jiffies; - - /* Wait for link training end. */ - start_jiffies = jiffies; - for (;;) { - altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, - PCI_EXP_LNKSTA, ®16); - if (!(reg16 & PCI_EXP_LNKSTA_LT)) - break; - - if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) { - dev_err(dev, "link retrain timeout\n"); - break; - } - udelay(100); - } - - /* Wait for link is up */ - start_jiffies = jiffies; - for (;;) { - if (altera_pcie_link_up(pcie)) - break; - - if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { - dev_err(dev, "link up timeout\n"); - break; - } - udelay(100); - } -} - -static void altera_pcie_retrain(struct altera_pcie *pcie) -{ - u16 linkcap, linkstat, linkctl; - - if (!altera_pcie_link_up(pcie)) - return; - - /* - * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but - * current speed is 2.5 GB/s. - */ - altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKCAP, - &linkcap); - if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) - return; - - altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKSTA, - &linkstat); - if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { - altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, - PCI_EXP_LNKCTL, &linkctl); - linkctl |= PCI_EXP_LNKCTL_RL; - altera_write_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, - PCI_EXP_LNKCTL, linkctl); - - altera_wait_link_retrain(pcie); - } -} - -static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - return 0; -} - -static const struct irq_domain_ops intx_domain_ops = { - .map = altera_pcie_intx_map, - .xlate = pci_irqd_intx_xlate, -}; - -static void altera_pcie_isr(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct altera_pcie *pcie; - struct device *dev; - unsigned long status; - u32 bit; - u32 virq; - - chained_irq_enter(chip, desc); - pcie = irq_desc_get_handler_data(desc); - dev = &pcie->pdev->dev; - - while ((status = cra_readl(pcie, P2A_INT_STATUS) - & P2A_INT_STS_ALL) != 0) { - for_each_set_bit(bit, &status, PCI_NUM_INTX) { - /* clear interrupts */ - cra_writel(pcie, 1 << bit, P2A_INT_STATUS); - - virq = irq_find_mapping(pcie->irq_domain, bit); - if (virq) - generic_handle_irq(virq); - else - dev_err(dev, "unexpected IRQ, INT%d\n", bit); - } - } - - chained_irq_exit(chip, desc); -} - -static int altera_pcie_parse_request_of_pci_ranges(struct altera_pcie *pcie) -{ - int err, res_valid = 0; - struct device *dev = &pcie->pdev->dev; - struct resource_entry *win; - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &pcie->resources, NULL); - if (err) - return err; - - err = devm_request_pci_bus_resources(dev, &pcie->resources); - if (err) - goto out_release_res; - - resource_list_for_each_entry(win, &pcie->resources) { - struct resource *res = win->res; - - if (resource_type(res) == IORESOURCE_MEM) - res_valid |= !(res->flags & IORESOURCE_PREFETCH); - } - - if (res_valid) - return 0; - - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; - -out_release_res: - pci_free_resource_list(&pcie->resources); - return err; -} - -static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - struct device_node *node = dev->of_node; - - /* Setup INTx */ - pcie->irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX, - &intx_domain_ops, pcie); - if (!pcie->irq_domain) { - dev_err(dev, "Failed to get a INTx IRQ domain\n"); - return -ENOMEM; - } - - return 0; -} - -static int altera_pcie_parse_dt(struct altera_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - struct platform_device *pdev = pcie->pdev; - struct resource *cra; - - cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra"); - pcie->cra_base = devm_ioremap_resource(dev, cra); - if (IS_ERR(pcie->cra_base)) - return PTR_ERR(pcie->cra_base); - - /* setup IRQ */ - pcie->irq = platform_get_irq(pdev, 0); - if (pcie->irq < 0) { - dev_err(dev, "failed to get IRQ: %d\n", pcie->irq); - return pcie->irq; - } - - irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie); - return 0; -} - -static void altera_pcie_host_init(struct altera_pcie *pcie) -{ - altera_pcie_retrain(pcie); -} - -static int altera_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct altera_pcie *pcie; - struct pci_bus *bus; - struct pci_bus *child; - struct pci_host_bridge *bridge; - int ret; - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); - if (!bridge) - return -ENOMEM; - - pcie = pci_host_bridge_priv(bridge); - pcie->pdev = pdev; - - ret = altera_pcie_parse_dt(pcie); - if (ret) { - dev_err(dev, "Parsing DT failed\n"); - return ret; - } - - INIT_LIST_HEAD(&pcie->resources); - - ret = altera_pcie_parse_request_of_pci_ranges(pcie); - if (ret) { - dev_err(dev, "Failed add resources\n"); - return ret; - } - - ret = altera_pcie_init_irq_domain(pcie); - if (ret) { - dev_err(dev, "Failed creating IRQ Domain\n"); - return ret; - } - - /* clear all interrupts */ - cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS); - /* enable all interrupts */ - cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); - altera_pcie_host_init(pcie); - - list_splice_init(&pcie->resources, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = pcie; - bridge->busnr = pcie->root_bus_nr; - bridge->ops = &altera_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) - return ret; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - - /* Configure PCI Express setting. */ - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(bus); - return ret; -} - -static const struct of_device_id altera_pcie_of_match[] = { - { .compatible = "altr,pcie-root-port-1.0", }, - {}, -}; - -static struct platform_driver altera_pcie_driver = { - .probe = altera_pcie_probe, - .driver = { - .name = "altera-pcie", - .of_match_table = altera_pcie_of_match, - .suppress_bind_attrs = true, - }, -}; - -builtin_platform_driver(altera_pcie_driver); diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c deleted file mode 100644 index aa55b064f64d..000000000000 --- a/drivers/pci/host/pcie-iproc-bcma.c +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2015 Broadcom Corporation - * Copyright (C) 2015 Hauke Mehrtens - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-iproc.h" - - -/* NS: CLASS field is R/O, and set to wrong 0x200 value */ -static void bcma_pcie2_fixup_class(struct pci_dev *dev) -{ - dev->class = PCI_CLASS_BRIDGE_PCI << 8; -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8011, bcma_pcie2_fixup_class); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class); - -static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - struct iproc_pcie *pcie = dev->sysdata; - struct bcma_device *bdev = container_of(pcie->dev, struct bcma_device, dev); - - return bcma_core_irq(bdev, 5); -} - -static int iproc_pcie_bcma_probe(struct bcma_device *bdev) -{ - struct device *dev = &bdev->dev; - struct iproc_pcie *pcie; - LIST_HEAD(resources); - struct pci_host_bridge *bridge; - int ret; - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); - if (!bridge) - return -ENOMEM; - - pcie = pci_host_bridge_priv(bridge); - - pcie->dev = dev; - - pcie->type = IPROC_PCIE_PAXB_BCMA; - pcie->base = bdev->io_addr; - if (!pcie->base) { - dev_err(dev, "no controller registers\n"); - return -ENOMEM; - } - - pcie->base_addr = bdev->addr; - - pcie->mem.start = bdev->addr_s[0]; - pcie->mem.end = bdev->addr_s[0] + SZ_128M - 1; - pcie->mem.name = "PCIe MEM space"; - pcie->mem.flags = IORESOURCE_MEM; - pci_add_resource(&resources, &pcie->mem); - - pcie->map_irq = iproc_pcie_bcma_map_irq; - - ret = iproc_pcie_setup(pcie, &resources); - if (ret) { - dev_err(dev, "PCIe controller setup failed\n"); - pci_free_resource_list(&resources); - return ret; - } - - bcma_set_drvdata(bdev, pcie); - return 0; -} - -static void iproc_pcie_bcma_remove(struct bcma_device *bdev) -{ - struct iproc_pcie *pcie = bcma_get_drvdata(bdev); - - iproc_pcie_remove(pcie); -} - -static const struct bcma_device_id iproc_pcie_bcma_table[] = { - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_PCIEG2, BCMA_ANY_REV, BCMA_ANY_CLASS), - {}, -}; -MODULE_DEVICE_TABLE(bcma, iproc_pcie_bcma_table); - -static struct bcma_driver iproc_pcie_bcma_driver = { - .name = KBUILD_MODNAME, - .id_table = iproc_pcie_bcma_table, - .probe = iproc_pcie_bcma_probe, - .remove = iproc_pcie_bcma_remove, -}; - -static int __init iproc_pcie_bcma_init(void) -{ - return bcma_driver_register(&iproc_pcie_bcma_driver); -} -module_init(iproc_pcie_bcma_init); - -static void __exit iproc_pcie_bcma_exit(void) -{ - bcma_driver_unregister(&iproc_pcie_bcma_driver); -} -module_exit(iproc_pcie_bcma_exit); - -MODULE_AUTHOR("Hauke Mehrtens"); -MODULE_DESCRIPTION("Broadcom iProc PCIe BCMA driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-iproc-msi.c b/drivers/pci/host/pcie-iproc-msi.c deleted file mode 100644 index 9deb56989d72..000000000000 --- a/drivers/pci/host/pcie-iproc-msi.c +++ /dev/null @@ -1,671 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2015 Broadcom Corporation - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-iproc.h" - -#define IPROC_MSI_INTR_EN_SHIFT 11 -#define IPROC_MSI_INTR_EN BIT(IPROC_MSI_INTR_EN_SHIFT) -#define IPROC_MSI_INT_N_EVENT_SHIFT 1 -#define IPROC_MSI_INT_N_EVENT BIT(IPROC_MSI_INT_N_EVENT_SHIFT) -#define IPROC_MSI_EQ_EN_SHIFT 0 -#define IPROC_MSI_EQ_EN BIT(IPROC_MSI_EQ_EN_SHIFT) - -#define IPROC_MSI_EQ_MASK 0x3f - -/* Max number of GIC interrupts */ -#define NR_HW_IRQS 6 - -/* Number of entries in each event queue */ -#define EQ_LEN 64 - -/* Size of each event queue memory region */ -#define EQ_MEM_REGION_SIZE SZ_4K - -/* Size of each MSI address region */ -#define MSI_MEM_REGION_SIZE SZ_4K - -enum iproc_msi_reg { - IPROC_MSI_EQ_PAGE = 0, - IPROC_MSI_EQ_PAGE_UPPER, - IPROC_MSI_PAGE, - IPROC_MSI_PAGE_UPPER, - IPROC_MSI_CTRL, - IPROC_MSI_EQ_HEAD, - IPROC_MSI_EQ_TAIL, - IPROC_MSI_INTS_EN, - IPROC_MSI_REG_SIZE, -}; - -struct iproc_msi; - -/** - * iProc MSI group - * - * One MSI group is allocated per GIC interrupt, serviced by one iProc MSI - * event queue. - * - * @msi: pointer to iProc MSI data - * @gic_irq: GIC interrupt - * @eq: Event queue number - */ -struct iproc_msi_grp { - struct iproc_msi *msi; - int gic_irq; - unsigned int eq; -}; - -/** - * iProc event queue based MSI - * - * Only meant to be used on platforms without MSI support integrated into the - * GIC. - * - * @pcie: pointer to iProc PCIe data - * @reg_offsets: MSI register offsets - * @grps: MSI groups - * @nr_irqs: number of total interrupts connected to GIC - * @nr_cpus: number of toal CPUs - * @has_inten_reg: indicates the MSI interrupt enable register needs to be - * set explicitly (required for some legacy platforms) - * @bitmap: MSI vector bitmap - * @bitmap_lock: lock to protect access to the MSI bitmap - * @nr_msi_vecs: total number of MSI vectors - * @inner_domain: inner IRQ domain - * @msi_domain: MSI IRQ domain - * @nr_eq_region: required number of 4K aligned memory region for MSI event - * queues - * @nr_msi_region: required number of 4K aligned address region for MSI posted - * writes - * @eq_cpu: pointer to allocated memory region for MSI event queues - * @eq_dma: DMA address of MSI event queues - * @msi_addr: MSI address - */ -struct iproc_msi { - struct iproc_pcie *pcie; - const u16 (*reg_offsets)[IPROC_MSI_REG_SIZE]; - struct iproc_msi_grp *grps; - int nr_irqs; - int nr_cpus; - bool has_inten_reg; - unsigned long *bitmap; - struct mutex bitmap_lock; - unsigned int nr_msi_vecs; - struct irq_domain *inner_domain; - struct irq_domain *msi_domain; - unsigned int nr_eq_region; - unsigned int nr_msi_region; - void *eq_cpu; - dma_addr_t eq_dma; - phys_addr_t msi_addr; -}; - -static const u16 iproc_msi_reg_paxb[NR_HW_IRQS][IPROC_MSI_REG_SIZE] = { - { 0x200, 0x2c0, 0x204, 0x2c4, 0x210, 0x250, 0x254, 0x208 }, - { 0x200, 0x2c0, 0x204, 0x2c4, 0x214, 0x258, 0x25c, 0x208 }, - { 0x200, 0x2c0, 0x204, 0x2c4, 0x218, 0x260, 0x264, 0x208 }, - { 0x200, 0x2c0, 0x204, 0x2c4, 0x21c, 0x268, 0x26c, 0x208 }, - { 0x200, 0x2c0, 0x204, 0x2c4, 0x220, 0x270, 0x274, 0x208 }, - { 0x200, 0x2c0, 0x204, 0x2c4, 0x224, 0x278, 0x27c, 0x208 }, -}; - -static const u16 iproc_msi_reg_paxc[NR_HW_IRQS][IPROC_MSI_REG_SIZE] = { - { 0xc00, 0xc04, 0xc08, 0xc0c, 0xc40, 0xc50, 0xc60 }, - { 0xc10, 0xc14, 0xc18, 0xc1c, 0xc44, 0xc54, 0xc64 }, - { 0xc20, 0xc24, 0xc28, 0xc2c, 0xc48, 0xc58, 0xc68 }, - { 0xc30, 0xc34, 0xc38, 0xc3c, 0xc4c, 0xc5c, 0xc6c }, -}; - -static inline u32 iproc_msi_read_reg(struct iproc_msi *msi, - enum iproc_msi_reg reg, - unsigned int eq) -{ - struct iproc_pcie *pcie = msi->pcie; - - return readl_relaxed(pcie->base + msi->reg_offsets[eq][reg]); -} - -static inline void iproc_msi_write_reg(struct iproc_msi *msi, - enum iproc_msi_reg reg, - int eq, u32 val) -{ - struct iproc_pcie *pcie = msi->pcie; - - writel_relaxed(val, pcie->base + msi->reg_offsets[eq][reg]); -} - -static inline u32 hwirq_to_group(struct iproc_msi *msi, unsigned long hwirq) -{ - return (hwirq % msi->nr_irqs); -} - -static inline unsigned int iproc_msi_addr_offset(struct iproc_msi *msi, - unsigned long hwirq) -{ - if (msi->nr_msi_region > 1) - return hwirq_to_group(msi, hwirq) * MSI_MEM_REGION_SIZE; - else - return hwirq_to_group(msi, hwirq) * sizeof(u32); -} - -static inline unsigned int iproc_msi_eq_offset(struct iproc_msi *msi, u32 eq) -{ - if (msi->nr_eq_region > 1) - return eq * EQ_MEM_REGION_SIZE; - else - return eq * EQ_LEN * sizeof(u32); -} - -static struct irq_chip iproc_msi_irq_chip = { - .name = "iProc-MSI", -}; - -static struct msi_domain_info iproc_msi_domain_info = { - .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, - .chip = &iproc_msi_irq_chip, -}; - -/* - * In iProc PCIe core, each MSI group is serviced by a GIC interrupt and a - * dedicated event queue. Each MSI group can support up to 64 MSI vectors. - * - * The number of MSI groups varies between different iProc SoCs. The total - * number of CPU cores also varies. To support MSI IRQ affinity, we - * distribute GIC interrupts across all available CPUs. MSI vector is moved - * from one GIC interrupt to another to steer to the target CPU. - * - * Assuming: - * - the number of MSI groups is M - * - the number of CPU cores is N - * - M is always a multiple of N - * - * Total number of raw MSI vectors = M * 64 - * Total number of supported MSI vectors = (M * 64) / N - */ -static inline int hwirq_to_cpu(struct iproc_msi *msi, unsigned long hwirq) -{ - return (hwirq % msi->nr_cpus); -} - -static inline unsigned long hwirq_to_canonical_hwirq(struct iproc_msi *msi, - unsigned long hwirq) -{ - return (hwirq - hwirq_to_cpu(msi, hwirq)); -} - -static int iproc_msi_irq_set_affinity(struct irq_data *data, - const struct cpumask *mask, bool force) -{ - struct iproc_msi *msi = irq_data_get_irq_chip_data(data); - int target_cpu = cpumask_first(mask); - int curr_cpu; - - curr_cpu = hwirq_to_cpu(msi, data->hwirq); - if (curr_cpu == target_cpu) - return IRQ_SET_MASK_OK_DONE; - - /* steer MSI to the target CPU */ - data->hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq) + target_cpu; - - return IRQ_SET_MASK_OK; -} - -static void iproc_msi_irq_compose_msi_msg(struct irq_data *data, - struct msi_msg *msg) -{ - struct iproc_msi *msi = irq_data_get_irq_chip_data(data); - dma_addr_t addr; - - addr = msi->msi_addr + iproc_msi_addr_offset(msi, data->hwirq); - msg->address_lo = lower_32_bits(addr); - msg->address_hi = upper_32_bits(addr); - msg->data = data->hwirq << 5; -} - -static struct irq_chip iproc_msi_bottom_irq_chip = { - .name = "MSI", - .irq_set_affinity = iproc_msi_irq_set_affinity, - .irq_compose_msi_msg = iproc_msi_irq_compose_msi_msg, -}; - -static int iproc_msi_irq_domain_alloc(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs, - void *args) -{ - struct iproc_msi *msi = domain->host_data; - int hwirq, i; - - mutex_lock(&msi->bitmap_lock); - - /* Allocate 'nr_cpus' number of MSI vectors each time */ - hwirq = bitmap_find_next_zero_area(msi->bitmap, msi->nr_msi_vecs, 0, - msi->nr_cpus, 0); - if (hwirq < msi->nr_msi_vecs) { - bitmap_set(msi->bitmap, hwirq, msi->nr_cpus); - } else { - mutex_unlock(&msi->bitmap_lock); - return -ENOSPC; - } - - mutex_unlock(&msi->bitmap_lock); - - for (i = 0; i < nr_irqs; i++) { - irq_domain_set_info(domain, virq + i, hwirq + i, - &iproc_msi_bottom_irq_chip, - domain->host_data, handle_simple_irq, - NULL, NULL); - } - - return hwirq; -} - -static void iproc_msi_irq_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) -{ - struct irq_data *data = irq_domain_get_irq_data(domain, virq); - struct iproc_msi *msi = irq_data_get_irq_chip_data(data); - unsigned int hwirq; - - mutex_lock(&msi->bitmap_lock); - - hwirq = hwirq_to_canonical_hwirq(msi, data->hwirq); - bitmap_clear(msi->bitmap, hwirq, msi->nr_cpus); - - mutex_unlock(&msi->bitmap_lock); - - irq_domain_free_irqs_parent(domain, virq, nr_irqs); -} - -static const struct irq_domain_ops msi_domain_ops = { - .alloc = iproc_msi_irq_domain_alloc, - .free = iproc_msi_irq_domain_free, -}; - -static inline u32 decode_msi_hwirq(struct iproc_msi *msi, u32 eq, u32 head) -{ - u32 *msg, hwirq; - unsigned int offs; - - offs = iproc_msi_eq_offset(msi, eq) + head * sizeof(u32); - msg = (u32 *)(msi->eq_cpu + offs); - hwirq = readl(msg); - hwirq = (hwirq >> 5) + (hwirq & 0x1f); - - /* - * Since we have multiple hwirq mapped to a single MSI vector, - * now we need to derive the hwirq at CPU0. It can then be used to - * mapped back to virq. - */ - return hwirq_to_canonical_hwirq(msi, hwirq); -} - -static void iproc_msi_handler(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct iproc_msi_grp *grp; - struct iproc_msi *msi; - u32 eq, head, tail, nr_events; - unsigned long hwirq; - int virq; - - chained_irq_enter(chip, desc); - - grp = irq_desc_get_handler_data(desc); - msi = grp->msi; - eq = grp->eq; - - /* - * iProc MSI event queue is tracked by head and tail pointers. Head - * pointer indicates the next entry (MSI data) to be consumed by SW in - * the queue and needs to be updated by SW. iProc MSI core uses the - * tail pointer as the next data insertion point. - * - * Entries between head and tail pointers contain valid MSI data. MSI - * data is guaranteed to be in the event queue memory before the tail - * pointer is updated by the iProc MSI core. - */ - head = iproc_msi_read_reg(msi, IPROC_MSI_EQ_HEAD, - eq) & IPROC_MSI_EQ_MASK; - do { - tail = iproc_msi_read_reg(msi, IPROC_MSI_EQ_TAIL, - eq) & IPROC_MSI_EQ_MASK; - - /* - * Figure out total number of events (MSI data) to be - * processed. - */ - nr_events = (tail < head) ? - (EQ_LEN - (head - tail)) : (tail - head); - if (!nr_events) - break; - - /* process all outstanding events */ - while (nr_events--) { - hwirq = decode_msi_hwirq(msi, eq, head); - virq = irq_find_mapping(msi->inner_domain, hwirq); - generic_handle_irq(virq); - - head++; - head %= EQ_LEN; - } - - /* - * Now all outstanding events have been processed. Update the - * head pointer. - */ - iproc_msi_write_reg(msi, IPROC_MSI_EQ_HEAD, eq, head); - - /* - * Now go read the tail pointer again to see if there are new - * oustanding events that came in during the above window. - */ - } while (true); - - chained_irq_exit(chip, desc); -} - -static void iproc_msi_enable(struct iproc_msi *msi) -{ - int i, eq; - u32 val; - - /* Program memory region for each event queue */ - for (i = 0; i < msi->nr_eq_region; i++) { - dma_addr_t addr = msi->eq_dma + (i * EQ_MEM_REGION_SIZE); - - iproc_msi_write_reg(msi, IPROC_MSI_EQ_PAGE, i, - lower_32_bits(addr)); - iproc_msi_write_reg(msi, IPROC_MSI_EQ_PAGE_UPPER, i, - upper_32_bits(addr)); - } - - /* Program address region for MSI posted writes */ - for (i = 0; i < msi->nr_msi_region; i++) { - phys_addr_t addr = msi->msi_addr + (i * MSI_MEM_REGION_SIZE); - - iproc_msi_write_reg(msi, IPROC_MSI_PAGE, i, - lower_32_bits(addr)); - iproc_msi_write_reg(msi, IPROC_MSI_PAGE_UPPER, i, - upper_32_bits(addr)); - } - - for (eq = 0; eq < msi->nr_irqs; eq++) { - /* Enable MSI event queue */ - val = IPROC_MSI_INTR_EN | IPROC_MSI_INT_N_EVENT | - IPROC_MSI_EQ_EN; - iproc_msi_write_reg(msi, IPROC_MSI_CTRL, eq, val); - - /* - * Some legacy platforms require the MSI interrupt enable - * register to be set explicitly. - */ - if (msi->has_inten_reg) { - val = iproc_msi_read_reg(msi, IPROC_MSI_INTS_EN, eq); - val |= BIT(eq); - iproc_msi_write_reg(msi, IPROC_MSI_INTS_EN, eq, val); - } - } -} - -static void iproc_msi_disable(struct iproc_msi *msi) -{ - u32 eq, val; - - for (eq = 0; eq < msi->nr_irqs; eq++) { - if (msi->has_inten_reg) { - val = iproc_msi_read_reg(msi, IPROC_MSI_INTS_EN, eq); - val &= ~BIT(eq); - iproc_msi_write_reg(msi, IPROC_MSI_INTS_EN, eq, val); - } - - val = iproc_msi_read_reg(msi, IPROC_MSI_CTRL, eq); - val &= ~(IPROC_MSI_INTR_EN | IPROC_MSI_INT_N_EVENT | - IPROC_MSI_EQ_EN); - iproc_msi_write_reg(msi, IPROC_MSI_CTRL, eq, val); - } -} - -static int iproc_msi_alloc_domains(struct device_node *node, - struct iproc_msi *msi) -{ - msi->inner_domain = irq_domain_add_linear(NULL, msi->nr_msi_vecs, - &msi_domain_ops, msi); - if (!msi->inner_domain) - return -ENOMEM; - - msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), - &iproc_msi_domain_info, - msi->inner_domain); - if (!msi->msi_domain) { - irq_domain_remove(msi->inner_domain); - return -ENOMEM; - } - - return 0; -} - -static void iproc_msi_free_domains(struct iproc_msi *msi) -{ - if (msi->msi_domain) - irq_domain_remove(msi->msi_domain); - - if (msi->inner_domain) - irq_domain_remove(msi->inner_domain); -} - -static void iproc_msi_irq_free(struct iproc_msi *msi, unsigned int cpu) -{ - int i; - - for (i = cpu; i < msi->nr_irqs; i += msi->nr_cpus) { - irq_set_chained_handler_and_data(msi->grps[i].gic_irq, - NULL, NULL); - } -} - -static int iproc_msi_irq_setup(struct iproc_msi *msi, unsigned int cpu) -{ - int i, ret; - cpumask_var_t mask; - struct iproc_pcie *pcie = msi->pcie; - - for (i = cpu; i < msi->nr_irqs; i += msi->nr_cpus) { - irq_set_chained_handler_and_data(msi->grps[i].gic_irq, - iproc_msi_handler, - &msi->grps[i]); - /* Dedicate GIC interrupt to each CPU core */ - if (alloc_cpumask_var(&mask, GFP_KERNEL)) { - cpumask_clear(mask); - cpumask_set_cpu(cpu, mask); - ret = irq_set_affinity(msi->grps[i].gic_irq, mask); - if (ret) - dev_err(pcie->dev, - "failed to set affinity for IRQ%d\n", - msi->grps[i].gic_irq); - free_cpumask_var(mask); - } else { - dev_err(pcie->dev, "failed to alloc CPU mask\n"); - ret = -EINVAL; - } - - if (ret) { - /* Free all configured/unconfigured IRQs */ - iproc_msi_irq_free(msi, cpu); - return ret; - } - } - - return 0; -} - -int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node) -{ - struct iproc_msi *msi; - int i, ret; - unsigned int cpu; - - if (!of_device_is_compatible(node, "brcm,iproc-msi")) - return -ENODEV; - - if (!of_find_property(node, "msi-controller", NULL)) - return -ENODEV; - - if (pcie->msi) - return -EBUSY; - - msi = devm_kzalloc(pcie->dev, sizeof(*msi), GFP_KERNEL); - if (!msi) - return -ENOMEM; - - msi->pcie = pcie; - pcie->msi = msi; - msi->msi_addr = pcie->base_addr; - mutex_init(&msi->bitmap_lock); - msi->nr_cpus = num_possible_cpus(); - - msi->nr_irqs = of_irq_count(node); - if (!msi->nr_irqs) { - dev_err(pcie->dev, "found no MSI GIC interrupt\n"); - return -ENODEV; - } - - if (msi->nr_irqs > NR_HW_IRQS) { - dev_warn(pcie->dev, "too many MSI GIC interrupts defined %d\n", - msi->nr_irqs); - msi->nr_irqs = NR_HW_IRQS; - } - - if (msi->nr_irqs < msi->nr_cpus) { - dev_err(pcie->dev, - "not enough GIC interrupts for MSI affinity\n"); - return -EINVAL; - } - - if (msi->nr_irqs % msi->nr_cpus != 0) { - msi->nr_irqs -= msi->nr_irqs % msi->nr_cpus; - dev_warn(pcie->dev, "Reducing number of interrupts to %d\n", - msi->nr_irqs); - } - - switch (pcie->type) { - case IPROC_PCIE_PAXB_BCMA: - case IPROC_PCIE_PAXB: - msi->reg_offsets = iproc_msi_reg_paxb; - msi->nr_eq_region = 1; - msi->nr_msi_region = 1; - break; - case IPROC_PCIE_PAXC: - msi->reg_offsets = iproc_msi_reg_paxc; - msi->nr_eq_region = msi->nr_irqs; - msi->nr_msi_region = msi->nr_irqs; - break; - default: - dev_err(pcie->dev, "incompatible iProc PCIe interface\n"); - return -EINVAL; - } - - if (of_find_property(node, "brcm,pcie-msi-inten", NULL)) - msi->has_inten_reg = true; - - msi->nr_msi_vecs = msi->nr_irqs * EQ_LEN; - msi->bitmap = devm_kcalloc(pcie->dev, BITS_TO_LONGS(msi->nr_msi_vecs), - sizeof(*msi->bitmap), GFP_KERNEL); - if (!msi->bitmap) - return -ENOMEM; - - msi->grps = devm_kcalloc(pcie->dev, msi->nr_irqs, sizeof(*msi->grps), - GFP_KERNEL); - if (!msi->grps) - return -ENOMEM; - - for (i = 0; i < msi->nr_irqs; i++) { - unsigned int irq = irq_of_parse_and_map(node, i); - - if (!irq) { - dev_err(pcie->dev, "unable to parse/map interrupt\n"); - ret = -ENODEV; - goto free_irqs; - } - msi->grps[i].gic_irq = irq; - msi->grps[i].msi = msi; - msi->grps[i].eq = i; - } - - /* Reserve memory for event queue and make sure memories are zeroed */ - msi->eq_cpu = dma_zalloc_coherent(pcie->dev, - msi->nr_eq_region * EQ_MEM_REGION_SIZE, - &msi->eq_dma, GFP_KERNEL); - if (!msi->eq_cpu) { - ret = -ENOMEM; - goto free_irqs; - } - - ret = iproc_msi_alloc_domains(node, msi); - if (ret) { - dev_err(pcie->dev, "failed to create MSI domains\n"); - goto free_eq_dma; - } - - for_each_online_cpu(cpu) { - ret = iproc_msi_irq_setup(msi, cpu); - if (ret) - goto free_msi_irq; - } - - iproc_msi_enable(msi); - - return 0; - -free_msi_irq: - for_each_online_cpu(cpu) - iproc_msi_irq_free(msi, cpu); - iproc_msi_free_domains(msi); - -free_eq_dma: - dma_free_coherent(pcie->dev, msi->nr_eq_region * EQ_MEM_REGION_SIZE, - msi->eq_cpu, msi->eq_dma); - -free_irqs: - for (i = 0; i < msi->nr_irqs; i++) { - if (msi->grps[i].gic_irq) - irq_dispose_mapping(msi->grps[i].gic_irq); - } - pcie->msi = NULL; - return ret; -} -EXPORT_SYMBOL(iproc_msi_init); - -void iproc_msi_exit(struct iproc_pcie *pcie) -{ - struct iproc_msi *msi = pcie->msi; - unsigned int i, cpu; - - if (!msi) - return; - - iproc_msi_disable(msi); - - for_each_online_cpu(cpu) - iproc_msi_irq_free(msi, cpu); - - iproc_msi_free_domains(msi); - - dma_free_coherent(pcie->dev, msi->nr_eq_region * EQ_MEM_REGION_SIZE, - msi->eq_cpu, msi->eq_dma); - - for (i = 0; i < msi->nr_irqs; i++) { - if (msi->grps[i].gic_irq) - irq_dispose_mapping(msi->grps[i].gic_irq); - } -} -EXPORT_SYMBOL(iproc_msi_exit); diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c deleted file mode 100644 index f30f5f3fb5c1..000000000000 --- a/drivers/pci/host/pcie-iproc-platform.c +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2015 Broadcom Corporation - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" -#include "pcie-iproc.h" - -static const struct of_device_id iproc_pcie_of_match_table[] = { - { - .compatible = "brcm,iproc-pcie", - .data = (int *)IPROC_PCIE_PAXB, - }, { - .compatible = "brcm,iproc-pcie-paxb-v2", - .data = (int *)IPROC_PCIE_PAXB_V2, - }, { - .compatible = "brcm,iproc-pcie-paxc", - .data = (int *)IPROC_PCIE_PAXC, - }, { - .compatible = "brcm,iproc-pcie-paxc-v2", - .data = (int *)IPROC_PCIE_PAXC_V2, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table); - -static int iproc_pcie_pltfm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct iproc_pcie *pcie; - struct device_node *np = dev->of_node; - struct resource reg; - resource_size_t iobase = 0; - LIST_HEAD(resources); - struct pci_host_bridge *bridge; - int ret; - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); - if (!bridge) - return -ENOMEM; - - pcie = pci_host_bridge_priv(bridge); - - pcie->dev = dev; - pcie->type = (enum iproc_pcie_type) of_device_get_match_data(dev); - - ret = of_address_to_resource(np, 0, ®); - if (ret < 0) { - dev_err(dev, "unable to obtain controller resources\n"); - return ret; - } - - pcie->base = devm_pci_remap_cfgspace(dev, reg.start, - resource_size(®)); - if (!pcie->base) { - dev_err(dev, "unable to map controller registers\n"); - return -ENOMEM; - } - pcie->base_addr = reg.start; - - if (of_property_read_bool(np, "brcm,pcie-ob")) { - u32 val; - - ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset", - &val); - if (ret) { - dev_err(dev, - "missing brcm,pcie-ob-axi-offset property\n"); - return ret; - } - pcie->ob.axi_offset = val; - pcie->need_ob_cfg = true; - } - - /* - * DT nodes are not used by all platforms that use the iProc PCIe - * core driver. For platforms that require explict inbound mapping - * configuration, "dma-ranges" would have been present in DT - */ - pcie->need_ib_cfg = of_property_read_bool(np, "dma-ranges"); - - /* PHY use is optional */ - pcie->phy = devm_phy_get(dev, "pcie-phy"); - if (IS_ERR(pcie->phy)) { - if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) - return -EPROBE_DEFER; - pcie->phy = NULL; - } - - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &resources, - &iobase); - if (ret) { - dev_err(dev, "unable to get PCI host bridge resources\n"); - return ret; - } - - /* PAXC doesn't support legacy IRQs, skip mapping */ - switch (pcie->type) { - case IPROC_PCIE_PAXC: - case IPROC_PCIE_PAXC_V2: - break; - default: - pcie->map_irq = of_irq_parse_and_map_pci; - } - - ret = iproc_pcie_setup(pcie, &resources); - if (ret) { - dev_err(dev, "PCIe controller setup failed\n"); - pci_free_resource_list(&resources); - return ret; - } - - platform_set_drvdata(pdev, pcie); - return 0; -} - -static int iproc_pcie_pltfm_remove(struct platform_device *pdev) -{ - struct iproc_pcie *pcie = platform_get_drvdata(pdev); - - return iproc_pcie_remove(pcie); -} - -static void iproc_pcie_pltfm_shutdown(struct platform_device *pdev) -{ - struct iproc_pcie *pcie = platform_get_drvdata(pdev); - - iproc_pcie_shutdown(pcie); -} - -static struct platform_driver iproc_pcie_pltfm_driver = { - .driver = { - .name = "iproc-pcie", - .of_match_table = of_match_ptr(iproc_pcie_of_match_table), - }, - .probe = iproc_pcie_pltfm_probe, - .remove = iproc_pcie_pltfm_remove, - .shutdown = iproc_pcie_pltfm_shutdown, -}; -module_platform_driver(iproc_pcie_pltfm_driver); - -MODULE_AUTHOR("Ray Jui "); -MODULE_DESCRIPTION("Broadcom iPROC PCIe platform driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c deleted file mode 100644 index 3c76c5fa4f32..000000000000 --- a/drivers/pci/host/pcie-iproc.c +++ /dev/null @@ -1,1432 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2014 Hauke Mehrtens - * Copyright (C) 2015 Broadcom Corporation - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-iproc.h" - -#define EP_PERST_SOURCE_SELECT_SHIFT 2 -#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT) -#define EP_MODE_SURVIVE_PERST_SHIFT 1 -#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT) -#define RC_PCIE_RST_OUTPUT_SHIFT 0 -#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT) -#define PAXC_RESET_MASK 0x7f - -#define GIC_V3_CFG_SHIFT 0 -#define GIC_V3_CFG BIT(GIC_V3_CFG_SHIFT) - -#define MSI_ENABLE_CFG_SHIFT 0 -#define MSI_ENABLE_CFG BIT(MSI_ENABLE_CFG_SHIFT) - -#define CFG_IND_ADDR_MASK 0x00001ffc - -#define CFG_ADDR_BUS_NUM_SHIFT 20 -#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000 -#define CFG_ADDR_DEV_NUM_SHIFT 15 -#define CFG_ADDR_DEV_NUM_MASK 0x000f8000 -#define CFG_ADDR_FUNC_NUM_SHIFT 12 -#define CFG_ADDR_FUNC_NUM_MASK 0x00007000 -#define CFG_ADDR_REG_NUM_SHIFT 2 -#define CFG_ADDR_REG_NUM_MASK 0x00000ffc -#define CFG_ADDR_CFG_TYPE_SHIFT 0 -#define CFG_ADDR_CFG_TYPE_MASK 0x00000003 - -#define SYS_RC_INTX_MASK 0xf - -#define PCIE_PHYLINKUP_SHIFT 3 -#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT) -#define PCIE_DL_ACTIVE_SHIFT 2 -#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT) - -#define APB_ERR_EN_SHIFT 0 -#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT) - -#define CFG_RETRY_STATUS 0xffff0001 -#define CFG_RETRY_STATUS_TIMEOUT_US 500000 /* 500 milliseconds */ - -/* derive the enum index of the outbound/inbound mapping registers */ -#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2) - -/* - * Maximum number of outbound mapping window sizes that can be supported by any - * OARR/OMAP mapping pair - */ -#define MAX_NUM_OB_WINDOW_SIZES 4 - -#define OARR_VALID_SHIFT 0 -#define OARR_VALID BIT(OARR_VALID_SHIFT) -#define OARR_SIZE_CFG_SHIFT 1 - -/* - * Maximum number of inbound mapping region sizes that can be supported by an - * IARR - */ -#define MAX_NUM_IB_REGION_SIZES 9 - -#define IMAP_VALID_SHIFT 0 -#define IMAP_VALID BIT(IMAP_VALID_SHIFT) - -#define IPROC_PCI_EXP_CAP 0xac - -#define IPROC_PCIE_REG_INVALID 0xffff - -/** - * iProc PCIe outbound mapping controller specific parameters - * - * @window_sizes: list of supported outbound mapping window sizes in MB - * @nr_sizes: number of supported outbound mapping window sizes - */ -struct iproc_pcie_ob_map { - resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES]; - unsigned int nr_sizes; -}; - -static const struct iproc_pcie_ob_map paxb_ob_map[] = { - { - /* OARR0/OMAP0 */ - .window_sizes = { 128, 256 }, - .nr_sizes = 2, - }, - { - /* OARR1/OMAP1 */ - .window_sizes = { 128, 256 }, - .nr_sizes = 2, - }, -}; - -static const struct iproc_pcie_ob_map paxb_v2_ob_map[] = { - { - /* OARR0/OMAP0 */ - .window_sizes = { 128, 256 }, - .nr_sizes = 2, - }, - { - /* OARR1/OMAP1 */ - .window_sizes = { 128, 256 }, - .nr_sizes = 2, - }, - { - /* OARR2/OMAP2 */ - .window_sizes = { 128, 256, 512, 1024 }, - .nr_sizes = 4, - }, - { - /* OARR3/OMAP3 */ - .window_sizes = { 128, 256, 512, 1024 }, - .nr_sizes = 4, - }, -}; - -/** - * iProc PCIe inbound mapping type - */ -enum iproc_pcie_ib_map_type { - /* for DDR memory */ - IPROC_PCIE_IB_MAP_MEM = 0, - - /* for device I/O memory */ - IPROC_PCIE_IB_MAP_IO, - - /* invalid or unused */ - IPROC_PCIE_IB_MAP_INVALID -}; - -/** - * iProc PCIe inbound mapping controller specific parameters - * - * @type: inbound mapping region type - * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or - * SZ_1G - * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or - * GB, depedning on the size unit - * @nr_sizes: number of supported inbound mapping region sizes - * @nr_windows: number of supported inbound mapping windows for the region - * @imap_addr_offset: register offset between the upper and lower 32-bit - * IMAP address registers - * @imap_window_offset: register offset between each IMAP window - */ -struct iproc_pcie_ib_map { - enum iproc_pcie_ib_map_type type; - unsigned int size_unit; - resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES]; - unsigned int nr_sizes; - unsigned int nr_windows; - u16 imap_addr_offset; - u16 imap_window_offset; -}; - -static const struct iproc_pcie_ib_map paxb_v2_ib_map[] = { - { - /* IARR0/IMAP0 */ - .type = IPROC_PCIE_IB_MAP_IO, - .size_unit = SZ_1K, - .region_sizes = { 32 }, - .nr_sizes = 1, - .nr_windows = 8, - .imap_addr_offset = 0x40, - .imap_window_offset = 0x4, - }, - { - /* IARR1/IMAP1 (currently unused) */ - .type = IPROC_PCIE_IB_MAP_INVALID, - }, - { - /* IARR2/IMAP2 */ - .type = IPROC_PCIE_IB_MAP_MEM, - .size_unit = SZ_1M, - .region_sizes = { 64, 128, 256, 512, 1024, 2048, 4096, 8192, - 16384 }, - .nr_sizes = 9, - .nr_windows = 1, - .imap_addr_offset = 0x4, - .imap_window_offset = 0x8, - }, - { - /* IARR3/IMAP3 */ - .type = IPROC_PCIE_IB_MAP_MEM, - .size_unit = SZ_1G, - .region_sizes = { 1, 2, 4, 8, 16, 32 }, - .nr_sizes = 6, - .nr_windows = 8, - .imap_addr_offset = 0x4, - .imap_window_offset = 0x8, - }, - { - /* IARR4/IMAP4 */ - .type = IPROC_PCIE_IB_MAP_MEM, - .size_unit = SZ_1G, - .region_sizes = { 32, 64, 128, 256, 512 }, - .nr_sizes = 5, - .nr_windows = 8, - .imap_addr_offset = 0x4, - .imap_window_offset = 0x8, - }, -}; - -/* - * iProc PCIe host registers - */ -enum iproc_pcie_reg { - /* clock/reset signal control */ - IPROC_PCIE_CLK_CTRL = 0, - - /* - * To allow MSI to be steered to an external MSI controller (e.g., ARM - * GICv3 ITS) - */ - IPROC_PCIE_MSI_GIC_MODE, - - /* - * IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the - * window where the MSI posted writes are written, for the writes to be - * interpreted as MSI writes. - */ - IPROC_PCIE_MSI_BASE_ADDR, - IPROC_PCIE_MSI_WINDOW_SIZE, - - /* - * To hold the address of the register where the MSI writes are - * programed. When ARM GICv3 ITS is used, this should be programmed - * with the address of the GITS_TRANSLATER register. - */ - IPROC_PCIE_MSI_ADDR_LO, - IPROC_PCIE_MSI_ADDR_HI, - - /* enable MSI */ - IPROC_PCIE_MSI_EN_CFG, - - /* allow access to root complex configuration space */ - IPROC_PCIE_CFG_IND_ADDR, - IPROC_PCIE_CFG_IND_DATA, - - /* allow access to device configuration space */ - IPROC_PCIE_CFG_ADDR, - IPROC_PCIE_CFG_DATA, - - /* enable INTx */ - IPROC_PCIE_INTX_EN, - - /* outbound address mapping */ - IPROC_PCIE_OARR0, - IPROC_PCIE_OMAP0, - IPROC_PCIE_OARR1, - IPROC_PCIE_OMAP1, - IPROC_PCIE_OARR2, - IPROC_PCIE_OMAP2, - IPROC_PCIE_OARR3, - IPROC_PCIE_OMAP3, - - /* inbound address mapping */ - IPROC_PCIE_IARR0, - IPROC_PCIE_IMAP0, - IPROC_PCIE_IARR1, - IPROC_PCIE_IMAP1, - IPROC_PCIE_IARR2, - IPROC_PCIE_IMAP2, - IPROC_PCIE_IARR3, - IPROC_PCIE_IMAP3, - IPROC_PCIE_IARR4, - IPROC_PCIE_IMAP4, - - /* link status */ - IPROC_PCIE_LINK_STATUS, - - /* enable APB error for unsupported requests */ - IPROC_PCIE_APB_ERR_EN, - - /* total number of core registers */ - IPROC_PCIE_MAX_NUM_REG, -}; - -/* iProc PCIe PAXB BCMA registers */ -static const u16 iproc_pcie_reg_paxb_bcma[] = { - [IPROC_PCIE_CLK_CTRL] = 0x000, - [IPROC_PCIE_CFG_IND_ADDR] = 0x120, - [IPROC_PCIE_CFG_IND_DATA] = 0x124, - [IPROC_PCIE_CFG_ADDR] = 0x1f8, - [IPROC_PCIE_CFG_DATA] = 0x1fc, - [IPROC_PCIE_INTX_EN] = 0x330, - [IPROC_PCIE_LINK_STATUS] = 0xf0c, -}; - -/* iProc PCIe PAXB registers */ -static const u16 iproc_pcie_reg_paxb[] = { - [IPROC_PCIE_CLK_CTRL] = 0x000, - [IPROC_PCIE_CFG_IND_ADDR] = 0x120, - [IPROC_PCIE_CFG_IND_DATA] = 0x124, - [IPROC_PCIE_CFG_ADDR] = 0x1f8, - [IPROC_PCIE_CFG_DATA] = 0x1fc, - [IPROC_PCIE_INTX_EN] = 0x330, - [IPROC_PCIE_OARR0] = 0xd20, - [IPROC_PCIE_OMAP0] = 0xd40, - [IPROC_PCIE_OARR1] = 0xd28, - [IPROC_PCIE_OMAP1] = 0xd48, - [IPROC_PCIE_LINK_STATUS] = 0xf0c, - [IPROC_PCIE_APB_ERR_EN] = 0xf40, -}; - -/* iProc PCIe PAXB v2 registers */ -static const u16 iproc_pcie_reg_paxb_v2[] = { - [IPROC_PCIE_CLK_CTRL] = 0x000, - [IPROC_PCIE_CFG_IND_ADDR] = 0x120, - [IPROC_PCIE_CFG_IND_DATA] = 0x124, - [IPROC_PCIE_CFG_ADDR] = 0x1f8, - [IPROC_PCIE_CFG_DATA] = 0x1fc, - [IPROC_PCIE_INTX_EN] = 0x330, - [IPROC_PCIE_OARR0] = 0xd20, - [IPROC_PCIE_OMAP0] = 0xd40, - [IPROC_PCIE_OARR1] = 0xd28, - [IPROC_PCIE_OMAP1] = 0xd48, - [IPROC_PCIE_OARR2] = 0xd60, - [IPROC_PCIE_OMAP2] = 0xd68, - [IPROC_PCIE_OARR3] = 0xdf0, - [IPROC_PCIE_OMAP3] = 0xdf8, - [IPROC_PCIE_IARR0] = 0xd00, - [IPROC_PCIE_IMAP0] = 0xc00, - [IPROC_PCIE_IARR2] = 0xd10, - [IPROC_PCIE_IMAP2] = 0xcc0, - [IPROC_PCIE_IARR3] = 0xe00, - [IPROC_PCIE_IMAP3] = 0xe08, - [IPROC_PCIE_IARR4] = 0xe68, - [IPROC_PCIE_IMAP4] = 0xe70, - [IPROC_PCIE_LINK_STATUS] = 0xf0c, - [IPROC_PCIE_APB_ERR_EN] = 0xf40, -}; - -/* iProc PCIe PAXC v1 registers */ -static const u16 iproc_pcie_reg_paxc[] = { - [IPROC_PCIE_CLK_CTRL] = 0x000, - [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, - [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, - [IPROC_PCIE_CFG_ADDR] = 0x1f8, - [IPROC_PCIE_CFG_DATA] = 0x1fc, -}; - -/* iProc PCIe PAXC v2 registers */ -static const u16 iproc_pcie_reg_paxc_v2[] = { - [IPROC_PCIE_MSI_GIC_MODE] = 0x050, - [IPROC_PCIE_MSI_BASE_ADDR] = 0x074, - [IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078, - [IPROC_PCIE_MSI_ADDR_LO] = 0x07c, - [IPROC_PCIE_MSI_ADDR_HI] = 0x080, - [IPROC_PCIE_MSI_EN_CFG] = 0x09c, - [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, - [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, - [IPROC_PCIE_CFG_ADDR] = 0x1f8, - [IPROC_PCIE_CFG_DATA] = 0x1fc, -}; - -static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) -{ - struct iproc_pcie *pcie = bus->sysdata; - return pcie; -} - -static inline bool iproc_pcie_reg_is_invalid(u16 reg_offset) -{ - return !!(reg_offset == IPROC_PCIE_REG_INVALID); -} - -static inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie, - enum iproc_pcie_reg reg) -{ - return pcie->reg_offsets[reg]; -} - -static inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie, - enum iproc_pcie_reg reg) -{ - u16 offset = iproc_pcie_reg_offset(pcie, reg); - - if (iproc_pcie_reg_is_invalid(offset)) - return 0; - - return readl(pcie->base + offset); -} - -static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie, - enum iproc_pcie_reg reg, u32 val) -{ - u16 offset = iproc_pcie_reg_offset(pcie, reg); - - if (iproc_pcie_reg_is_invalid(offset)) - return; - - writel(val, pcie->base + offset); -} - -/** - * APB error forwarding can be disabled during access of configuration - * registers of the endpoint device, to prevent unsupported requests - * (typically seen during enumeration with multi-function devices) from - * triggering a system exception. - */ -static inline void iproc_pcie_apb_err_disable(struct pci_bus *bus, - bool disable) -{ - struct iproc_pcie *pcie = iproc_data(bus); - u32 val; - - if (bus->number && pcie->has_apb_err_disable) { - val = iproc_pcie_read_reg(pcie, IPROC_PCIE_APB_ERR_EN); - if (disable) - val &= ~APB_ERR_EN; - else - val |= APB_ERR_EN; - iproc_pcie_write_reg(pcie, IPROC_PCIE_APB_ERR_EN, val); - } -} - -static void __iomem *iproc_pcie_map_ep_cfg_reg(struct iproc_pcie *pcie, - unsigned int busno, - unsigned int slot, - unsigned int fn, - int where) -{ - u16 offset; - u32 val; - - /* EP device access */ - val = (busno << CFG_ADDR_BUS_NUM_SHIFT) | - (slot << CFG_ADDR_DEV_NUM_SHIFT) | - (fn << CFG_ADDR_FUNC_NUM_SHIFT) | - (where & CFG_ADDR_REG_NUM_MASK) | - (1 & CFG_ADDR_CFG_TYPE_MASK); - - iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val); - offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA); - - if (iproc_pcie_reg_is_invalid(offset)) - return NULL; - - return (pcie->base + offset); -} - -static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p) -{ - int timeout = CFG_RETRY_STATUS_TIMEOUT_US; - unsigned int data; - - /* - * As per PCIe spec r3.1, sec 2.3.2, CRS Software Visibility only - * affects config reads of the Vendor ID. For config writes or any - * other config reads, the Root may automatically reissue the - * configuration request again as a new request. - * - * For config reads, this hardware returns CFG_RETRY_STATUS data - * when it receives a CRS completion, regardless of the address of - * the read or the CRS Software Visibility Enable bit. As a - * partial workaround for this, we retry in software any read that - * returns CFG_RETRY_STATUS. - * - * Note that a non-Vendor ID config register may have a value of - * CFG_RETRY_STATUS. If we read that, we can't distinguish it from - * a CRS completion, so we will incorrectly retry the read and - * eventually return the wrong data (0xffffffff). - */ - data = readl(cfg_data_p); - while (data == CFG_RETRY_STATUS && timeout--) { - udelay(1); - data = readl(cfg_data_p); - } - - if (data == CFG_RETRY_STATUS) - data = 0xffffffff; - - return data; -} - -static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct iproc_pcie *pcie = iproc_data(bus); - unsigned int slot = PCI_SLOT(devfn); - unsigned int fn = PCI_FUNC(devfn); - unsigned int busno = bus->number; - void __iomem *cfg_data_p; - unsigned int data; - int ret; - - /* root complex access */ - if (busno == 0) { - ret = pci_generic_config_read32(bus, devfn, where, size, val); - if (ret != PCIBIOS_SUCCESSFUL) - return ret; - - /* Don't advertise CRS SV support */ - if ((where & ~0x3) == IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL) - *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); - return PCIBIOS_SUCCESSFUL; - } - - cfg_data_p = iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where); - - if (!cfg_data_p) - return PCIBIOS_DEVICE_NOT_FOUND; - - data = iproc_pcie_cfg_retry(cfg_data_p); - - *val = data; - if (size <= 2) - *val = (data >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); - - return PCIBIOS_SUCCESSFUL; -} - -/** - * Note access to the configuration registers are protected at the higher layer - * by 'pci_lock' in drivers/pci/access.c - */ -static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie, - int busno, unsigned int devfn, - int where) -{ - unsigned slot = PCI_SLOT(devfn); - unsigned fn = PCI_FUNC(devfn); - u16 offset; - - /* root complex access */ - if (busno == 0) { - if (slot > 0 || fn > 0) - return NULL; - - iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR, - where & CFG_IND_ADDR_MASK); - offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA); - if (iproc_pcie_reg_is_invalid(offset)) - return NULL; - else - return (pcie->base + offset); - } - - /* - * PAXC is connected to an internally emulated EP within the SoC. It - * allows only one device. - */ - if (pcie->ep_is_internal) - if (slot > 0) - return NULL; - - return iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where); -} - -static void __iomem *iproc_pcie_bus_map_cfg_bus(struct pci_bus *bus, - unsigned int devfn, - int where) -{ - return iproc_pcie_map_cfg_bus(iproc_data(bus), bus->number, devfn, - where); -} - -static int iproc_pci_raw_config_read32(struct iproc_pcie *pcie, - unsigned int devfn, int where, - int size, u32 *val) -{ - void __iomem *addr; - - addr = iproc_pcie_map_cfg_bus(pcie, 0, devfn, where & ~0x3); - if (!addr) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - *val = readl(addr); - - if (size <= 2) - *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); - - return PCIBIOS_SUCCESSFUL; -} - -static int iproc_pci_raw_config_write32(struct iproc_pcie *pcie, - unsigned int devfn, int where, - int size, u32 val) -{ - void __iomem *addr; - u32 mask, tmp; - - addr = iproc_pcie_map_cfg_bus(pcie, 0, devfn, where & ~0x3); - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (size == 4) { - writel(val, addr); - return PCIBIOS_SUCCESSFUL; - } - - mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); - tmp = readl(addr) & mask; - tmp |= val << ((where & 0x3) * 8); - writel(tmp, addr); - - return PCIBIOS_SUCCESSFUL; -} - -static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - int ret; - struct iproc_pcie *pcie = iproc_data(bus); - - iproc_pcie_apb_err_disable(bus, true); - if (pcie->type == IPROC_PCIE_PAXB_V2) - ret = iproc_pcie_config_read(bus, devfn, where, size, val); - else - ret = pci_generic_config_read32(bus, devfn, where, size, val); - iproc_pcie_apb_err_disable(bus, false); - - return ret; -} - -static int iproc_pcie_config_write32(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - int ret; - - iproc_pcie_apb_err_disable(bus, true); - ret = pci_generic_config_write32(bus, devfn, where, size, val); - iproc_pcie_apb_err_disable(bus, false); - - return ret; -} - -static struct pci_ops iproc_pcie_ops = { - .map_bus = iproc_pcie_bus_map_cfg_bus, - .read = iproc_pcie_config_read32, - .write = iproc_pcie_config_write32, -}; - -static void iproc_pcie_perst_ctrl(struct iproc_pcie *pcie, bool assert) -{ - u32 val; - - /* - * PAXC and the internal emulated endpoint device downstream should not - * be reset. If firmware has been loaded on the endpoint device at an - * earlier boot stage, reset here causes issues. - */ - if (pcie->ep_is_internal) - return; - - if (assert) { - val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); - val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST & - ~RC_PCIE_RST_OUTPUT; - iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); - udelay(250); - } else { - val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); - val |= RC_PCIE_RST_OUTPUT; - iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); - msleep(100); - } -} - -int iproc_pcie_shutdown(struct iproc_pcie *pcie) -{ - iproc_pcie_perst_ctrl(pcie, true); - msleep(500); - - return 0; -} -EXPORT_SYMBOL_GPL(iproc_pcie_shutdown); - -static int iproc_pcie_check_link(struct iproc_pcie *pcie) -{ - struct device *dev = pcie->dev; - u32 hdr_type, link_ctrl, link_status, class, val; - bool link_is_active = false; - - /* - * PAXC connects to emulated endpoint devices directly and does not - * have a Serdes. Therefore skip the link detection logic here. - */ - if (pcie->ep_is_internal) - return 0; - - val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS); - if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) { - dev_err(dev, "PHY or data link is INACTIVE!\n"); - return -ENODEV; - } - - /* make sure we are not in EP mode */ - iproc_pci_raw_config_read32(pcie, 0, PCI_HEADER_TYPE, 1, &hdr_type); - if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) { - dev_err(dev, "in EP mode, hdr=%#02x\n", hdr_type); - return -EFAULT; - } - - /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */ -#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c -#define PCI_CLASS_BRIDGE_MASK 0xffff00 -#define PCI_CLASS_BRIDGE_SHIFT 8 - iproc_pci_raw_config_read32(pcie, 0, PCI_BRIDGE_CTRL_REG_OFFSET, - 4, &class); - class &= ~PCI_CLASS_BRIDGE_MASK; - class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT); - iproc_pci_raw_config_write32(pcie, 0, PCI_BRIDGE_CTRL_REG_OFFSET, - 4, class); - - /* check link status to see if link is active */ - iproc_pci_raw_config_read32(pcie, 0, IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA, - 2, &link_status); - if (link_status & PCI_EXP_LNKSTA_NLW) - link_is_active = true; - - if (!link_is_active) { - /* try GEN 1 link speed */ -#define PCI_TARGET_LINK_SPEED_MASK 0xf -#define PCI_TARGET_LINK_SPEED_GEN2 0x2 -#define PCI_TARGET_LINK_SPEED_GEN1 0x1 - iproc_pci_raw_config_read32(pcie, 0, - IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2, - 4, &link_ctrl); - if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) == - PCI_TARGET_LINK_SPEED_GEN2) { - link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK; - link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1; - iproc_pci_raw_config_write32(pcie, 0, - IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2, - 4, link_ctrl); - msleep(100); - - iproc_pci_raw_config_read32(pcie, 0, - IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA, - 2, &link_status); - if (link_status & PCI_EXP_LNKSTA_NLW) - link_is_active = true; - } - } - - dev_info(dev, "link: %s\n", link_is_active ? "UP" : "DOWN"); - - return link_is_active ? 0 : -ENODEV; -} - -static void iproc_pcie_enable(struct iproc_pcie *pcie) -{ - iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK); -} - -static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie, - int window_idx) -{ - u32 val; - - val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx)); - - return !!(val & OARR_VALID); -} - -static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx, - int size_idx, u64 axi_addr, u64 pci_addr) -{ - struct device *dev = pcie->dev; - u16 oarr_offset, omap_offset; - - /* - * Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based - * on window index. - */ - oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0, - window_idx)); - omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0, - window_idx)); - if (iproc_pcie_reg_is_invalid(oarr_offset) || - iproc_pcie_reg_is_invalid(omap_offset)) - return -EINVAL; - - /* - * Program the OARR registers. The upper 32-bit OARR register is - * always right after the lower 32-bit OARR register. - */ - writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) | - OARR_VALID, pcie->base + oarr_offset); - writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4); - - /* now program the OMAP registers */ - writel(lower_32_bits(pci_addr), pcie->base + omap_offset); - writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4); - - dev_info(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n", - window_idx, oarr_offset, &axi_addr, &pci_addr); - dev_info(dev, "oarr lo 0x%x oarr hi 0x%x\n", - readl(pcie->base + oarr_offset), - readl(pcie->base + oarr_offset + 4)); - dev_info(dev, "omap lo 0x%x omap hi 0x%x\n", - readl(pcie->base + omap_offset), - readl(pcie->base + omap_offset + 4)); - - return 0; -} - -/** - * Some iProc SoCs require the SW to configure the outbound address mapping - * - * Outbound address translation: - * - * iproc_pcie_address = axi_address - axi_offset - * OARR = iproc_pcie_address - * OMAP = pci_addr - * - * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address - */ -static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, - u64 pci_addr, resource_size_t size) -{ - struct iproc_pcie_ob *ob = &pcie->ob; - struct device *dev = pcie->dev; - int ret = -EINVAL, window_idx, size_idx; - - if (axi_addr < ob->axi_offset) { - dev_err(dev, "axi address %pap less than offset %pap\n", - &axi_addr, &ob->axi_offset); - return -EINVAL; - } - - /* - * Translate the AXI address to the internal address used by the iProc - * PCIe core before programming the OARR - */ - axi_addr -= ob->axi_offset; - - /* iterate through all OARR/OMAP mapping windows */ - for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) { - const struct iproc_pcie_ob_map *ob_map = - &pcie->ob_map[window_idx]; - - /* - * If current outbound window is already in use, move on to the - * next one. - */ - if (iproc_pcie_ob_is_valid(pcie, window_idx)) - continue; - - /* - * Iterate through all supported window sizes within the - * OARR/OMAP pair to find a match. Go through the window sizes - * in a descending order. - */ - for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0; - size_idx--) { - resource_size_t window_size = - ob_map->window_sizes[size_idx] * SZ_1M; - - if (size < window_size) - continue; - - if (!IS_ALIGNED(axi_addr, window_size) || - !IS_ALIGNED(pci_addr, window_size)) { - dev_err(dev, - "axi %pap or pci %pap not aligned\n", - &axi_addr, &pci_addr); - return -EINVAL; - } - - /* - * Match found! Program both OARR and OMAP and mark - * them as a valid entry. - */ - ret = iproc_pcie_ob_write(pcie, window_idx, size_idx, - axi_addr, pci_addr); - if (ret) - goto err_ob; - - size -= window_size; - if (size == 0) - return 0; - - /* - * If we are here, we are done with the current window, - * but not yet finished all mappings. Need to move on - * to the next window. - */ - axi_addr += window_size; - pci_addr += window_size; - break; - } - } - -err_ob: - dev_err(dev, "unable to configure outbound mapping\n"); - dev_err(dev, - "axi %pap, axi offset %pap, pci %pap, res size %pap\n", - &axi_addr, &ob->axi_offset, &pci_addr, &size); - - return ret; -} - -static int iproc_pcie_map_ranges(struct iproc_pcie *pcie, - struct list_head *resources) -{ - struct device *dev = pcie->dev; - struct resource_entry *window; - int ret; - - resource_list_for_each_entry(window, resources) { - struct resource *res = window->res; - u64 res_type = resource_type(res); - - switch (res_type) { - case IORESOURCE_IO: - case IORESOURCE_BUS: - break; - case IORESOURCE_MEM: - ret = iproc_pcie_setup_ob(pcie, res->start, - res->start - window->offset, - resource_size(res)); - if (ret) - return ret; - break; - default: - dev_err(dev, "invalid resource %pR\n", res); - return -EINVAL; - } - } - - return 0; -} - -static inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie, - int region_idx) -{ - const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; - u32 val; - - val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx)); - - return !!(val & (BIT(ib_map->nr_sizes) - 1)); -} - -static inline bool iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map, - enum iproc_pcie_ib_map_type type) -{ - return !!(ib_map->type == type); -} - -static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx, - int size_idx, int nr_windows, u64 axi_addr, - u64 pci_addr, resource_size_t size) -{ - struct device *dev = pcie->dev; - const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; - u16 iarr_offset, imap_offset; - u32 val; - int window_idx; - - iarr_offset = iproc_pcie_reg_offset(pcie, - MAP_REG(IPROC_PCIE_IARR0, region_idx)); - imap_offset = iproc_pcie_reg_offset(pcie, - MAP_REG(IPROC_PCIE_IMAP0, region_idx)); - if (iproc_pcie_reg_is_invalid(iarr_offset) || - iproc_pcie_reg_is_invalid(imap_offset)) - return -EINVAL; - - dev_info(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n", - region_idx, iarr_offset, &axi_addr, &pci_addr); - - /* - * Program the IARR registers. The upper 32-bit IARR register is - * always right after the lower 32-bit IARR register. - */ - writel(lower_32_bits(pci_addr) | BIT(size_idx), - pcie->base + iarr_offset); - writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4); - - dev_info(dev, "iarr lo 0x%x iarr hi 0x%x\n", - readl(pcie->base + iarr_offset), - readl(pcie->base + iarr_offset + 4)); - - /* - * Now program the IMAP registers. Each IARR region may have one or - * more IMAP windows. - */ - size >>= ilog2(nr_windows); - for (window_idx = 0; window_idx < nr_windows; window_idx++) { - val = readl(pcie->base + imap_offset); - val |= lower_32_bits(axi_addr) | IMAP_VALID; - writel(val, pcie->base + imap_offset); - writel(upper_32_bits(axi_addr), - pcie->base + imap_offset + ib_map->imap_addr_offset); - - dev_info(dev, "imap window [%d] lo 0x%x hi 0x%x\n", - window_idx, readl(pcie->base + imap_offset), - readl(pcie->base + imap_offset + - ib_map->imap_addr_offset)); - - imap_offset += ib_map->imap_window_offset; - axi_addr += size; - } - - return 0; -} - -static int iproc_pcie_setup_ib(struct iproc_pcie *pcie, - struct of_pci_range *range, - enum iproc_pcie_ib_map_type type) -{ - struct device *dev = pcie->dev; - struct iproc_pcie_ib *ib = &pcie->ib; - int ret; - unsigned int region_idx, size_idx; - u64 axi_addr = range->cpu_addr, pci_addr = range->pci_addr; - resource_size_t size = range->size; - - /* iterate through all IARR mapping regions */ - for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) { - const struct iproc_pcie_ib_map *ib_map = - &pcie->ib_map[region_idx]; - - /* - * If current inbound region is already in use or not a - * compatible type, move on to the next. - */ - if (iproc_pcie_ib_is_in_use(pcie, region_idx) || - !iproc_pcie_ib_check_type(ib_map, type)) - continue; - - /* iterate through all supported region sizes to find a match */ - for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) { - resource_size_t region_size = - ib_map->region_sizes[size_idx] * ib_map->size_unit; - - if (size != region_size) - continue; - - if (!IS_ALIGNED(axi_addr, region_size) || - !IS_ALIGNED(pci_addr, region_size)) { - dev_err(dev, - "axi %pap or pci %pap not aligned\n", - &axi_addr, &pci_addr); - return -EINVAL; - } - - /* Match found! Program IARR and all IMAP windows. */ - ret = iproc_pcie_ib_write(pcie, region_idx, size_idx, - ib_map->nr_windows, axi_addr, - pci_addr, size); - if (ret) - goto err_ib; - else - return 0; - - } - } - ret = -EINVAL; - -err_ib: - dev_err(dev, "unable to configure inbound mapping\n"); - dev_err(dev, "axi %pap, pci %pap, res size %pap\n", - &axi_addr, &pci_addr, &size); - - return ret; -} - -static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) -{ - struct of_pci_range range; - struct of_pci_range_parser parser; - int ret; - - /* Get the dma-ranges from DT */ - ret = of_pci_dma_range_parser_init(&parser, pcie->dev->of_node); - if (ret) - return ret; - - for_each_of_pci_range(&parser, &range) { - /* Each range entry corresponds to an inbound mapping region */ - ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_MEM); - if (ret) - return ret; - } - - return 0; -} - -static int iproce_pcie_get_msi(struct iproc_pcie *pcie, - struct device_node *msi_node, - u64 *msi_addr) -{ - struct device *dev = pcie->dev; - int ret; - struct resource res; - - /* - * Check if 'msi-map' points to ARM GICv3 ITS, which is the only - * supported external MSI controller that requires steering. - */ - if (!of_device_is_compatible(msi_node, "arm,gic-v3-its")) { - dev_err(dev, "unable to find compatible MSI controller\n"); - return -ENODEV; - } - - /* derive GITS_TRANSLATER address from GICv3 */ - ret = of_address_to_resource(msi_node, 0, &res); - if (ret < 0) { - dev_err(dev, "unable to obtain MSI controller resources\n"); - return ret; - } - - *msi_addr = res.start + GITS_TRANSLATER; - return 0; -} - -static int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr) -{ - int ret; - struct of_pci_range range; - - memset(&range, 0, sizeof(range)); - range.size = SZ_32K; - range.pci_addr = range.cpu_addr = msi_addr & ~(range.size - 1); - - ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_IO); - return ret; -} - -static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr) -{ - u32 val; - - /* - * Program bits [43:13] of address of GITS_TRANSLATER register into - * bits [30:0] of the MSI base address register. In fact, in all iProc - * based SoCs, all I/O register bases are well below the 32-bit - * boundary, so we can safely assume bits [43:32] are always zeros. - */ - iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_BASE_ADDR, - (u32)(msi_addr >> 13)); - - /* use a default 8K window size */ - iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_WINDOW_SIZE, 0); - - /* steering MSI to GICv3 ITS */ - val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_GIC_MODE); - val |= GIC_V3_CFG; - iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_GIC_MODE, val); - - /* - * Program bits [43:2] of address of GITS_TRANSLATER register into the - * iProc MSI address registers. - */ - msi_addr >>= 2; - iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_HI, - upper_32_bits(msi_addr)); - iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_LO, - lower_32_bits(msi_addr)); - - /* enable MSI */ - val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG); - val |= MSI_ENABLE_CFG; - iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val); -} - -static int iproc_pcie_msi_steer(struct iproc_pcie *pcie, - struct device_node *msi_node) -{ - struct device *dev = pcie->dev; - int ret; - u64 msi_addr; - - ret = iproce_pcie_get_msi(pcie, msi_node, &msi_addr); - if (ret < 0) { - dev_err(dev, "msi steering failed\n"); - return ret; - } - - switch (pcie->type) { - case IPROC_PCIE_PAXB_V2: - ret = iproc_pcie_paxb_v2_msi_steer(pcie, msi_addr); - if (ret) - return ret; - break; - case IPROC_PCIE_PAXC_V2: - iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int iproc_pcie_msi_enable(struct iproc_pcie *pcie) -{ - struct device_node *msi_node; - int ret; - - /* - * Either the "msi-parent" or the "msi-map" phandle needs to exist - * for us to obtain the MSI node. - */ - - msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0); - if (!msi_node) { - const __be32 *msi_map = NULL; - int len; - u32 phandle; - - msi_map = of_get_property(pcie->dev->of_node, "msi-map", &len); - if (!msi_map) - return -ENODEV; - - phandle = be32_to_cpup(msi_map + 1); - msi_node = of_find_node_by_phandle(phandle); - if (!msi_node) - return -ENODEV; - } - - /* - * Certain revisions of the iProc PCIe controller require additional - * configurations to steer the MSI writes towards an external MSI - * controller. - */ - if (pcie->need_msi_steer) { - ret = iproc_pcie_msi_steer(pcie, msi_node); - if (ret) - return ret; - } - - /* - * If another MSI controller is being used, the call below should fail - * but that is okay - */ - return iproc_msi_init(pcie, msi_node); -} - -static void iproc_pcie_msi_disable(struct iproc_pcie *pcie) -{ - iproc_msi_exit(pcie); -} - -static int iproc_pcie_rev_init(struct iproc_pcie *pcie) -{ - struct device *dev = pcie->dev; - unsigned int reg_idx; - const u16 *regs; - - switch (pcie->type) { - case IPROC_PCIE_PAXB_BCMA: - regs = iproc_pcie_reg_paxb_bcma; - break; - case IPROC_PCIE_PAXB: - regs = iproc_pcie_reg_paxb; - pcie->has_apb_err_disable = true; - if (pcie->need_ob_cfg) { - pcie->ob_map = paxb_ob_map; - pcie->ob.nr_windows = ARRAY_SIZE(paxb_ob_map); - } - break; - case IPROC_PCIE_PAXB_V2: - regs = iproc_pcie_reg_paxb_v2; - pcie->has_apb_err_disable = true; - if (pcie->need_ob_cfg) { - pcie->ob_map = paxb_v2_ob_map; - pcie->ob.nr_windows = ARRAY_SIZE(paxb_v2_ob_map); - } - pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map); - pcie->ib_map = paxb_v2_ib_map; - pcie->need_msi_steer = true; - dev_warn(dev, "reads of config registers that contain %#x return incorrect data\n", - CFG_RETRY_STATUS); - break; - case IPROC_PCIE_PAXC: - regs = iproc_pcie_reg_paxc; - pcie->ep_is_internal = true; - break; - case IPROC_PCIE_PAXC_V2: - regs = iproc_pcie_reg_paxc_v2; - pcie->ep_is_internal = true; - pcie->need_msi_steer = true; - break; - default: - dev_err(dev, "incompatible iProc PCIe interface\n"); - return -EINVAL; - } - - pcie->reg_offsets = devm_kcalloc(dev, IPROC_PCIE_MAX_NUM_REG, - sizeof(*pcie->reg_offsets), - GFP_KERNEL); - if (!pcie->reg_offsets) - return -ENOMEM; - - /* go through the register table and populate all valid registers */ - pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ? - IPROC_PCIE_REG_INVALID : regs[0]; - for (reg_idx = 1; reg_idx < IPROC_PCIE_MAX_NUM_REG; reg_idx++) - pcie->reg_offsets[reg_idx] = regs[reg_idx] ? - regs[reg_idx] : IPROC_PCIE_REG_INVALID; - - return 0; -} - -int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) -{ - struct device *dev; - int ret; - struct pci_bus *child; - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - - dev = pcie->dev; - - ret = iproc_pcie_rev_init(pcie); - if (ret) { - dev_err(dev, "unable to initialize controller parameters\n"); - return ret; - } - - ret = devm_request_pci_bus_resources(dev, res); - if (ret) - return ret; - - ret = phy_init(pcie->phy); - if (ret) { - dev_err(dev, "unable to initialize PCIe PHY\n"); - return ret; - } - - ret = phy_power_on(pcie->phy); - if (ret) { - dev_err(dev, "unable to power on PCIe PHY\n"); - goto err_exit_phy; - } - - iproc_pcie_perst_ctrl(pcie, true); - iproc_pcie_perst_ctrl(pcie, false); - - if (pcie->need_ob_cfg) { - ret = iproc_pcie_map_ranges(pcie, res); - if (ret) { - dev_err(dev, "map failed\n"); - goto err_power_off_phy; - } - } - - if (pcie->need_ib_cfg) { - ret = iproc_pcie_map_dma_ranges(pcie); - if (ret && ret != -ENOENT) - goto err_power_off_phy; - } - - ret = iproc_pcie_check_link(pcie); - if (ret) { - dev_err(dev, "no PCIe EP device detected\n"); - goto err_power_off_phy; - } - - iproc_pcie_enable(pcie); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - if (iproc_pcie_msi_enable(pcie)) - dev_info(dev, "not using iProc MSI\n"); - - list_splice_init(res, &host->windows); - host->busnr = 0; - host->dev.parent = dev; - host->ops = &iproc_pcie_ops; - host->sysdata = pcie; - host->map_irq = pcie->map_irq; - host->swizzle_irq = pci_common_swizzle; - - ret = pci_scan_root_bus_bridge(host); - if (ret < 0) { - dev_err(dev, "failed to scan host: %d\n", ret); - goto err_power_off_phy; - } - - pci_assign_unassigned_bus_resources(host->bus); - - pcie->root_bus = host->bus; - - list_for_each_entry(child, &host->bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(host->bus); - - return 0; - -err_power_off_phy: - phy_power_off(pcie->phy); -err_exit_phy: - phy_exit(pcie->phy); - return ret; -} -EXPORT_SYMBOL(iproc_pcie_setup); - -int iproc_pcie_remove(struct iproc_pcie *pcie) -{ - pci_stop_root_bus(pcie->root_bus); - pci_remove_root_bus(pcie->root_bus); - - iproc_pcie_msi_disable(pcie); - - phy_power_off(pcie->phy); - phy_exit(pcie->phy); - - return 0; -} -EXPORT_SYMBOL(iproc_pcie_remove); - -MODULE_AUTHOR("Ray Jui "); -MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h deleted file mode 100644 index 814b600b383a..000000000000 --- a/drivers/pci/host/pcie-iproc.h +++ /dev/null @@ -1,119 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2014-2015 Broadcom Corporation - */ - -#ifndef _PCIE_IPROC_H -#define _PCIE_IPROC_H - -/** - * iProc PCIe interface type - * - * PAXB is the wrapper used in root complex that can be connected to an - * external endpoint device. - * - * PAXC is the wrapper used in root complex dedicated for internal emulated - * endpoint devices. - */ -enum iproc_pcie_type { - IPROC_PCIE_PAXB_BCMA = 0, - IPROC_PCIE_PAXB, - IPROC_PCIE_PAXB_V2, - IPROC_PCIE_PAXC, - IPROC_PCIE_PAXC_V2, -}; - -/** - * iProc PCIe outbound mapping - * @axi_offset: offset from the AXI address to the internal address used by - * the iProc PCIe core - * @nr_windows: total number of supported outbound mapping windows - */ -struct iproc_pcie_ob { - resource_size_t axi_offset; - unsigned int nr_windows; -}; - -/** - * iProc PCIe inbound mapping - * @nr_regions: total number of supported inbound mapping regions - */ -struct iproc_pcie_ib { - unsigned int nr_regions; -}; - -struct iproc_pcie_ob_map; -struct iproc_pcie_ib_map; -struct iproc_msi; - -/** - * iProc PCIe device - * - * @dev: pointer to device data structure - * @type: iProc PCIe interface type - * @reg_offsets: register offsets - * @base: PCIe host controller I/O register base - * @base_addr: PCIe host controller register base physical address - * @root_bus: pointer to root bus - * @phy: optional PHY device that controls the Serdes - * @map_irq: function callback to map interrupts - * @ep_is_internal: indicates an internal emulated endpoint device is connected - * @has_apb_err_disable: indicates the controller can be configured to prevent - * unsupported request from being forwarded as an APB bus error - * - * @need_ob_cfg: indicates SW needs to configure the outbound mapping window - * @ob: outbound mapping related parameters - * @ob_map: outbound mapping related parameters specific to the controller - * - * @need_ib_cfg: indicates SW needs to configure the inbound mapping window - * @ib: inbound mapping related parameters - * @ib_map: outbound mapping region related parameters - * - * @need_msi_steer: indicates additional configuration of the iProc PCIe - * controller is required to steer MSI writes to external interrupt controller - * @msi: MSI data - */ -struct iproc_pcie { - struct device *dev; - enum iproc_pcie_type type; - u16 *reg_offsets; - void __iomem *base; - phys_addr_t base_addr; - struct resource mem; - struct pci_bus *root_bus; - struct phy *phy; - int (*map_irq)(const struct pci_dev *, u8, u8); - bool ep_is_internal; - bool has_apb_err_disable; - - bool need_ob_cfg; - struct iproc_pcie_ob ob; - const struct iproc_pcie_ob_map *ob_map; - - bool need_ib_cfg; - struct iproc_pcie_ib ib; - const struct iproc_pcie_ib_map *ib_map; - - bool need_msi_steer; - struct iproc_msi *msi; -}; - -int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res); -int iproc_pcie_remove(struct iproc_pcie *pcie); -int iproc_pcie_shutdown(struct iproc_pcie *pcie); - -#ifdef CONFIG_PCIE_IPROC_MSI -int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node); -void iproc_msi_exit(struct iproc_pcie *pcie); -#else -static inline int iproc_msi_init(struct iproc_pcie *pcie, - struct device_node *node) -{ - return -ENODEV; -} -static inline void iproc_msi_exit(struct iproc_pcie *pcie) -{ -} -#endif - -#endif /* _PCIE_IPROC_H */ diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c deleted file mode 100644 index 0baabe30858f..000000000000 --- a/drivers/pci/host/pcie-mediatek.c +++ /dev/null @@ -1,1218 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * MediaTek PCIe host controller driver. - * - * Copyright (c) 2017 MediaTek Inc. - * Author: Ryder Lee - * Honghui Zhang - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -/* PCIe shared registers */ -#define PCIE_SYS_CFG 0x00 -#define PCIE_INT_ENABLE 0x0c -#define PCIE_CFG_ADDR 0x20 -#define PCIE_CFG_DATA 0x24 - -/* PCIe per port registers */ -#define PCIE_BAR0_SETUP 0x10 -#define PCIE_CLASS 0x34 -#define PCIE_LINK_STATUS 0x50 - -#define PCIE_PORT_INT_EN(x) BIT(20 + (x)) -#define PCIE_PORT_PERST(x) BIT(1 + (x)) -#define PCIE_PORT_LINKUP BIT(0) -#define PCIE_BAR_MAP_MAX GENMASK(31, 16) - -#define PCIE_BAR_ENABLE BIT(0) -#define PCIE_REVISION_ID BIT(0) -#define PCIE_CLASS_CODE (0x60400 << 8) -#define PCIE_CONF_REG(regn) (((regn) & GENMASK(7, 2)) | \ - ((((regn) >> 8) & GENMASK(3, 0)) << 24)) -#define PCIE_CONF_FUN(fun) (((fun) << 8) & GENMASK(10, 8)) -#define PCIE_CONF_DEV(dev) (((dev) << 11) & GENMASK(15, 11)) -#define PCIE_CONF_BUS(bus) (((bus) << 16) & GENMASK(23, 16)) -#define PCIE_CONF_ADDR(regn, fun, dev, bus) \ - (PCIE_CONF_REG(regn) | PCIE_CONF_FUN(fun) | \ - PCIE_CONF_DEV(dev) | PCIE_CONF_BUS(bus)) - -/* MediaTek specific configuration registers */ -#define PCIE_FTS_NUM 0x70c -#define PCIE_FTS_NUM_MASK GENMASK(15, 8) -#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8) - -#define PCIE_FC_CREDIT 0x73c -#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) -#define PCIE_FC_CREDIT_VAL(x) ((x) << 16) - -/* PCIe V2 share registers */ -#define PCIE_SYS_CFG_V2 0x0 -#define PCIE_CSR_LTSSM_EN(x) BIT(0 + (x) * 8) -#define PCIE_CSR_ASPM_L1_EN(x) BIT(1 + (x) * 8) - -/* PCIe V2 per-port registers */ -#define PCIE_MSI_VECTOR 0x0c0 - -#define PCIE_CONF_VEND_ID 0x100 -#define PCIE_CONF_CLASS_ID 0x106 - -#define PCIE_INT_MASK 0x420 -#define INTX_MASK GENMASK(19, 16) -#define INTX_SHIFT 16 -#define PCIE_INT_STATUS 0x424 -#define MSI_STATUS BIT(23) -#define PCIE_IMSI_STATUS 0x42c -#define PCIE_IMSI_ADDR 0x430 -#define MSI_MASK BIT(23) -#define MTK_MSI_IRQS_NUM 32 - -#define PCIE_AHB_TRANS_BASE0_L 0x438 -#define PCIE_AHB_TRANS_BASE0_H 0x43c -#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0)) -#define PCIE_AXI_WINDOW0 0x448 -#define WIN_ENABLE BIT(7) - -/* PCIe V2 configuration transaction header */ -#define PCIE_CFG_HEADER0 0x460 -#define PCIE_CFG_HEADER1 0x464 -#define PCIE_CFG_HEADER2 0x468 -#define PCIE_CFG_WDATA 0x470 -#define PCIE_APP_TLP_REQ 0x488 -#define PCIE_CFG_RDATA 0x48c -#define APP_CFG_REQ BIT(0) -#define APP_CPL_STATUS GENMASK(7, 5) - -#define CFG_WRRD_TYPE_0 4 -#define CFG_WR_FMT 2 -#define CFG_RD_FMT 0 - -#define CFG_DW0_LENGTH(length) ((length) & GENMASK(9, 0)) -#define CFG_DW0_TYPE(type) (((type) << 24) & GENMASK(28, 24)) -#define CFG_DW0_FMT(fmt) (((fmt) << 29) & GENMASK(31, 29)) -#define CFG_DW2_REGN(regn) ((regn) & GENMASK(11, 2)) -#define CFG_DW2_FUN(fun) (((fun) << 16) & GENMASK(18, 16)) -#define CFG_DW2_DEV(dev) (((dev) << 19) & GENMASK(23, 19)) -#define CFG_DW2_BUS(bus) (((bus) << 24) & GENMASK(31, 24)) -#define CFG_HEADER_DW0(type, fmt) \ - (CFG_DW0_LENGTH(1) | CFG_DW0_TYPE(type) | CFG_DW0_FMT(fmt)) -#define CFG_HEADER_DW1(where, size) \ - (GENMASK(((size) - 1), 0) << ((where) & 0x3)) -#define CFG_HEADER_DW2(regn, fun, dev, bus) \ - (CFG_DW2_REGN(regn) | CFG_DW2_FUN(fun) | \ - CFG_DW2_DEV(dev) | CFG_DW2_BUS(bus)) - -#define PCIE_RST_CTRL 0x510 -#define PCIE_PHY_RSTB BIT(0) -#define PCIE_PIPE_SRSTB BIT(1) -#define PCIE_MAC_SRSTB BIT(2) -#define PCIE_CRSTB BIT(3) -#define PCIE_PERSTB BIT(8) -#define PCIE_LINKDOWN_RST_EN GENMASK(15, 13) -#define PCIE_LINK_STATUS_V2 0x804 -#define PCIE_PORT_LINKUP_V2 BIT(10) - -struct mtk_pcie_port; - -/** - * struct mtk_pcie_soc - differentiate between host generations - * @need_fix_class_id: whether this host's class ID needed to be fixed or not - * @ops: pointer to configuration access functions - * @startup: pointer to controller setting functions - * @setup_irq: pointer to initialize IRQ functions - */ -struct mtk_pcie_soc { - bool need_fix_class_id; - struct pci_ops *ops; - int (*startup)(struct mtk_pcie_port *port); - int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node); -}; - -/** - * struct mtk_pcie_port - PCIe port information - * @base: IO mapped register base - * @list: port list - * @pcie: pointer to PCIe host info - * @reset: pointer to port reset control - * @sys_ck: pointer to transaction/data link layer clock - * @ahb_ck: pointer to AHB slave interface operating clock for CSR access - * and RC initiated MMIO access - * @axi_ck: pointer to application layer MMIO channel operating clock - * @aux_ck: pointer to pe2_mac_bridge and pe2_mac_core operating clock - * when pcie_mac_ck/pcie_pipe_ck is turned off - * @obff_ck: pointer to OBFF functional block operating clock - * @pipe_ck: pointer to LTSSM and PHY/MAC layer operating clock - * @phy: pointer to PHY control block - * @lane: lane count - * @slot: port slot - * @irq_domain: legacy INTx IRQ domain - * @inner_domain: inner IRQ domain - * @msi_domain: MSI IRQ domain - * @lock: protect the msi_irq_in_use bitmap - * @msi_irq_in_use: bit map for assigned MSI IRQ - */ -struct mtk_pcie_port { - void __iomem *base; - struct list_head list; - struct mtk_pcie *pcie; - struct reset_control *reset; - struct clk *sys_ck; - struct clk *ahb_ck; - struct clk *axi_ck; - struct clk *aux_ck; - struct clk *obff_ck; - struct clk *pipe_ck; - struct phy *phy; - u32 lane; - u32 slot; - struct irq_domain *irq_domain; - struct irq_domain *inner_domain; - struct irq_domain *msi_domain; - struct mutex lock; - DECLARE_BITMAP(msi_irq_in_use, MTK_MSI_IRQS_NUM); -}; - -/** - * struct mtk_pcie - PCIe host information - * @dev: pointer to PCIe device - * @base: IO mapped register base - * @free_ck: free-run reference clock - * @io: IO resource - * @pio: PIO resource - * @mem: non-prefetchable memory resource - * @busn: bus range - * @offset: IO / Memory offset - * @ports: pointer to PCIe port information - * @soc: pointer to SoC-dependent operations - */ -struct mtk_pcie { - struct device *dev; - void __iomem *base; - struct clk *free_ck; - - struct resource io; - struct resource pio; - struct resource mem; - struct resource busn; - struct { - resource_size_t mem; - resource_size_t io; - } offset; - struct list_head ports; - const struct mtk_pcie_soc *soc; -}; - -static void mtk_pcie_subsys_powerdown(struct mtk_pcie *pcie) -{ - struct device *dev = pcie->dev; - - clk_disable_unprepare(pcie->free_ck); - - if (dev->pm_domain) { - pm_runtime_put_sync(dev); - pm_runtime_disable(dev); - } -} - -static void mtk_pcie_port_free(struct mtk_pcie_port *port) -{ - struct mtk_pcie *pcie = port->pcie; - struct device *dev = pcie->dev; - - devm_iounmap(dev, port->base); - list_del(&port->list); - devm_kfree(dev, port); -} - -static void mtk_pcie_put_resources(struct mtk_pcie *pcie) -{ - struct mtk_pcie_port *port, *tmp; - - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { - phy_power_off(port->phy); - phy_exit(port->phy); - clk_disable_unprepare(port->pipe_ck); - clk_disable_unprepare(port->obff_ck); - clk_disable_unprepare(port->axi_ck); - clk_disable_unprepare(port->aux_ck); - clk_disable_unprepare(port->ahb_ck); - clk_disable_unprepare(port->sys_ck); - mtk_pcie_port_free(port); - } - - mtk_pcie_subsys_powerdown(pcie); -} - -static int mtk_pcie_check_cfg_cpld(struct mtk_pcie_port *port) -{ - u32 val; - int err; - - err = readl_poll_timeout_atomic(port->base + PCIE_APP_TLP_REQ, val, - !(val & APP_CFG_REQ), 10, - 100 * USEC_PER_MSEC); - if (err) - return PCIBIOS_SET_FAILED; - - if (readl(port->base + PCIE_APP_TLP_REQ) & APP_CPL_STATUS) - return PCIBIOS_SET_FAILED; - - return PCIBIOS_SUCCESSFUL; -} - -static int mtk_pcie_hw_rd_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn, - int where, int size, u32 *val) -{ - u32 tmp; - - /* Write PCIe configuration transaction header for Cfgrd */ - writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_RD_FMT), - port->base + PCIE_CFG_HEADER0); - writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1); - writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus), - port->base + PCIE_CFG_HEADER2); - - /* Trigger h/w to transmit Cfgrd TLP */ - tmp = readl(port->base + PCIE_APP_TLP_REQ); - tmp |= APP_CFG_REQ; - writel(tmp, port->base + PCIE_APP_TLP_REQ); - - /* Check completion status */ - if (mtk_pcie_check_cfg_cpld(port)) - return PCIBIOS_SET_FAILED; - - /* Read cpld payload of Cfgrd */ - *val = readl(port->base + PCIE_CFG_RDATA); - - if (size == 1) - *val = (*val >> (8 * (where & 3))) & 0xff; - else if (size == 2) - *val = (*val >> (8 * (where & 3))) & 0xffff; - - return PCIBIOS_SUCCESSFUL; -} - -static int mtk_pcie_hw_wr_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn, - int where, int size, u32 val) -{ - /* Write PCIe configuration transaction header for Cfgwr */ - writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_WR_FMT), - port->base + PCIE_CFG_HEADER0); - writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1); - writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus), - port->base + PCIE_CFG_HEADER2); - - /* Write Cfgwr data */ - val = val << 8 * (where & 3); - writel(val, port->base + PCIE_CFG_WDATA); - - /* Trigger h/w to transmit Cfgwr TLP */ - val = readl(port->base + PCIE_APP_TLP_REQ); - val |= APP_CFG_REQ; - writel(val, port->base + PCIE_APP_TLP_REQ); - - /* Check completion status */ - return mtk_pcie_check_cfg_cpld(port); -} - -static struct mtk_pcie_port *mtk_pcie_find_port(struct pci_bus *bus, - unsigned int devfn) -{ - struct mtk_pcie *pcie = bus->sysdata; - struct mtk_pcie_port *port; - - list_for_each_entry(port, &pcie->ports, list) - if (port->slot == PCI_SLOT(devfn)) - return port; - - return NULL; -} - -static int mtk_pcie_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct mtk_pcie_port *port; - u32 bn = bus->number; - int ret; - - port = mtk_pcie_find_port(bus, devfn); - if (!port) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - ret = mtk_pcie_hw_rd_cfg(port, bn, devfn, where, size, val); - if (ret) - *val = ~0; - - return ret; -} - -static int mtk_pcie_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct mtk_pcie_port *port; - u32 bn = bus->number; - - port = mtk_pcie_find_port(bus, devfn); - if (!port) - return PCIBIOS_DEVICE_NOT_FOUND; - - return mtk_pcie_hw_wr_cfg(port, bn, devfn, where, size, val); -} - -static struct pci_ops mtk_pcie_ops_v2 = { - .read = mtk_pcie_config_read, - .write = mtk_pcie_config_write, -}; - -static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port) -{ - struct mtk_pcie *pcie = port->pcie; - struct resource *mem = &pcie->mem; - const struct mtk_pcie_soc *soc = port->pcie->soc; - u32 val; - size_t size; - int err; - - /* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */ - if (pcie->base) { - val = readl(pcie->base + PCIE_SYS_CFG_V2); - val |= PCIE_CSR_LTSSM_EN(port->slot) | - PCIE_CSR_ASPM_L1_EN(port->slot); - writel(val, pcie->base + PCIE_SYS_CFG_V2); - } - - /* Assert all reset signals */ - writel(0, port->base + PCIE_RST_CTRL); - - /* - * Enable PCIe link down reset, if link status changed from link up to - * link down, this will reset MAC control registers and configuration - * space. - */ - writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL); - - /* De-assert PHY, PE, PIPE, MAC and configuration reset */ - val = readl(port->base + PCIE_RST_CTRL); - val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB | - PCIE_MAC_SRSTB | PCIE_CRSTB; - writel(val, port->base + PCIE_RST_CTRL); - - /* Set up vendor ID and class code */ - if (soc->need_fix_class_id) { - val = PCI_VENDOR_ID_MEDIATEK; - writew(val, port->base + PCIE_CONF_VEND_ID); - - val = PCI_CLASS_BRIDGE_HOST; - writew(val, port->base + PCIE_CONF_CLASS_ID); - } - - /* 100ms timeout value should be enough for Gen1/2 training */ - err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_V2, val, - !!(val & PCIE_PORT_LINKUP_V2), 20, - 100 * USEC_PER_MSEC); - if (err) - return -ETIMEDOUT; - - /* Set INTx mask */ - val = readl(port->base + PCIE_INT_MASK); - val &= ~INTX_MASK; - writel(val, port->base + PCIE_INT_MASK); - - /* Set AHB to PCIe translation windows */ - size = mem->end - mem->start; - val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size)); - writel(val, port->base + PCIE_AHB_TRANS_BASE0_L); - - val = upper_32_bits(mem->start); - writel(val, port->base + PCIE_AHB_TRANS_BASE0_H); - - /* Set PCIe to AXI translation memory space.*/ - val = fls(0xffffffff) | WIN_ENABLE; - writel(val, port->base + PCIE_AXI_WINDOW0); - - return 0; -} - -static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); - phys_addr_t addr; - - /* MT2712/MT7622 only support 32-bit MSI addresses */ - addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); - msg->address_hi = 0; - msg->address_lo = lower_32_bits(addr); - - msg->data = data->hwirq; - - dev_dbg(port->pcie->dev, "msi#%d address_hi %#x address_lo %#x\n", - (int)data->hwirq, msg->address_hi, msg->address_lo); -} - -static int mtk_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) -{ - return -EINVAL; -} - -static void mtk_msi_ack_irq(struct irq_data *data) -{ - struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); - u32 hwirq = data->hwirq; - - writel(1 << hwirq, port->base + PCIE_IMSI_STATUS); -} - -static struct irq_chip mtk_msi_bottom_irq_chip = { - .name = "MTK MSI", - .irq_compose_msi_msg = mtk_compose_msi_msg, - .irq_set_affinity = mtk_msi_set_affinity, - .irq_ack = mtk_msi_ack_irq, -}; - -static int mtk_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *args) -{ - struct mtk_pcie_port *port = domain->host_data; - unsigned long bit; - - WARN_ON(nr_irqs != 1); - mutex_lock(&port->lock); - - bit = find_first_zero_bit(port->msi_irq_in_use, MTK_MSI_IRQS_NUM); - if (bit >= MTK_MSI_IRQS_NUM) { - mutex_unlock(&port->lock); - return -ENOSPC; - } - - __set_bit(bit, port->msi_irq_in_use); - - mutex_unlock(&port->lock); - - irq_domain_set_info(domain, virq, bit, &mtk_msi_bottom_irq_chip, - domain->host_data, handle_edge_irq, - NULL, NULL); - - return 0; -} - -static void mtk_pcie_irq_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) -{ - struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct mtk_pcie_port *port = irq_data_get_irq_chip_data(d); - - mutex_lock(&port->lock); - - if (!test_bit(d->hwirq, port->msi_irq_in_use)) - dev_err(port->pcie->dev, "trying to free unused MSI#%lu\n", - d->hwirq); - else - __clear_bit(d->hwirq, port->msi_irq_in_use); - - mutex_unlock(&port->lock); - - irq_domain_free_irqs_parent(domain, virq, nr_irqs); -} - -static const struct irq_domain_ops msi_domain_ops = { - .alloc = mtk_pcie_irq_domain_alloc, - .free = mtk_pcie_irq_domain_free, -}; - -static struct irq_chip mtk_msi_irq_chip = { - .name = "MTK PCIe MSI", - .irq_ack = irq_chip_ack_parent, - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; - -static struct msi_domain_info mtk_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX), - .chip = &mtk_msi_irq_chip, -}; - -static int mtk_pcie_allocate_msi_domains(struct mtk_pcie_port *port) -{ - struct fwnode_handle *fwnode = of_node_to_fwnode(port->pcie->dev->of_node); - - mutex_init(&port->lock); - - port->inner_domain = irq_domain_create_linear(fwnode, MTK_MSI_IRQS_NUM, - &msi_domain_ops, port); - if (!port->inner_domain) { - dev_err(port->pcie->dev, "failed to create IRQ domain\n"); - return -ENOMEM; - } - - port->msi_domain = pci_msi_create_irq_domain(fwnode, &mtk_msi_domain_info, - port->inner_domain); - if (!port->msi_domain) { - dev_err(port->pcie->dev, "failed to create MSI domain\n"); - irq_domain_remove(port->inner_domain); - return -ENOMEM; - } - - return 0; -} - -static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) -{ - u32 val; - phys_addr_t msg_addr; - - msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); - val = lower_32_bits(msg_addr); - writel(val, port->base + PCIE_IMSI_ADDR); - - val = readl(port->base + PCIE_INT_MASK); - val &= ~MSI_MASK; - writel(val, port->base + PCIE_INT_MASK); -} - -static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -static const struct irq_domain_ops intx_domain_ops = { - .map = mtk_pcie_intx_map, -}; - -static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port, - struct device_node *node) -{ - struct device *dev = port->pcie->dev; - struct device_node *pcie_intc_node; - int ret; - - /* Setup INTx */ - pcie_intc_node = of_get_next_child(node, NULL); - if (!pcie_intc_node) { - dev_err(dev, "no PCIe Intc node found\n"); - return -ENODEV; - } - - port->irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, - &intx_domain_ops, port); - if (!port->irq_domain) { - dev_err(dev, "failed to get INTx IRQ domain\n"); - return -ENODEV; - } - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - ret = mtk_pcie_allocate_msi_domains(port); - if (ret) - return ret; - - mtk_pcie_enable_msi(port); - } - - return 0; -} - -static void mtk_pcie_intr_handler(struct irq_desc *desc) -{ - struct mtk_pcie_port *port = irq_desc_get_handler_data(desc); - struct irq_chip *irqchip = irq_desc_get_chip(desc); - unsigned long status; - u32 virq; - u32 bit = INTX_SHIFT; - - chained_irq_enter(irqchip, desc); - - status = readl(port->base + PCIE_INT_STATUS); - if (status & INTX_MASK) { - for_each_set_bit_from(bit, &status, PCI_NUM_INTX + INTX_SHIFT) { - /* Clear the INTx */ - writel(1 << bit, port->base + PCIE_INT_STATUS); - virq = irq_find_mapping(port->irq_domain, - bit - INTX_SHIFT); - generic_handle_irq(virq); - } - } - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (status & MSI_STATUS){ - unsigned long imsi_status; - - while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) { - for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) { - virq = irq_find_mapping(port->inner_domain, bit); - generic_handle_irq(virq); - } - } - /* Clear MSI interrupt status */ - writel(MSI_STATUS, port->base + PCIE_INT_STATUS); - } - } - - chained_irq_exit(irqchip, desc); - - return; -} - -static int mtk_pcie_setup_irq(struct mtk_pcie_port *port, - struct device_node *node) -{ - struct mtk_pcie *pcie = port->pcie; - struct device *dev = pcie->dev; - struct platform_device *pdev = to_platform_device(dev); - int err, irq; - - err = mtk_pcie_init_irq_domain(port, node); - if (err) { - dev_err(dev, "failed to init PCIe IRQ domain\n"); - return err; - } - - irq = platform_get_irq(pdev, port->slot); - irq_set_chained_handler_and_data(irq, mtk_pcie_intr_handler, port); - - return 0; -} - -static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus, - unsigned int devfn, int where) -{ - struct mtk_pcie *pcie = bus->sysdata; - - writel(PCIE_CONF_ADDR(where, PCI_FUNC(devfn), PCI_SLOT(devfn), - bus->number), pcie->base + PCIE_CFG_ADDR); - - return pcie->base + PCIE_CFG_DATA + (where & 3); -} - -static struct pci_ops mtk_pcie_ops = { - .map_bus = mtk_pcie_map_bus, - .read = pci_generic_config_read, - .write = pci_generic_config_write, -}; - -static int mtk_pcie_startup_port(struct mtk_pcie_port *port) -{ - struct mtk_pcie *pcie = port->pcie; - u32 func = PCI_FUNC(port->slot << 3); - u32 slot = PCI_SLOT(port->slot << 3); - u32 val; - int err; - - /* assert port PERST_N */ - val = readl(pcie->base + PCIE_SYS_CFG); - val |= PCIE_PORT_PERST(port->slot); - writel(val, pcie->base + PCIE_SYS_CFG); - - /* de-assert port PERST_N */ - val = readl(pcie->base + PCIE_SYS_CFG); - val &= ~PCIE_PORT_PERST(port->slot); - writel(val, pcie->base + PCIE_SYS_CFG); - - /* 100ms timeout value should be enough for Gen1/2 training */ - err = readl_poll_timeout(port->base + PCIE_LINK_STATUS, val, - !!(val & PCIE_PORT_LINKUP), 20, - 100 * USEC_PER_MSEC); - if (err) - return -ETIMEDOUT; - - /* enable interrupt */ - val = readl(pcie->base + PCIE_INT_ENABLE); - val |= PCIE_PORT_INT_EN(port->slot); - writel(val, pcie->base + PCIE_INT_ENABLE); - - /* map to all DDR region. We need to set it before cfg operation. */ - writel(PCIE_BAR_MAP_MAX | PCIE_BAR_ENABLE, - port->base + PCIE_BAR0_SETUP); - - /* configure class code and revision ID */ - writel(PCIE_CLASS_CODE | PCIE_REVISION_ID, port->base + PCIE_CLASS); - - /* configure FC credit */ - writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, func, slot, 0), - pcie->base + PCIE_CFG_ADDR); - val = readl(pcie->base + PCIE_CFG_DATA); - val &= ~PCIE_FC_CREDIT_MASK; - val |= PCIE_FC_CREDIT_VAL(0x806c); - writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, func, slot, 0), - pcie->base + PCIE_CFG_ADDR); - writel(val, pcie->base + PCIE_CFG_DATA); - - /* configure RC FTS number to 250 when it leaves L0s */ - writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, func, slot, 0), - pcie->base + PCIE_CFG_ADDR); - val = readl(pcie->base + PCIE_CFG_DATA); - val &= ~PCIE_FTS_NUM_MASK; - val |= PCIE_FTS_NUM_L0(0x50); - writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, func, slot, 0), - pcie->base + PCIE_CFG_ADDR); - writel(val, pcie->base + PCIE_CFG_DATA); - - return 0; -} - -static void mtk_pcie_enable_port(struct mtk_pcie_port *port) -{ - struct mtk_pcie *pcie = port->pcie; - struct device *dev = pcie->dev; - int err; - - err = clk_prepare_enable(port->sys_ck); - if (err) { - dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot); - goto err_sys_clk; - } - - err = clk_prepare_enable(port->ahb_ck); - if (err) { - dev_err(dev, "failed to enable ahb_ck%d\n", port->slot); - goto err_ahb_clk; - } - - err = clk_prepare_enable(port->aux_ck); - if (err) { - dev_err(dev, "failed to enable aux_ck%d\n", port->slot); - goto err_aux_clk; - } - - err = clk_prepare_enable(port->axi_ck); - if (err) { - dev_err(dev, "failed to enable axi_ck%d\n", port->slot); - goto err_axi_clk; - } - - err = clk_prepare_enable(port->obff_ck); - if (err) { - dev_err(dev, "failed to enable obff_ck%d\n", port->slot); - goto err_obff_clk; - } - - err = clk_prepare_enable(port->pipe_ck); - if (err) { - dev_err(dev, "failed to enable pipe_ck%d\n", port->slot); - goto err_pipe_clk; - } - - reset_control_assert(port->reset); - reset_control_deassert(port->reset); - - err = phy_init(port->phy); - if (err) { - dev_err(dev, "failed to initialize port%d phy\n", port->slot); - goto err_phy_init; - } - - err = phy_power_on(port->phy); - if (err) { - dev_err(dev, "failed to power on port%d phy\n", port->slot); - goto err_phy_on; - } - - if (!pcie->soc->startup(port)) - return; - - dev_info(dev, "Port%d link down\n", port->slot); - - phy_power_off(port->phy); -err_phy_on: - phy_exit(port->phy); -err_phy_init: - clk_disable_unprepare(port->pipe_ck); -err_pipe_clk: - clk_disable_unprepare(port->obff_ck); -err_obff_clk: - clk_disable_unprepare(port->axi_ck); -err_axi_clk: - clk_disable_unprepare(port->aux_ck); -err_aux_clk: - clk_disable_unprepare(port->ahb_ck); -err_ahb_clk: - clk_disable_unprepare(port->sys_ck); -err_sys_clk: - mtk_pcie_port_free(port); -} - -static int mtk_pcie_parse_port(struct mtk_pcie *pcie, - struct device_node *node, - int slot) -{ - struct mtk_pcie_port *port; - struct resource *regs; - struct device *dev = pcie->dev; - struct platform_device *pdev = to_platform_device(dev); - char name[10]; - int err; - - port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); - if (!port) - return -ENOMEM; - - err = of_property_read_u32(node, "num-lanes", &port->lane); - if (err) { - dev_err(dev, "missing num-lanes property\n"); - return err; - } - - snprintf(name, sizeof(name), "port%d", slot); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - port->base = devm_ioremap_resource(dev, regs); - if (IS_ERR(port->base)) { - dev_err(dev, "failed to map port%d base\n", slot); - return PTR_ERR(port->base); - } - - snprintf(name, sizeof(name), "sys_ck%d", slot); - port->sys_ck = devm_clk_get(dev, name); - if (IS_ERR(port->sys_ck)) { - dev_err(dev, "failed to get sys_ck%d clock\n", slot); - return PTR_ERR(port->sys_ck); - } - - /* sys_ck might be divided into the following parts in some chips */ - snprintf(name, sizeof(name), "ahb_ck%d", slot); - port->ahb_ck = devm_clk_get(dev, name); - if (IS_ERR(port->ahb_ck)) { - if (PTR_ERR(port->ahb_ck) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - port->ahb_ck = NULL; - } - - snprintf(name, sizeof(name), "axi_ck%d", slot); - port->axi_ck = devm_clk_get(dev, name); - if (IS_ERR(port->axi_ck)) { - if (PTR_ERR(port->axi_ck) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - port->axi_ck = NULL; - } - - snprintf(name, sizeof(name), "aux_ck%d", slot); - port->aux_ck = devm_clk_get(dev, name); - if (IS_ERR(port->aux_ck)) { - if (PTR_ERR(port->aux_ck) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - port->aux_ck = NULL; - } - - snprintf(name, sizeof(name), "obff_ck%d", slot); - port->obff_ck = devm_clk_get(dev, name); - if (IS_ERR(port->obff_ck)) { - if (PTR_ERR(port->obff_ck) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - port->obff_ck = NULL; - } - - snprintf(name, sizeof(name), "pipe_ck%d", slot); - port->pipe_ck = devm_clk_get(dev, name); - if (IS_ERR(port->pipe_ck)) { - if (PTR_ERR(port->pipe_ck) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - port->pipe_ck = NULL; - } - - snprintf(name, sizeof(name), "pcie-rst%d", slot); - port->reset = devm_reset_control_get_optional_exclusive(dev, name); - if (PTR_ERR(port->reset) == -EPROBE_DEFER) - return PTR_ERR(port->reset); - - /* some platforms may use default PHY setting */ - snprintf(name, sizeof(name), "pcie-phy%d", slot); - port->phy = devm_phy_optional_get(dev, name); - if (IS_ERR(port->phy)) - return PTR_ERR(port->phy); - - port->slot = slot; - port->pcie = pcie; - - if (pcie->soc->setup_irq) { - err = pcie->soc->setup_irq(port, node); - if (err) - return err; - } - - INIT_LIST_HEAD(&port->list); - list_add_tail(&port->list, &pcie->ports); - - return 0; -} - -static int mtk_pcie_subsys_powerup(struct mtk_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct platform_device *pdev = to_platform_device(dev); - struct resource *regs; - int err; - - /* get shared registers, which are optional */ - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "subsys"); - if (regs) { - pcie->base = devm_ioremap_resource(dev, regs); - if (IS_ERR(pcie->base)) { - dev_err(dev, "failed to map shared register\n"); - return PTR_ERR(pcie->base); - } - } - - pcie->free_ck = devm_clk_get(dev, "free_ck"); - if (IS_ERR(pcie->free_ck)) { - if (PTR_ERR(pcie->free_ck) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - pcie->free_ck = NULL; - } - - if (dev->pm_domain) { - pm_runtime_enable(dev); - pm_runtime_get_sync(dev); - } - - /* enable top level clock */ - err = clk_prepare_enable(pcie->free_ck); - if (err) { - dev_err(dev, "failed to enable free_ck\n"); - goto err_free_ck; - } - - return 0; - -err_free_ck: - if (dev->pm_domain) { - pm_runtime_put_sync(dev); - pm_runtime_disable(dev); - } - - return err; -} - -static int mtk_pcie_setup(struct mtk_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct device_node *node = dev->of_node, *child; - struct of_pci_range_parser parser; - struct of_pci_range range; - struct resource res; - struct mtk_pcie_port *port, *tmp; - int err; - - if (of_pci_range_parser_init(&parser, node)) { - dev_err(dev, "missing \"ranges\" property\n"); - return -EINVAL; - } - - for_each_of_pci_range(&parser, &range) { - err = of_pci_range_to_resource(&range, node, &res); - if (err < 0) - return err; - - switch (res.flags & IORESOURCE_TYPE_BITS) { - case IORESOURCE_IO: - pcie->offset.io = res.start - range.pci_addr; - - memcpy(&pcie->pio, &res, sizeof(res)); - pcie->pio.name = node->full_name; - - pcie->io.start = range.cpu_addr; - pcie->io.end = range.cpu_addr + range.size - 1; - pcie->io.flags = IORESOURCE_MEM; - pcie->io.name = "I/O"; - - memcpy(&res, &pcie->io, sizeof(res)); - break; - - case IORESOURCE_MEM: - pcie->offset.mem = res.start - range.pci_addr; - - memcpy(&pcie->mem, &res, sizeof(res)); - pcie->mem.name = "non-prefetchable"; - break; - } - } - - err = of_pci_parse_bus_range(node, &pcie->busn); - if (err < 0) { - dev_err(dev, "failed to parse bus ranges property: %d\n", err); - pcie->busn.name = node->name; - pcie->busn.start = 0; - pcie->busn.end = 0xff; - pcie->busn.flags = IORESOURCE_BUS; - } - - for_each_available_child_of_node(node, child) { - int slot; - - err = of_pci_get_devfn(child); - if (err < 0) { - dev_err(dev, "failed to parse devfn: %d\n", err); - return err; - } - - slot = PCI_SLOT(err); - - err = mtk_pcie_parse_port(pcie, child, slot); - if (err) - return err; - } - - err = mtk_pcie_subsys_powerup(pcie); - if (err) - return err; - - /* enable each port, and then check link status */ - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); - - /* power down PCIe subsys if slots are all empty (link down) */ - if (list_empty(&pcie->ports)) - mtk_pcie_subsys_powerdown(pcie); - - return 0; -} - -static int mtk_pcie_request_resources(struct mtk_pcie *pcie) -{ - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct list_head *windows = &host->windows; - struct device *dev = pcie->dev; - int err; - - pci_add_resource_offset(windows, &pcie->pio, pcie->offset.io); - pci_add_resource_offset(windows, &pcie->mem, pcie->offset.mem); - pci_add_resource(windows, &pcie->busn); - - err = devm_request_pci_bus_resources(dev, windows); - if (err < 0) - return err; - - pci_remap_iospace(&pcie->pio, pcie->io.start); - - return 0; -} - -static int mtk_pcie_register_host(struct pci_host_bridge *host) -{ - struct mtk_pcie *pcie = pci_host_bridge_priv(host); - struct pci_bus *child; - int err; - - host->busnr = pcie->busn.start; - host->dev.parent = pcie->dev; - host->ops = pcie->soc->ops; - host->map_irq = of_irq_parse_and_map_pci; - host->swizzle_irq = pci_common_swizzle; - host->sysdata = pcie; - - err = pci_scan_root_bus_bridge(host); - if (err < 0) - return err; - - pci_bus_size_bridges(host->bus); - pci_bus_assign_resources(host->bus); - - list_for_each_entry(child, &host->bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(host->bus); - - return 0; -} - -static int mtk_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mtk_pcie *pcie; - struct pci_host_bridge *host; - int err; - - host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); - if (!host) - return -ENOMEM; - - pcie = pci_host_bridge_priv(host); - - pcie->dev = dev; - pcie->soc = of_device_get_match_data(dev); - platform_set_drvdata(pdev, pcie); - INIT_LIST_HEAD(&pcie->ports); - - err = mtk_pcie_setup(pcie); - if (err) - return err; - - err = mtk_pcie_request_resources(pcie); - if (err) - goto put_resources; - - err = mtk_pcie_register_host(host); - if (err) - goto put_resources; - - return 0; - -put_resources: - if (!list_empty(&pcie->ports)) - mtk_pcie_put_resources(pcie); - - return err; -} - -static const struct mtk_pcie_soc mtk_pcie_soc_v1 = { - .ops = &mtk_pcie_ops, - .startup = mtk_pcie_startup_port, -}; - -static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = { - .ops = &mtk_pcie_ops_v2, - .startup = mtk_pcie_startup_port_v2, - .setup_irq = mtk_pcie_setup_irq, -}; - -static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = { - .need_fix_class_id = true, - .ops = &mtk_pcie_ops_v2, - .startup = mtk_pcie_startup_port_v2, - .setup_irq = mtk_pcie_setup_irq, -}; - -static const struct of_device_id mtk_pcie_ids[] = { - { .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 }, - { .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 }, - { .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_mt2712 }, - { .compatible = "mediatek,mt7622-pcie", .data = &mtk_pcie_soc_mt7622 }, - {}, -}; - -static struct platform_driver mtk_pcie_driver = { - .probe = mtk_pcie_probe, - .driver = { - .name = "mtk-pcie", - .of_match_table = mtk_pcie_ids, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(mtk_pcie_driver); diff --git a/drivers/pci/host/pcie-mobiveil.c b/drivers/pci/host/pcie-mobiveil.c deleted file mode 100644 index 4d6c20e47bed..000000000000 --- a/drivers/pci/host/pcie-mobiveil.c +++ /dev/null @@ -1,866 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe host controller driver for Mobiveil PCIe Host controller - * - * Copyright (c) 2018 Mobiveil Inc. - * Author: Subrahmanya Lingappa - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* register offsets and bit positions */ - -/* - * translation tables are grouped into windows, each window registers are - * grouped into blocks of 4 or 16 registers each - */ -#define PAB_REG_BLOCK_SIZE 16 -#define PAB_EXT_REG_BLOCK_SIZE 4 - -#define PAB_REG_ADDR(offset, win) (offset + (win * PAB_REG_BLOCK_SIZE)) -#define PAB_EXT_REG_ADDR(offset, win) (offset + (win * PAB_EXT_REG_BLOCK_SIZE)) - -#define LTSSM_STATUS 0x0404 -#define LTSSM_STATUS_L0_MASK 0x3f -#define LTSSM_STATUS_L0 0x2d - -#define PAB_CTRL 0x0808 -#define AMBA_PIO_ENABLE_SHIFT 0 -#define PEX_PIO_ENABLE_SHIFT 1 -#define PAGE_SEL_SHIFT 13 -#define PAGE_SEL_MASK 0x3f -#define PAGE_LO_MASK 0x3ff -#define PAGE_SEL_EN 0xc00 -#define PAGE_SEL_OFFSET_SHIFT 10 - -#define PAB_AXI_PIO_CTRL 0x0840 -#define APIO_EN_MASK 0xf - -#define PAB_PEX_PIO_CTRL 0x08c0 -#define PIO_ENABLE_SHIFT 0 - -#define PAB_INTP_AMBA_MISC_ENB 0x0b0c -#define PAB_INTP_AMBA_MISC_STAT 0x0b1c -#define PAB_INTP_INTX_MASK 0x01e0 -#define PAB_INTP_MSI_MASK 0x8 - -#define PAB_AXI_AMAP_CTRL(win) PAB_REG_ADDR(0x0ba0, win) -#define WIN_ENABLE_SHIFT 0 -#define WIN_TYPE_SHIFT 1 - -#define PAB_EXT_AXI_AMAP_SIZE(win) PAB_EXT_REG_ADDR(0xbaf0, win) - -#define PAB_AXI_AMAP_AXI_WIN(win) PAB_REG_ADDR(0x0ba4, win) -#define AXI_WINDOW_ALIGN_MASK 3 - -#define PAB_AXI_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x0ba8, win) -#define PAB_BUS_SHIFT 24 -#define PAB_DEVICE_SHIFT 19 -#define PAB_FUNCTION_SHIFT 16 - -#define PAB_AXI_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x0bac, win) -#define PAB_INTP_AXI_PIO_CLASS 0x474 - -#define PAB_PEX_AMAP_CTRL(win) PAB_REG_ADDR(0x4ba0, win) -#define AMAP_CTRL_EN_SHIFT 0 -#define AMAP_CTRL_TYPE_SHIFT 1 - -#define PAB_EXT_PEX_AMAP_SIZEN(win) PAB_EXT_REG_ADDR(0xbef0, win) -#define PAB_PEX_AMAP_AXI_WIN(win) PAB_REG_ADDR(0x4ba4, win) -#define PAB_PEX_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x4ba8, win) -#define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win) - -/* starting offset of INTX bits in status register */ -#define PAB_INTX_START 5 - -/* supported number of MSI interrupts */ -#define PCI_NUM_MSI 16 - -/* MSI registers */ -#define MSI_BASE_LO_OFFSET 0x04 -#define MSI_BASE_HI_OFFSET 0x08 -#define MSI_SIZE_OFFSET 0x0c -#define MSI_ENABLE_OFFSET 0x14 -#define MSI_STATUS_OFFSET 0x18 -#define MSI_DATA_OFFSET 0x20 -#define MSI_ADDR_L_OFFSET 0x24 -#define MSI_ADDR_H_OFFSET 0x28 - -/* outbound and inbound window definitions */ -#define WIN_NUM_0 0 -#define WIN_NUM_1 1 -#define CFG_WINDOW_TYPE 0 -#define IO_WINDOW_TYPE 1 -#define MEM_WINDOW_TYPE 2 -#define IB_WIN_SIZE (256 * 1024 * 1024 * 1024) -#define MAX_PIO_WINDOWS 8 - -/* Parameters for the waiting for link up routine */ -#define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_MIN 90000 -#define LINK_WAIT_MAX 100000 - -struct mobiveil_msi { /* MSI information */ - struct mutex lock; /* protect bitmap variable */ - struct irq_domain *msi_domain; - struct irq_domain *dev_domain; - phys_addr_t msi_pages_phys; - int num_of_vectors; - DECLARE_BITMAP(msi_irq_in_use, PCI_NUM_MSI); -}; - -struct mobiveil_pcie { - struct platform_device *pdev; - struct list_head resources; - void __iomem *config_axi_slave_base; /* endpoint config base */ - void __iomem *csr_axi_slave_base; /* root port config base */ - void __iomem *apb_csr_base; /* MSI register base */ - void __iomem *pcie_reg_base; /* Physical PCIe Controller Base */ - struct irq_domain *intx_domain; - raw_spinlock_t intx_mask_lock; - int irq; - int apio_wins; - int ppio_wins; - int ob_wins_configured; /* configured outbound windows */ - int ib_wins_configured; /* configured inbound windows */ - struct resource *ob_io_res; - char root_bus_nr; - struct mobiveil_msi msi; -}; - -static inline void csr_writel(struct mobiveil_pcie *pcie, const u32 value, - const u32 reg) -{ - writel_relaxed(value, pcie->csr_axi_slave_base + reg); -} - -static inline u32 csr_readl(struct mobiveil_pcie *pcie, const u32 reg) -{ - return readl_relaxed(pcie->csr_axi_slave_base + reg); -} - -static bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie) -{ - return (csr_readl(pcie, LTSSM_STATUS) & - LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0; -} - -static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) -{ - struct mobiveil_pcie *pcie = bus->sysdata; - - /* Only one device down on each root port */ - if ((bus->number == pcie->root_bus_nr) && (devfn > 0)) - return false; - - /* - * Do not read more than one device on the bus directly - * attached to RC - */ - if ((bus->primary == pcie->root_bus_nr) && (devfn > 0)) - return false; - - return true; -} - -/* - * mobiveil_pcie_map_bus - routine to get the configuration base of either - * root port or endpoint - */ -static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, - unsigned int devfn, int where) -{ - struct mobiveil_pcie *pcie = bus->sysdata; - - if (!mobiveil_pcie_valid_device(bus, devfn)) - return NULL; - - if (bus->number == pcie->root_bus_nr) { - /* RC config access */ - return pcie->csr_axi_slave_base + where; - } - - /* - * EP config access (in Config/APIO space) - * Program PEX Address base (31..16 bits) with appropriate value - * (BDF) in PAB_AXI_AMAP_PEX_WIN_L0 Register. - * Relies on pci_lock serialization - */ - csr_writel(pcie, bus->number << PAB_BUS_SHIFT | - PCI_SLOT(devfn) << PAB_DEVICE_SHIFT | - PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT, - PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0)); - return pcie->config_axi_slave_base + where; -} - -static struct pci_ops mobiveil_pcie_ops = { - .map_bus = mobiveil_pcie_map_bus, - .read = pci_generic_config_read, - .write = pci_generic_config_write, -}; - -static void mobiveil_pcie_isr(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct mobiveil_pcie *pcie = irq_desc_get_handler_data(desc); - struct device *dev = &pcie->pdev->dev; - struct mobiveil_msi *msi = &pcie->msi; - u32 msi_data, msi_addr_lo, msi_addr_hi; - u32 intr_status, msi_status; - unsigned long shifted_status; - u32 bit, virq, val, mask; - - /* - * The core provides a single interrupt for both INTx/MSI messages. - * So we'll read both INTx and MSI status - */ - - chained_irq_enter(chip, desc); - - /* read INTx status */ - val = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT); - mask = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); - intr_status = val & mask; - - /* Handle INTx */ - if (intr_status & PAB_INTP_INTX_MASK) { - shifted_status = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT) >> - PAB_INTX_START; - do { - for_each_set_bit(bit, &shifted_status, PCI_NUM_INTX) { - virq = irq_find_mapping(pcie->intx_domain, - bit + 1); - if (virq) - generic_handle_irq(virq); - else - dev_err_ratelimited(dev, - "unexpected IRQ, INT%d\n", bit); - - /* clear interrupt */ - csr_writel(pcie, - shifted_status << PAB_INTX_START, - PAB_INTP_AMBA_MISC_STAT); - } - } while ((shifted_status >> PAB_INTX_START) != 0); - } - - /* read extra MSI status register */ - msi_status = readl_relaxed(pcie->apb_csr_base + MSI_STATUS_OFFSET); - - /* handle MSI interrupts */ - while (msi_status & 1) { - msi_data = readl_relaxed(pcie->apb_csr_base - + MSI_DATA_OFFSET); - - /* - * MSI_STATUS_OFFSET register gets updated to zero - * once we pop not only the MSI data but also address - * from MSI hardware FIFO. So keeping these following - * two dummy reads. - */ - msi_addr_lo = readl_relaxed(pcie->apb_csr_base + - MSI_ADDR_L_OFFSET); - msi_addr_hi = readl_relaxed(pcie->apb_csr_base + - MSI_ADDR_H_OFFSET); - dev_dbg(dev, "MSI registers, data: %08x, addr: %08x:%08x\n", - msi_data, msi_addr_hi, msi_addr_lo); - - virq = irq_find_mapping(msi->dev_domain, msi_data); - if (virq) - generic_handle_irq(virq); - - msi_status = readl_relaxed(pcie->apb_csr_base + - MSI_STATUS_OFFSET); - } - - /* Clear the interrupt status */ - csr_writel(pcie, intr_status, PAB_INTP_AMBA_MISC_STAT); - chained_irq_exit(chip, desc); -} - -static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - struct platform_device *pdev = pcie->pdev; - struct device_node *node = dev->of_node; - struct resource *res; - const char *type; - - type = of_get_property(node, "device_type", NULL); - if (!type || strcmp(type, "pci")) { - dev_err(dev, "invalid \"device_type\" %s\n", type); - return -EINVAL; - } - - /* map config resource */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "config_axi_slave"); - pcie->config_axi_slave_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pcie->config_axi_slave_base)) - return PTR_ERR(pcie->config_axi_slave_base); - pcie->ob_io_res = res; - - /* map csr resource */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "csr_axi_slave"); - pcie->csr_axi_slave_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pcie->csr_axi_slave_base)) - return PTR_ERR(pcie->csr_axi_slave_base); - pcie->pcie_reg_base = res->start; - - /* map MSI config resource */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb_csr"); - pcie->apb_csr_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pcie->apb_csr_base)) - return PTR_ERR(pcie->apb_csr_base); - - /* read the number of windows requested */ - if (of_property_read_u32(node, "apio-wins", &pcie->apio_wins)) - pcie->apio_wins = MAX_PIO_WINDOWS; - - if (of_property_read_u32(node, "ppio-wins", &pcie->ppio_wins)) - pcie->ppio_wins = MAX_PIO_WINDOWS; - - pcie->irq = platform_get_irq(pdev, 0); - if (pcie->irq <= 0) { - dev_err(dev, "failed to map IRQ: %d\n", pcie->irq); - return -ENODEV; - } - - irq_set_chained_handler_and_data(pcie->irq, mobiveil_pcie_isr, pcie); - - return 0; -} - -/* - * select_paged_register - routine to access paged register of root complex - * - * registers of RC are paged, for this scheme to work - * extracted higher 6 bits of the offset will be written to pg_sel - * field of PAB_CTRL register and rest of the lower 10 bits enabled with - * PAGE_SEL_EN are used as offset of the register. - */ -static void select_paged_register(struct mobiveil_pcie *pcie, u32 offset) -{ - int pab_ctrl_dw, pg_sel; - - /* clear pg_sel field */ - pab_ctrl_dw = csr_readl(pcie, PAB_CTRL); - pab_ctrl_dw = (pab_ctrl_dw & ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT)); - - /* set pg_sel field */ - pg_sel = (offset >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK; - pab_ctrl_dw |= ((pg_sel << PAGE_SEL_SHIFT)); - csr_writel(pcie, pab_ctrl_dw, PAB_CTRL); -} - -static void write_paged_register(struct mobiveil_pcie *pcie, - u32 val, u32 offset) -{ - u32 off = (offset & PAGE_LO_MASK) | PAGE_SEL_EN; - - select_paged_register(pcie, offset); - csr_writel(pcie, val, off); -} - -static u32 read_paged_register(struct mobiveil_pcie *pcie, u32 offset) -{ - u32 off = (offset & PAGE_LO_MASK) | PAGE_SEL_EN; - - select_paged_register(pcie, offset); - return csr_readl(pcie, off); -} - -static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, - int pci_addr, u32 type, u64 size) -{ - int pio_ctrl_val; - int amap_ctrl_dw; - u64 size64 = ~(size - 1); - - if ((pcie->ib_wins_configured + 1) > pcie->ppio_wins) { - dev_err(&pcie->pdev->dev, - "ERROR: max inbound windows reached !\n"); - return; - } - - pio_ctrl_val = csr_readl(pcie, PAB_PEX_PIO_CTRL); - csr_writel(pcie, - pio_ctrl_val | (1 << PIO_ENABLE_SHIFT), PAB_PEX_PIO_CTRL); - amap_ctrl_dw = read_paged_register(pcie, PAB_PEX_AMAP_CTRL(win_num)); - amap_ctrl_dw = (amap_ctrl_dw | (type << AMAP_CTRL_TYPE_SHIFT)); - amap_ctrl_dw = (amap_ctrl_dw | (1 << AMAP_CTRL_EN_SHIFT)); - - write_paged_register(pcie, amap_ctrl_dw | lower_32_bits(size64), - PAB_PEX_AMAP_CTRL(win_num)); - - write_paged_register(pcie, upper_32_bits(size64), - PAB_EXT_PEX_AMAP_SIZEN(win_num)); - - write_paged_register(pcie, pci_addr, PAB_PEX_AMAP_AXI_WIN(win_num)); - write_paged_register(pcie, pci_addr, PAB_PEX_AMAP_PEX_WIN_L(win_num)); - write_paged_register(pcie, 0, PAB_PEX_AMAP_PEX_WIN_H(win_num)); -} - -/* - * routine to program the outbound windows - */ -static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, - u64 cpu_addr, u64 pci_addr, u32 config_io_bit, u64 size) -{ - - u32 value, type; - u64 size64 = ~(size - 1); - - if ((pcie->ob_wins_configured + 1) > pcie->apio_wins) { - dev_err(&pcie->pdev->dev, - "ERROR: max outbound windows reached !\n"); - return; - } - - /* - * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit - * to 4 KB in PAB_AXI_AMAP_CTRL register - */ - type = config_io_bit; - value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); - csr_writel(pcie, 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | - lower_32_bits(size64), PAB_AXI_AMAP_CTRL(win_num)); - - write_paged_register(pcie, upper_32_bits(size64), - PAB_EXT_AXI_AMAP_SIZE(win_num)); - - /* - * program AXI window base with appropriate value in - * PAB_AXI_AMAP_AXI_WIN0 register - */ - value = csr_readl(pcie, PAB_AXI_AMAP_AXI_WIN(win_num)); - csr_writel(pcie, cpu_addr & (~AXI_WINDOW_ALIGN_MASK), - PAB_AXI_AMAP_AXI_WIN(win_num)); - - value = csr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_H(win_num)); - - csr_writel(pcie, lower_32_bits(pci_addr), - PAB_AXI_AMAP_PEX_WIN_L(win_num)); - csr_writel(pcie, upper_32_bits(pci_addr), - PAB_AXI_AMAP_PEX_WIN_H(win_num)); - - pcie->ob_wins_configured++; -} - -static int mobiveil_bringup_link(struct mobiveil_pcie *pcie) -{ - int retries; - - /* check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (mobiveil_pcie_link_up(pcie)) - return 0; - - usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); - } - dev_err(&pcie->pdev->dev, "link never came up\n"); - return -ETIMEDOUT; -} - -static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie) -{ - phys_addr_t msg_addr = pcie->pcie_reg_base; - struct mobiveil_msi *msi = &pcie->msi; - - pcie->msi.num_of_vectors = PCI_NUM_MSI; - msi->msi_pages_phys = (phys_addr_t)msg_addr; - - writel_relaxed(lower_32_bits(msg_addr), - pcie->apb_csr_base + MSI_BASE_LO_OFFSET); - writel_relaxed(upper_32_bits(msg_addr), - pcie->apb_csr_base + MSI_BASE_HI_OFFSET); - writel_relaxed(4096, pcie->apb_csr_base + MSI_SIZE_OFFSET); - writel_relaxed(1, pcie->apb_csr_base + MSI_ENABLE_OFFSET); -} - -static int mobiveil_host_init(struct mobiveil_pcie *pcie) -{ - u32 value, pab_ctrl, type = 0; - int err; - struct resource_entry *win, *tmp; - - err = mobiveil_bringup_link(pcie); - if (err) { - dev_info(&pcie->pdev->dev, "link bring-up failed\n"); - return err; - } - - /* - * program Bus Master Enable Bit in Command Register in PAB Config - * Space - */ - value = csr_readl(pcie, PCI_COMMAND); - csr_writel(pcie, value | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER, PCI_COMMAND); - - /* - * program PIO Enable Bit to 1 (and PEX PIO Enable to 1) in PAB_CTRL - * register - */ - pab_ctrl = csr_readl(pcie, PAB_CTRL); - csr_writel(pcie, pab_ctrl | (1 << AMBA_PIO_ENABLE_SHIFT) | - (1 << PEX_PIO_ENABLE_SHIFT), PAB_CTRL); - - csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK), - PAB_INTP_AMBA_MISC_ENB); - - /* - * program PIO Enable Bit to 1 and Config Window Enable Bit to 1 in - * PAB_AXI_PIO_CTRL Register - */ - value = csr_readl(pcie, PAB_AXI_PIO_CTRL); - csr_writel(pcie, value | APIO_EN_MASK, PAB_AXI_PIO_CTRL); - - /* - * we'll program one outbound window for config reads and - * another default inbound window for all the upstream traffic - * rest of the outbound windows will be configured according to - * the "ranges" field defined in device tree - */ - - /* config outbound translation window */ - program_ob_windows(pcie, pcie->ob_wins_configured, - pcie->ob_io_res->start, 0, CFG_WINDOW_TYPE, - resource_size(pcie->ob_io_res)); - - /* memory inbound translation window */ - program_ib_windows(pcie, WIN_NUM_1, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE); - - /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry_safe(win, tmp, &pcie->resources) { - type = 0; - if (resource_type(win->res) == IORESOURCE_MEM) - type = MEM_WINDOW_TYPE; - if (resource_type(win->res) == IORESOURCE_IO) - type = IO_WINDOW_TYPE; - if (type) { - /* configure outbound translation window */ - program_ob_windows(pcie, pcie->ob_wins_configured, - win->res->start, 0, type, - resource_size(win->res)); - } - } - - /* setup MSI hardware registers */ - mobiveil_pcie_enable_msi(pcie); - - return err; -} - -static void mobiveil_mask_intx_irq(struct irq_data *data) -{ - struct irq_desc *desc = irq_to_desc(data->irq); - struct mobiveil_pcie *pcie; - unsigned long flags; - u32 mask, shifted_val; - - pcie = irq_desc_get_chip_data(desc); - mask = 1 << ((data->hwirq + PAB_INTX_START) - 1); - raw_spin_lock_irqsave(&pcie->intx_mask_lock, flags); - shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); - csr_writel(pcie, (shifted_val & (~mask)), PAB_INTP_AMBA_MISC_ENB); - raw_spin_unlock_irqrestore(&pcie->intx_mask_lock, flags); -} - -static void mobiveil_unmask_intx_irq(struct irq_data *data) -{ - struct irq_desc *desc = irq_to_desc(data->irq); - struct mobiveil_pcie *pcie; - unsigned long flags; - u32 shifted_val, mask; - - pcie = irq_desc_get_chip_data(desc); - mask = 1 << ((data->hwirq + PAB_INTX_START) - 1); - raw_spin_lock_irqsave(&pcie->intx_mask_lock, flags); - shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); - csr_writel(pcie, (shifted_val | mask), PAB_INTP_AMBA_MISC_ENB); - raw_spin_unlock_irqrestore(&pcie->intx_mask_lock, flags); -} - -static struct irq_chip intx_irq_chip = { - .name = "mobiveil_pcie:intx", - .irq_enable = mobiveil_unmask_intx_irq, - .irq_disable = mobiveil_mask_intx_irq, - .irq_mask = mobiveil_mask_intx_irq, - .irq_unmask = mobiveil_unmask_intx_irq, -}; - -/* routine to setup the INTx related data */ -static int mobiveil_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &intx_irq_chip, handle_level_irq); - irq_set_chip_data(irq, domain->host_data); - return 0; -} - -/* INTx domain operations structure */ -static const struct irq_domain_ops intx_domain_ops = { - .map = mobiveil_pcie_intx_map, -}; - -static struct irq_chip mobiveil_msi_irq_chip = { - .name = "Mobiveil PCIe MSI", - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; - -static struct msi_domain_info mobiveil_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), - .chip = &mobiveil_msi_irq_chip, -}; - -static void mobiveil_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(data); - phys_addr_t addr = pcie->pcie_reg_base + (data->hwirq * sizeof(int)); - - msg->address_lo = lower_32_bits(addr); - msg->address_hi = upper_32_bits(addr); - msg->data = data->hwirq; - - dev_dbg(&pcie->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n", - (int)data->hwirq, msg->address_hi, msg->address_lo); -} - -static int mobiveil_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) -{ - return -EINVAL; -} - -static struct irq_chip mobiveil_msi_bottom_irq_chip = { - .name = "Mobiveil MSI", - .irq_compose_msi_msg = mobiveil_compose_msi_msg, - .irq_set_affinity = mobiveil_msi_set_affinity, -}; - -static int mobiveil_irq_msi_domain_alloc(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs, void *args) -{ - struct mobiveil_pcie *pcie = domain->host_data; - struct mobiveil_msi *msi = &pcie->msi; - unsigned long bit; - - WARN_ON(nr_irqs != 1); - mutex_lock(&msi->lock); - - bit = find_first_zero_bit(msi->msi_irq_in_use, msi->num_of_vectors); - if (bit >= msi->num_of_vectors) { - mutex_unlock(&msi->lock); - return -ENOSPC; - } - - set_bit(bit, msi->msi_irq_in_use); - - mutex_unlock(&msi->lock); - - irq_domain_set_info(domain, virq, bit, &mobiveil_msi_bottom_irq_chip, - domain->host_data, handle_level_irq, - NULL, NULL); - return 0; -} - -static void mobiveil_irq_msi_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) -{ - struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(d); - struct mobiveil_msi *msi = &pcie->msi; - - mutex_lock(&msi->lock); - - if (!test_bit(d->hwirq, msi->msi_irq_in_use)) { - dev_err(&pcie->pdev->dev, "trying to free unused MSI#%lu\n", - d->hwirq); - } else { - __clear_bit(d->hwirq, msi->msi_irq_in_use); - } - - mutex_unlock(&msi->lock); -} -static const struct irq_domain_ops msi_domain_ops = { - .alloc = mobiveil_irq_msi_domain_alloc, - .free = mobiveil_irq_msi_domain_free, -}; - -static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); - struct mobiveil_msi *msi = &pcie->msi; - - mutex_init(&pcie->msi.lock); - msi->dev_domain = irq_domain_add_linear(NULL, msi->num_of_vectors, - &msi_domain_ops, pcie); - if (!msi->dev_domain) { - dev_err(dev, "failed to create IRQ domain\n"); - return -ENOMEM; - } - - msi->msi_domain = pci_msi_create_irq_domain(fwnode, - &mobiveil_msi_domain_info, msi->dev_domain); - if (!msi->msi_domain) { - dev_err(dev, "failed to create MSI domain\n"); - irq_domain_remove(msi->dev_domain); - return -ENOMEM; - } - return 0; -} - -static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - struct device_node *node = dev->of_node; - int ret; - - /* setup INTx */ - pcie->intx_domain = irq_domain_add_linear(node, - PCI_NUM_INTX, &intx_domain_ops, pcie); - - if (!pcie->intx_domain) { - dev_err(dev, "Failed to get a INTx IRQ domain\n"); - return -ENODEV; - } - - raw_spin_lock_init(&pcie->intx_mask_lock); - - /* setup MSI */ - ret = mobiveil_allocate_msi_domains(pcie); - if (ret) - return ret; - - return 0; -} - -static int mobiveil_pcie_probe(struct platform_device *pdev) -{ - struct mobiveil_pcie *pcie; - struct pci_bus *bus; - struct pci_bus *child; - struct pci_host_bridge *bridge; - struct device *dev = &pdev->dev; - resource_size_t iobase; - int ret; - - /* allocate the PCIe port */ - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); - if (!bridge) - return -ENODEV; - - pcie = pci_host_bridge_priv(bridge); - if (!pcie) - return -ENOMEM; - - pcie->pdev = pdev; - - ret = mobiveil_pcie_parse_dt(pcie); - if (ret) { - dev_err(dev, "Parsing DT failed, ret: %x\n", ret); - return ret; - } - - INIT_LIST_HEAD(&pcie->resources); - - /* parse the host bridge base addresses from the device tree file */ - ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &pcie->resources, &iobase); - if (ret) { - dev_err(dev, "Getting bridge resources failed\n"); - return -ENOMEM; - } - - /* - * configure all inbound and outbound windows and prepare the RC for - * config access - */ - ret = mobiveil_host_init(pcie); - if (ret) { - dev_err(dev, "Failed to initialize host\n"); - goto error; - } - - /* fixup for PCIe class register */ - csr_writel(pcie, 0x060402ab, PAB_INTP_AXI_PIO_CLASS); - - /* initialize the IRQ domains */ - ret = mobiveil_pcie_init_irq_domain(pcie); - if (ret) { - dev_err(dev, "Failed creating IRQ Domain\n"); - goto error; - } - - ret = devm_request_pci_bus_resources(dev, &pcie->resources); - if (ret) - goto error; - - /* Initialize bridge */ - list_splice_init(&pcie->resources, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = pcie; - bridge->busnr = pcie->root_bus_nr; - bridge->ops = &mobiveil_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - /* setup the kernel resources for the newly added PCIe root bus */ - ret = pci_scan_root_bus_bridge(bridge); - if (ret) - goto error; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - - return 0; -error: - pci_free_resource_list(&pcie->resources); - return ret; -} - -static const struct of_device_id mobiveil_pcie_of_match[] = { - {.compatible = "mbvl,gpex40-pcie",}, - {}, -}; - -MODULE_DEVICE_TABLE(of, mobiveil_pcie_of_match); - -static struct platform_driver mobiveil_pcie_driver = { - .probe = mobiveil_pcie_probe, - .driver = { - .name = "mobiveil-pcie", - .of_match_table = mobiveil_pcie_of_match, - .suppress_bind_attrs = true, - }, -}; - -builtin_platform_driver(mobiveil_pcie_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Mobiveil PCIe host controller driver"); -MODULE_AUTHOR("Subrahmanya Lingappa "); diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c deleted file mode 100644 index 874d75c9ee4a..000000000000 --- a/drivers/pci/host/pcie-rcar.c +++ /dev/null @@ -1,1222 +0,0 @@ -// 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 - -#include "../pci.h" - -#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 CFINIT 1 -#define PCIETSTR 0x02004 -#define DATA_LINK_ACTIVE 1 -#define PCIEERRFR 0x02020 -#define UNSUPPORTED_REQUEST BIT(4) -#define PCIEMSIFR 0x02044 -#define PCIEMSIALR 0x02048 -#define MSIFE 1 -#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 SPEED_CHANGE BIT(24) -#define SCRAMBLE_DISABLE BIT(27) -#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; -}; - -static void rcar_pci_write_reg(struct rcar_pcie *pcie, unsigned long val, - unsigned long reg) -{ - writel(val, pcie->base + reg); -} - -static unsigned long rcar_pci_read_reg(struct rcar_pcie *pcie, - unsigned long 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) -{ - int shift = 8 * (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) -{ - int shift = 8 * (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) -{ - 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 >> (8 * (where & 3))) & 0xff; - else if (size == 2) - *val = (*val >> (8 * (where & 2))) & 0xffff; - - dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08lx\n", - bus->number, devfn, where, size, (unsigned long)*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; - int shift, ret; - u32 data; - - 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%08lx\n", - bus->number, devfn, where, size, (unsigned long)val); - - if (size == 1) { - shift = 8 * (where & 3); - data &= ~(0xff << shift); - data |= ((val & 0xff) << shift); - } else if (size == 2) { - shift = 8 * (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 *res) -{ - /* Setup PCIe address space mappings for each resource */ - resource_size_t size; - resource_size_t res_start; - 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); - else - res_start = res->start; - - 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, res); - 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 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, unsigned int addr, - unsigned int lane, unsigned int data) -{ - unsigned long 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); - - /* 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; - - return phy_power_on(pcie->phy); -} - -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 irq; - - /* clear the interrupt */ - rcar_pci_write_reg(pcie, 1 << index, PCIEMSIFR); - - irq = irq_find_mapping(msi->domain, index); - if (irq) { - if (test_bit(index, msi->used)) - generic_handle_irq(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 int rcar_pcie_enable_msi(struct rcar_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct rcar_msi *msi = &pcie->msi; - unsigned long base; - 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_err(dev, "failed to request IRQ: %d\n", err); - goto err; - } - - /* setup MSI data target */ - msi->pages = __get_free_pages(GFP_KERNEL, 0); - base = virt_to_phys((void *)msi->pages); - - rcar_pci_write_reg(pcie, base | MSIFE, PCIEMSIALR); - rcar_pci_write_reg(pcie, 0, PCIEMSIAUR); - - /* enable all MSI interrupts */ - rcar_pci_write_reg(pcie, 0xffffffff, PCIEMSIIER); - - return 0; - -err: - rcar_pcie_unmap_msi(pcie); - return err; -} - -static void rcar_pcie_teardown_msi(struct rcar_pcie *pcie) -{ - struct rcar_msi *msi = &pcie->msi; - - /* Disable all MSI interrupts */ - rcar_pci_write_reg(pcie, 0, PCIEMSIIER); - - /* Disable address decoding of the MSI interrupt, MSIFE */ - rcar_pci_write_reg(pcie, 0, PCIEMSIALR); - - free_pages(msi->pages, 0); - - rcar_pcie_unmap_msi(pcie); -} - -static int rcar_pcie_get_resources(struct rcar_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct resource res; - int err, i; - - pcie->phy = devm_phy_optional_get(dev, "pcie"); - if (IS_ERR(pcie->phy)) - return PTR_ERR(pcie->phy); - - err = of_address_to_resource(dev->of_node, 0, &res); - if (err) - return err; - - pcie->base = devm_ioremap_resource(dev, &res); - if (IS_ERR(pcie->base)) - return PTR_ERR(pcie->base); - - pcie->bus_clk = devm_clk_get(dev, "pcie_bus"); - if (IS_ERR(pcie->bus_clk)) { - dev_err(dev, "cannot get pcie bus clock\n"); - return PTR_ERR(pcie->bus_clk); - } - - i = irq_of_parse_and_map(dev->of_node, 0); - if (!i) { - dev_err(dev, "cannot get platform resources for msi interrupt\n"); - err = -ENOENT; - goto err_irq1; - } - pcie->msi.irq1 = i; - - i = irq_of_parse_and_map(dev->of_node, 1); - if (!i) { - dev_err(dev, "cannot get platform resources for msi interrupt\n"); - err = -ENOENT; - goto err_irq2; - } - pcie->msi.irq2 = i; - - return 0; - -err_irq2: - irq_dispose_mapping(pcie->msi.irq1); -err_irq1: - return err; -} - -static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie, - struct of_pci_range *range, - int *index) -{ - u64 restype = range->flags; - u64 cpu_addr = range->cpu_addr; - u64 cpu_end = range->cpu_addr + range->size; - u64 pci_addr = range->pci_addr; - u32 flags = LAM_64BIT | LAR_ENABLE; - u64 mask; - u64 size; - int idx = *index; - - if (restype & IORESOURCE_PREFETCH) - flags |= LAM_PREFETCH; - - /* - * If the size of the range is larger than the alignment of the start - * address, we have to use multiple entries to perform the mapping. - */ - if (cpu_addr > 0) { - unsigned long nr_zeros = __ffs64(cpu_addr); - u64 alignment = 1ULL << nr_zeros; - - size = min(range->size, alignment); - } else { - size = range->size; - } - /* Hardware supports max 4GiB inbound region */ - size = min(size, 1ULL << 32); - - mask = roundup_pow_of_two(size) - 1; - mask &= ~0xf; - - while (cpu_addr < cpu_end) { - /* - * Set up 64-bit inbound regions as the range parser doesn't - * distinguish between 32 and 64-bit types. - */ - rcar_pci_write_reg(pcie, lower_32_bits(pci_addr), - PCIEPRAR(idx)); - rcar_pci_write_reg(pcie, lower_32_bits(cpu_addr), PCIELAR(idx)); - rcar_pci_write_reg(pcie, lower_32_bits(mask) | flags, - PCIELAMR(idx)); - - rcar_pci_write_reg(pcie, upper_32_bits(pci_addr), - PCIEPRAR(idx + 1)); - rcar_pci_write_reg(pcie, upper_32_bits(cpu_addr), - PCIELAR(idx + 1)); - rcar_pci_write_reg(pcie, 0, PCIELAMR(idx + 1)); - - pci_addr += size; - cpu_addr += size; - idx += 2; - - if (idx > MAX_NR_INBOUND_MAPS) { - dev_err(pcie->dev, "Failed to map inbound regions!\n"); - return -EINVAL; - } - } - *index = idx; - - return 0; -} - -static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie, - struct device_node *np) -{ - struct of_pci_range range; - struct of_pci_range_parser parser; - int index = 0; - int err; - - if (of_pci_dma_range_parser_init(&parser, np)) - return -EINVAL; - - /* Get the dma-ranges from DT */ - for_each_of_pci_range(&parser, &range) { - u64 end = range.cpu_addr + range.size - 1; - - dev_dbg(pcie->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", - range.flags, range.cpu_addr, end, range.pci_addr); - - err = rcar_pcie_inbound_ranges(pcie, &range, &index); - if (err) - return err; - } - - return 0; -} - -static const struct of_device_id rcar_pcie_of_match[] = { - { .compatible = "renesas,pcie-r8a7779", - .data = rcar_pcie_phy_init_h1 }, - { .compatible = "renesas,pcie-r8a7790", - .data = rcar_pcie_phy_init_gen2 }, - { .compatible = "renesas,pcie-r8a7791", - .data = rcar_pcie_phy_init_gen2 }, - { .compatible = "renesas,pcie-rcar-gen2", - .data = rcar_pcie_phy_init_gen2 }, - { .compatible = "renesas,pcie-r8a7795", - .data = rcar_pcie_phy_init_gen3 }, - { .compatible = "renesas,pcie-rcar-gen3", - .data = rcar_pcie_phy_init_gen3 }, - {}, -}; - -static int rcar_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rcar_pcie *pcie; - unsigned int data; - int err; - int (*phy_init_fn)(struct rcar_pcie *); - struct pci_host_bridge *bridge; - - bridge = pci_alloc_host_bridge(sizeof(*pcie)); - if (!bridge) - return -ENOMEM; - - pcie = pci_host_bridge_priv(bridge); - - pcie->dev = dev; - - err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, NULL); - if (err) - goto err_free_bridge; - - pm_runtime_enable(pcie->dev); - err = pm_runtime_get_sync(pcie->dev); - if (err < 0) { - dev_err(pcie->dev, "pm_runtime_get_sync failed\n"); - goto err_pm_disable; - } - - err = rcar_pcie_get_resources(pcie); - if (err < 0) { - dev_err(dev, "failed to request resources: %d\n", err); - goto err_pm_put; - } - - err = clk_prepare_enable(pcie->bus_clk); - if (err) { - dev_err(dev, "failed to enable bus clock: %d\n", err); - goto err_unmap_msi_irqs; - } - - err = rcar_pcie_parse_map_dma_ranges(pcie, dev->of_node); - if (err) - goto err_clk_disable; - - phy_init_fn = of_device_get_match_data(dev); - err = phy_init_fn(pcie); - if (err) { - dev_err(dev, "failed to init PCIe PHY\n"); - goto err_clk_disable; - } - - /* Failure to get a link might just be that no cards are inserted */ - if (rcar_pcie_hw_init(pcie)) { - dev_info(dev, "PCIe link down\n"); - err = -ENODEV; - goto err_clk_disable; - } - - data = rcar_pci_read_reg(pcie, MACSR); - dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - err = rcar_pcie_enable_msi(pcie); - if (err < 0) { - dev_err(dev, - "failed to enable MSI support: %d\n", - err); - goto err_clk_disable; - } - } - - err = rcar_pcie_enable(pcie); - if (err) - goto err_msi_teardown; - - return 0; - -err_msi_teardown: - if (IS_ENABLED(CONFIG_PCI_MSI)) - rcar_pcie_teardown_msi(pcie); - -err_clk_disable: - clk_disable_unprepare(pcie->bus_clk); - -err_unmap_msi_irqs: - irq_dispose_mapping(pcie->msi.irq2); - irq_dispose_mapping(pcie->msi.irq1); - -err_pm_put: - pm_runtime_put(dev); - -err_pm_disable: - pm_runtime_disable(dev); - pci_free_resource_list(&pcie->resources); - -err_free_bridge: - pci_free_host_bridge(bridge); - - return err; -} - -static struct platform_driver rcar_pcie_driver = { - .driver = { - .name = "rcar-pcie", - .of_match_table = rcar_pcie_of_match, - .suppress_bind_attrs = true, - }, - .probe = rcar_pcie_probe, -}; -builtin_platform_driver(rcar_pcie_driver); diff --git a/drivers/pci/host/pcie-rockchip-ep.c b/drivers/pci/host/pcie-rockchip-ep.c deleted file mode 100644 index fc267a49a932..000000000000 --- a/drivers/pci/host/pcie-rockchip-ep.c +++ /dev/null @@ -1,642 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Rockchip AXI PCIe endpoint controller driver - * - * Copyright (c) 2018 Rockchip, Inc. - * - * Author: Shawn Lin - * Simon Xue - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pcie-rockchip.h" - -/** - * struct rockchip_pcie_ep - private data for PCIe endpoint controller driver - * @rockchip: Rockchip PCIe controller - * @max_regions: maximum number of regions supported by hardware - * @ob_region_map: bitmask of mapped outbound regions - * @ob_addr: base addresses in the AXI bus where the outbound regions start - * @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ - * dedicated outbound regions is mapped. - * @irq_cpu_addr: base address in the CPU space where a write access triggers - * the sending of a memory write (MSI) / normal message (legacy - * IRQ) TLP through the PCIe bus. - * @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ - * dedicated outbound region. - * @irq_pci_fn: the latest PCI function that has updated the mapping of - * the MSI/legacy IRQ dedicated outbound region. - * @irq_pending: bitmask of asserted legacy IRQs. - */ -struct rockchip_pcie_ep { - struct rockchip_pcie rockchip; - struct pci_epc *epc; - u32 max_regions; - unsigned long ob_region_map; - phys_addr_t *ob_addr; - phys_addr_t irq_phys_addr; - void __iomem *irq_cpu_addr; - u64 irq_pci_addr; - u8 irq_pci_fn; - u8 irq_pending; -}; - -static void rockchip_pcie_clear_ep_ob_atu(struct rockchip_pcie *rockchip, - u32 region) -{ - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(region)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(region)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_DESC0(region)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_DESC1(region)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(region)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(region)); -} - -static void rockchip_pcie_prog_ep_ob_atu(struct rockchip_pcie *rockchip, u8 fn, - u32 r, u32 type, u64 cpu_addr, - u64 pci_addr, size_t size) -{ - u64 sz = 1ULL << fls64(size - 1); - int num_pass_bits = ilog2(sz); - u32 addr0, addr1, desc0, desc1; - bool is_nor_msg = (type == AXI_WRAPPER_NOR_MSG); - - /* The minimal region size is 1MB */ - if (num_pass_bits < 8) - num_pass_bits = 8; - - cpu_addr -= rockchip->mem_res->start; - addr0 = ((is_nor_msg ? 0x10 : (num_pass_bits - 1)) & - PCIE_CORE_OB_REGION_ADDR0_NUM_BITS) | - (lower_32_bits(cpu_addr) & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); - addr1 = upper_32_bits(is_nor_msg ? cpu_addr : pci_addr); - desc0 = ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN(fn) | type; - desc1 = 0; - - if (is_nor_msg) { - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r)); - rockchip_pcie_write(rockchip, desc0, - ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r)); - rockchip_pcie_write(rockchip, desc1, - ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r)); - } else { - /* PCI bus address region */ - rockchip_pcie_write(rockchip, addr0, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r)); - rockchip_pcie_write(rockchip, addr1, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r)); - rockchip_pcie_write(rockchip, desc0, - ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r)); - rockchip_pcie_write(rockchip, desc1, - ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r)); - - addr0 = - ((num_pass_bits - 1) & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS) | - (lower_32_bits(cpu_addr) & - PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); - addr1 = upper_32_bits(cpu_addr); - } - - /* CPU bus address region */ - rockchip_pcie_write(rockchip, addr0, - ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(r)); - rockchip_pcie_write(rockchip, addr1, - ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(r)); -} - -static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, - struct pci_epf_header *hdr) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - struct rockchip_pcie *rockchip = &ep->rockchip; - - /* All functions share the same vendor ID with function 0 */ - if (fn == 0) { - u32 vid_regs = (hdr->vendorid & GENMASK(15, 0)) | - (hdr->subsys_vendor_id & GENMASK(31, 16)) << 16; - - rockchip_pcie_write(rockchip, vid_regs, - PCIE_CORE_CONFIG_VENDOR); - } - - rockchip_pcie_write(rockchip, hdr->deviceid << 16, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + PCI_VENDOR_ID); - - rockchip_pcie_write(rockchip, - hdr->revid | - hdr->progif_code << 8 | - hdr->subclass_code << 16 | - hdr->baseclass_code << 24, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + PCI_REVISION_ID); - rockchip_pcie_write(rockchip, hdr->cache_line_size, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - PCI_CACHE_LINE_SIZE); - rockchip_pcie_write(rockchip, hdr->subsys_id << 16, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - PCI_SUBSYSTEM_VENDOR_ID); - rockchip_pcie_write(rockchip, hdr->interrupt_pin << 8, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - PCI_INTERRUPT_LINE); - - return 0; -} - -static int rockchip_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, - struct pci_epf_bar *epf_bar) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - struct rockchip_pcie *rockchip = &ep->rockchip; - dma_addr_t bar_phys = epf_bar->phys_addr; - enum pci_barno bar = epf_bar->barno; - int flags = epf_bar->flags; - u32 addr0, addr1, reg, cfg, b, aperture, ctrl; - u64 sz; - - /* BAR size is 2^(aperture + 7) */ - sz = max_t(size_t, epf_bar->size, MIN_EP_APERTURE); - - /* - * roundup_pow_of_two() returns an unsigned long, which is not suited - * for 64bit values. - */ - sz = 1ULL << fls64(sz - 1); - aperture = ilog2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */ - - if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { - ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_IO_32BITS; - } else { - bool is_prefetch = !!(flags & PCI_BASE_ADDRESS_MEM_PREFETCH); - bool is_64bits = sz > SZ_2G; - - if (is_64bits && (bar & 1)) - return -EINVAL; - - if (is_64bits && is_prefetch) - ctrl = - ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; - else if (is_prefetch) - ctrl = - ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_32BITS; - else if (is_64bits) - ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_64BITS; - else - ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_32BITS; - } - - if (bar < BAR_4) { - reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0(fn); - b = bar; - } else { - reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG1(fn); - b = bar - BAR_4; - } - - addr0 = lower_32_bits(bar_phys); - addr1 = upper_32_bits(bar_phys); - - cfg = rockchip_pcie_read(rockchip, reg); - cfg &= ~(ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | - ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); - cfg |= (ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) | - ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl)); - - rockchip_pcie_write(rockchip, cfg, reg); - rockchip_pcie_write(rockchip, addr0, - ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar)); - rockchip_pcie_write(rockchip, addr1, - ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar)); - - return 0; -} - -static void rockchip_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, - struct pci_epf_bar *epf_bar) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - struct rockchip_pcie *rockchip = &ep->rockchip; - u32 reg, cfg, b, ctrl; - enum pci_barno bar = epf_bar->barno; - - if (bar < BAR_4) { - reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0(fn); - b = bar; - } else { - reg = ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG1(fn); - b = bar - BAR_4; - } - - ctrl = ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_DISABLED; - cfg = rockchip_pcie_read(rockchip, reg); - cfg &= ~(ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | - ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); - cfg |= ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl); - - rockchip_pcie_write(rockchip, cfg, reg); - rockchip_pcie_write(rockchip, 0x0, - ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar)); - rockchip_pcie_write(rockchip, 0x0, - ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar)); -} - -static int rockchip_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, - phys_addr_t addr, u64 pci_addr, - size_t size) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - struct rockchip_pcie *pcie = &ep->rockchip; - u32 r; - - r = find_first_zero_bit(&ep->ob_region_map, - sizeof(ep->ob_region_map) * BITS_PER_LONG); - /* - * Region 0 is reserved for configuration space and shouldn't - * be used elsewhere per TRM, so leave it out. - */ - if (r >= ep->max_regions - 1) { - dev_err(&epc->dev, "no free outbound region\n"); - return -EINVAL; - } - - rockchip_pcie_prog_ep_ob_atu(pcie, fn, r, AXI_WRAPPER_MEM_WRITE, addr, - pci_addr, size); - - set_bit(r, &ep->ob_region_map); - ep->ob_addr[r] = addr; - - return 0; -} - -static void rockchip_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, - phys_addr_t addr) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - struct rockchip_pcie *rockchip = &ep->rockchip; - u32 r; - - for (r = 0; r < ep->max_regions - 1; r++) - if (ep->ob_addr[r] == addr) - break; - - /* - * Region 0 is reserved for configuration space and shouldn't - * be used elsewhere per TRM, so leave it out. - */ - if (r == ep->max_regions - 1) - return; - - rockchip_pcie_clear_ep_ob_atu(rockchip, r); - - ep->ob_addr[r] = 0; - clear_bit(r, &ep->ob_region_map); -} - -static int rockchip_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, - u8 multi_msg_cap) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - struct rockchip_pcie *rockchip = &ep->rockchip; - u16 flags; - - flags = rockchip_pcie_read(rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_MSI_CTRL_REG); - flags &= ~ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_MASK; - flags |= - ((multi_msg_cap << 1) << ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET) | - PCI_MSI_FLAGS_64BIT; - flags &= ~ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP; - rockchip_pcie_write(rockchip, flags, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_MSI_CTRL_REG); - return 0; -} - -static int rockchip_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - struct rockchip_pcie *rockchip = &ep->rockchip; - u16 flags; - - flags = rockchip_pcie_read(rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_MSI_CTRL_REG); - if (!(flags & ROCKCHIP_PCIE_EP_MSI_CTRL_ME)) - return -EINVAL; - - return ((flags & ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK) >> - ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET); -} - -static void rockchip_pcie_ep_assert_intx(struct rockchip_pcie_ep *ep, u8 fn, - u8 intx, bool is_asserted) -{ - struct rockchip_pcie *rockchip = &ep->rockchip; - u32 r = ep->max_regions - 1; - u32 offset; - u16 status; - u8 msg_code; - - if (unlikely(ep->irq_pci_addr != ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR || - ep->irq_pci_fn != fn)) { - rockchip_pcie_prog_ep_ob_atu(rockchip, fn, r, - AXI_WRAPPER_NOR_MSG, - ep->irq_phys_addr, 0, 0); - ep->irq_pci_addr = ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR; - ep->irq_pci_fn = fn; - } - - intx &= 3; - if (is_asserted) { - ep->irq_pending |= BIT(intx); - msg_code = ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTA + intx; - } else { - ep->irq_pending &= ~BIT(intx); - msg_code = ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTA + intx; - } - - status = rockchip_pcie_read(rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_CMD_STATUS); - status &= ROCKCHIP_PCIE_EP_CMD_STATUS_IS; - - if ((status != 0) ^ (ep->irq_pending != 0)) { - status ^= ROCKCHIP_PCIE_EP_CMD_STATUS_IS; - rockchip_pcie_write(rockchip, status, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_CMD_STATUS); - } - - offset = - ROCKCHIP_PCIE_MSG_ROUTING(ROCKCHIP_PCIE_MSG_ROUTING_LOCAL_INTX) | - ROCKCHIP_PCIE_MSG_CODE(msg_code) | ROCKCHIP_PCIE_MSG_NO_DATA; - writel(0, ep->irq_cpu_addr + offset); -} - -static int rockchip_pcie_ep_send_legacy_irq(struct rockchip_pcie_ep *ep, u8 fn, - u8 intx) -{ - u16 cmd; - - cmd = rockchip_pcie_read(&ep->rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_CMD_STATUS); - - if (cmd & PCI_COMMAND_INTX_DISABLE) - return -EINVAL; - - /* - * Should add some delay between toggling INTx per TRM vaguely saying - * it depends on some cycles of the AHB bus clock to function it. So - * add sufficient 1ms here. - */ - rockchip_pcie_ep_assert_intx(ep, fn, intx, true); - mdelay(1); - rockchip_pcie_ep_assert_intx(ep, fn, intx, false); - return 0; -} - -static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, - u8 interrupt_num) -{ - struct rockchip_pcie *rockchip = &ep->rockchip; - u16 flags, mme, data, data_mask; - u8 msi_count; - u64 pci_addr, pci_addr_mask = 0xff; - - /* Check MSI enable bit */ - flags = rockchip_pcie_read(&ep->rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_MSI_CTRL_REG); - if (!(flags & ROCKCHIP_PCIE_EP_MSI_CTRL_ME)) - return -EINVAL; - - /* Get MSI numbers from MME */ - mme = ((flags & ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK) >> - ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET); - msi_count = 1 << mme; - if (!interrupt_num || interrupt_num > msi_count) - return -EINVAL; - - /* Set MSI private data */ - data_mask = msi_count - 1; - data = rockchip_pcie_read(rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_MSI_CTRL_REG + - PCI_MSI_DATA_64); - data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask); - - /* Get MSI PCI address */ - pci_addr = rockchip_pcie_read(rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_MSI_CTRL_REG + - PCI_MSI_ADDRESS_HI); - pci_addr <<= 32; - pci_addr |= rockchip_pcie_read(rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_MSI_CTRL_REG + - PCI_MSI_ADDRESS_LO); - pci_addr &= GENMASK_ULL(63, 2); - - /* Set the outbound region if needed. */ - if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) || - ep->irq_pci_fn != fn)) { - rockchip_pcie_prog_ep_ob_atu(rockchip, fn, ep->max_regions - 1, - AXI_WRAPPER_MEM_WRITE, - ep->irq_phys_addr, - pci_addr & ~pci_addr_mask, - pci_addr_mask + 1); - ep->irq_pci_addr = (pci_addr & ~pci_addr_mask); - ep->irq_pci_fn = fn; - } - - writew(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask)); - return 0; -} - -static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, - enum pci_epc_irq_type type, - u8 interrupt_num) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - - switch (type) { - case PCI_EPC_IRQ_LEGACY: - return rockchip_pcie_ep_send_legacy_irq(ep, fn, 0); - case PCI_EPC_IRQ_MSI: - return rockchip_pcie_ep_send_msi_irq(ep, fn, interrupt_num); - default: - return -EINVAL; - } -} - -static int rockchip_pcie_ep_start(struct pci_epc *epc) -{ - struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); - struct rockchip_pcie *rockchip = &ep->rockchip; - struct pci_epf *epf; - u32 cfg; - - cfg = BIT(0); - list_for_each_entry(epf, &epc->pci_epf, list) - cfg |= BIT(epf->func_no); - - rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG); - - list_for_each_entry(epf, &epc->pci_epf, list) - pci_epf_linkup(epf); - - return 0; -} - -static const struct pci_epc_ops rockchip_pcie_epc_ops = { - .write_header = rockchip_pcie_ep_write_header, - .set_bar = rockchip_pcie_ep_set_bar, - .clear_bar = rockchip_pcie_ep_clear_bar, - .map_addr = rockchip_pcie_ep_map_addr, - .unmap_addr = rockchip_pcie_ep_unmap_addr, - .set_msi = rockchip_pcie_ep_set_msi, - .get_msi = rockchip_pcie_ep_get_msi, - .raise_irq = rockchip_pcie_ep_raise_irq, - .start = rockchip_pcie_ep_start, -}; - -static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip, - struct rockchip_pcie_ep *ep) -{ - struct device *dev = rockchip->dev; - int err; - - err = rockchip_pcie_parse_dt(rockchip); - if (err) - return err; - - err = rockchip_pcie_get_phys(rockchip); - if (err) - return err; - - err = of_property_read_u32(dev->of_node, - "rockchip,max-outbound-regions", - &ep->max_regions); - if (err < 0 || ep->max_regions > MAX_REGION_LIMIT) - ep->max_regions = MAX_REGION_LIMIT; - - err = of_property_read_u8(dev->of_node, "max-functions", - &ep->epc->max_functions); - if (err < 0) - ep->epc->max_functions = 1; - - return 0; -} - -static const struct of_device_id rockchip_pcie_ep_of_match[] = { - { .compatible = "rockchip,rk3399-pcie-ep"}, - {}, -}; - -static int rockchip_pcie_ep_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rockchip_pcie_ep *ep; - struct rockchip_pcie *rockchip; - struct pci_epc *epc; - size_t max_regions; - int err; - - ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); - if (!ep) - return -ENOMEM; - - rockchip = &ep->rockchip; - rockchip->is_rc = false; - rockchip->dev = dev; - - epc = devm_pci_epc_create(dev, &rockchip_pcie_epc_ops); - if (IS_ERR(epc)) { - dev_err(dev, "failed to create epc device\n"); - return PTR_ERR(epc); - } - - ep->epc = epc; - epc_set_drvdata(epc, ep); - - err = rockchip_pcie_parse_ep_dt(rockchip, ep); - if (err) - return err; - - err = rockchip_pcie_enable_clocks(rockchip); - if (err) - return err; - - err = rockchip_pcie_init_port(rockchip); - if (err) - goto err_disable_clocks; - - /* Establish the link automatically */ - rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_ENABLE, - PCIE_CLIENT_CONFIG); - - max_regions = ep->max_regions; - ep->ob_addr = devm_kzalloc(dev, max_regions * sizeof(*ep->ob_addr), - GFP_KERNEL); - - if (!ep->ob_addr) { - err = -ENOMEM; - goto err_uninit_port; - } - - /* Only enable function 0 by default */ - rockchip_pcie_write(rockchip, BIT(0), PCIE_CORE_PHY_FUNC_CFG); - - err = pci_epc_mem_init(epc, rockchip->mem_res->start, - resource_size(rockchip->mem_res)); - if (err < 0) { - dev_err(dev, "failed to initialize the memory space\n"); - goto err_uninit_port; - } - - ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr, - SZ_128K); - if (!ep->irq_cpu_addr) { - dev_err(dev, "failed to reserve memory space for MSI\n"); - err = -ENOMEM; - goto err_epc_mem_exit; - } - - ep->irq_pci_addr = ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR; - - return 0; -err_epc_mem_exit: - pci_epc_mem_exit(epc); -err_uninit_port: - rockchip_pcie_deinit_phys(rockchip); -err_disable_clocks: - rockchip_pcie_disable_clocks(rockchip); - return err; -} - -static struct platform_driver rockchip_pcie_ep_driver = { - .driver = { - .name = "rockchip-pcie-ep", - .of_match_table = rockchip_pcie_ep_of_match, - }, - .probe = rockchip_pcie_ep_probe, -}; - -builtin_platform_driver(rockchip_pcie_ep_driver); diff --git a/drivers/pci/host/pcie-rockchip-host.c b/drivers/pci/host/pcie-rockchip-host.c deleted file mode 100644 index 1372d270764f..000000000000 --- a/drivers/pci/host/pcie-rockchip-host.c +++ /dev/null @@ -1,1142 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Rockchip AXI PCIe host controller driver - * - * Copyright (c) 2016 Rockchip, Inc. - * - * Author: Shawn Lin - * Wenrui Li - * - * Bits taken from Synopsys DesignWare Host controller driver and - * ARM PCI Host generic driver. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" -#include "pcie-rockchip.h" - -static void rockchip_pcie_enable_bw_int(struct rockchip_pcie *rockchip) -{ - u32 status; - - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); - status |= (PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE); - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); -} - -static void rockchip_pcie_clr_bw_int(struct rockchip_pcie *rockchip) -{ - u32 status; - - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); - status |= (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS) << 16; - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); -} - -static void rockchip_pcie_update_txcredit_mui(struct rockchip_pcie *rockchip) -{ - u32 val; - - /* Update Tx credit maximum update interval */ - val = rockchip_pcie_read(rockchip, PCIE_CORE_TXCREDIT_CFG1); - val &= ~PCIE_CORE_TXCREDIT_CFG1_MUI_MASK; - val |= PCIE_CORE_TXCREDIT_CFG1_MUI_ENCODE(24000); /* ns */ - rockchip_pcie_write(rockchip, val, PCIE_CORE_TXCREDIT_CFG1); -} - -static int rockchip_pcie_valid_device(struct rockchip_pcie *rockchip, - struct pci_bus *bus, int dev) -{ - /* access only one slot on each root port */ - if (bus->number == rockchip->root_bus_nr && dev > 0) - return 0; - - /* - * do not read more than one device on the bus directly attached - * to RC's downstream side. - */ - if (bus->primary == rockchip->root_bus_nr && dev > 0) - return 0; - - return 1; -} - -static u8 rockchip_pcie_lane_map(struct rockchip_pcie *rockchip) -{ - u32 val; - u8 map; - - if (rockchip->legacy_phy) - return GENMASK(MAX_LANE_NUM - 1, 0); - - val = rockchip_pcie_read(rockchip, PCIE_CORE_LANE_MAP); - map = val & PCIE_CORE_LANE_MAP_MASK; - - /* The link may be using a reverse-indexed mapping. */ - if (val & PCIE_CORE_LANE_MAP_REVERSE) - map = bitrev8(map) >> 4; - - return map; -} - -static int rockchip_pcie_rd_own_conf(struct rockchip_pcie *rockchip, - int where, int size, u32 *val) -{ - void __iomem *addr; - - addr = rockchip->apb_base + PCIE_RC_CONFIG_NORMAL_BASE + 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 rockchip_pcie_wr_own_conf(struct rockchip_pcie *rockchip, - int where, int size, u32 val) -{ - u32 mask, tmp, offset; - void __iomem *addr; - - offset = where & ~0x3; - addr = rockchip->apb_base + PCIE_RC_CONFIG_NORMAL_BASE + offset; - - if (size == 4) { - writel(val, addr); - return PCIBIOS_SUCCESSFUL; - } - - mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); - - /* - * N.B. This read/modify/write isn't safe in general because it can - * corrupt RW1C bits in adjacent registers. But the hardware - * doesn't support smaller writes. - */ - tmp = readl(addr) & mask; - tmp |= val << ((where & 0x3) * 8); - writel(tmp, addr); - - return PCIBIOS_SUCCESSFUL; -} - -static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip, - struct pci_bus *bus, u32 devfn, - int where, int size, u32 *val) -{ - u32 busdev; - - busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn), - PCI_FUNC(devfn), where); - - if (!IS_ALIGNED(busdev, size)) { - *val = 0; - return PCIBIOS_BAD_REGISTER_NUMBER; - } - - if (bus->parent->number == rockchip->root_bus_nr) - rockchip_pcie_cfg_configuration_accesses(rockchip, - AXI_WRAPPER_TYPE0_CFG); - else - rockchip_pcie_cfg_configuration_accesses(rockchip, - AXI_WRAPPER_TYPE1_CFG); - - if (size == 4) { - *val = readl(rockchip->reg_base + busdev); - } else if (size == 2) { - *val = readw(rockchip->reg_base + busdev); - } else if (size == 1) { - *val = readb(rockchip->reg_base + busdev); - } else { - *val = 0; - return PCIBIOS_BAD_REGISTER_NUMBER; - } - return PCIBIOS_SUCCESSFUL; -} - -static int rockchip_pcie_wr_other_conf(struct rockchip_pcie *rockchip, - struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) -{ - u32 busdev; - - busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn), - PCI_FUNC(devfn), where); - if (!IS_ALIGNED(busdev, size)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - if (bus->parent->number == rockchip->root_bus_nr) - rockchip_pcie_cfg_configuration_accesses(rockchip, - AXI_WRAPPER_TYPE0_CFG); - else - rockchip_pcie_cfg_configuration_accesses(rockchip, - AXI_WRAPPER_TYPE1_CFG); - - if (size == 4) - writel(val, rockchip->reg_base + busdev); - else if (size == 2) - writew(val, rockchip->reg_base + busdev); - else if (size == 1) - writeb(val, rockchip->reg_base + busdev); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - return PCIBIOS_SUCCESSFUL; -} - -static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) -{ - struct rockchip_pcie *rockchip = bus->sysdata; - - if (!rockchip_pcie_valid_device(rockchip, bus, PCI_SLOT(devfn))) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - if (bus->number == rockchip->root_bus_nr) - return rockchip_pcie_rd_own_conf(rockchip, where, size, val); - - return rockchip_pcie_rd_other_conf(rockchip, bus, devfn, where, size, - val); -} - -static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) -{ - struct rockchip_pcie *rockchip = bus->sysdata; - - if (!rockchip_pcie_valid_device(rockchip, bus, PCI_SLOT(devfn))) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (bus->number == rockchip->root_bus_nr) - return rockchip_pcie_wr_own_conf(rockchip, where, size, val); - - return rockchip_pcie_wr_other_conf(rockchip, bus, devfn, where, size, - val); -} - -static struct pci_ops rockchip_pcie_ops = { - .read = rockchip_pcie_rd_conf, - .write = rockchip_pcie_wr_conf, -}; - -static void rockchip_pcie_set_power_limit(struct rockchip_pcie *rockchip) -{ - int curr; - u32 status, scale, power; - - if (IS_ERR(rockchip->vpcie3v3)) - return; - - /* - * Set RC's captured slot power limit and scale if - * vpcie3v3 available. The default values are both zero - * which means the software should set these two according - * to the actual power supply. - */ - curr = regulator_get_current_limit(rockchip->vpcie3v3); - if (curr <= 0) - return; - - scale = 3; /* 0.001x */ - curr = curr / 1000; /* convert to mA */ - power = (curr * 3300) / 1000; /* milliwatt */ - while (power > PCIE_RC_CONFIG_DCR_CSPL_LIMIT) { - if (!scale) { - dev_warn(rockchip->dev, "invalid power supply\n"); - return; - } - scale--; - power = power / 10; - } - - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_DCR); - status |= (power << PCIE_RC_CONFIG_DCR_CSPL_SHIFT) | - (scale << PCIE_RC_CONFIG_DCR_CPLS_SHIFT); - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_DCR); -} - -/** - * rockchip_pcie_host_init_port - Initialize hardware - * @rockchip: PCIe port information - */ -static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - int err, i = MAX_LANE_NUM; - u32 status; - - gpiod_set_value_cansleep(rockchip->ep_gpio, 0); - - err = rockchip_pcie_init_port(rockchip); - if (err) - return err; - - /* Fix the transmitted FTS count desired to exit from L0s. */ - status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL_PLC1); - status = (status & ~PCIE_CORE_CTRL_PLC1_FTS_MASK) | - (PCIE_CORE_CTRL_PLC1_FTS_CNT << PCIE_CORE_CTRL_PLC1_FTS_SHIFT); - rockchip_pcie_write(rockchip, status, PCIE_CORE_CTRL_PLC1); - - rockchip_pcie_set_power_limit(rockchip); - - /* Set RC's clock architecture as common clock */ - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); - status |= PCI_EXP_LNKSTA_SLC << 16; - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); - - /* Set RC's RCB to 128 */ - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); - status |= PCI_EXP_LNKCTL_RCB; - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); - - /* Enable Gen1 training */ - rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_ENABLE, - PCIE_CLIENT_CONFIG); - - gpiod_set_value_cansleep(rockchip->ep_gpio, 1); - - /* 500ms timeout value should be enough for Gen1/2 training */ - err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1, - status, PCIE_LINK_UP(status), 20, - 500 * USEC_PER_MSEC); - if (err) { - dev_err(dev, "PCIe link training gen1 timeout!\n"); - goto err_power_off_phy; - } - - if (rockchip->link_gen == 2) { - /* - * Enable retrain for gen2. This should be configured only after - * gen1 finished. - */ - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS); - status |= PCI_EXP_LNKCTL_RL; - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); - - err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL, - status, PCIE_LINK_IS_GEN2(status), 20, - 500 * USEC_PER_MSEC); - if (err) - dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n"); - } - - /* Check the final link width from negotiated lane counter from MGMT */ - status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL); - status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >> - PCIE_CORE_PL_CONF_LANE_SHIFT); - dev_dbg(dev, "current link width is x%d\n", status); - - /* Power off unused lane(s) */ - rockchip->lanes_map = rockchip_pcie_lane_map(rockchip); - for (i = 0; i < MAX_LANE_NUM; i++) { - if (!(rockchip->lanes_map & BIT(i))) { - dev_dbg(dev, "idling lane %d\n", i); - phy_power_off(rockchip->phys[i]); - } - } - - rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID, - PCIE_CORE_CONFIG_VENDOR); - rockchip_pcie_write(rockchip, - PCI_CLASS_BRIDGE_PCI << PCIE_RC_CONFIG_SCC_SHIFT, - PCIE_RC_CONFIG_RID_CCR); - - /* Clear THP cap's next cap pointer to remove L1 substate cap */ - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_THP_CAP); - status &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK; - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_THP_CAP); - - /* Clear L0s from RC's link cap */ - if (of_property_read_bool(dev->of_node, "aspm-no-l0s")) { - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LINK_CAP); - status &= ~PCIE_RC_CONFIG_LINK_CAP_L0S; - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LINK_CAP); - } - - status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_DCSR); - status &= ~PCIE_RC_CONFIG_DCSR_MPS_MASK; - status |= PCIE_RC_CONFIG_DCSR_MPS_256; - rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_DCSR); - - return 0; -err_power_off_phy: - while (i--) - phy_power_off(rockchip->phys[i]); - i = MAX_LANE_NUM; - while (i--) - phy_exit(rockchip->phys[i]); - return err; -} - -static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg) -{ - struct rockchip_pcie *rockchip = arg; - struct device *dev = rockchip->dev; - u32 reg; - u32 sub_reg; - - reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS); - if (reg & PCIE_CLIENT_INT_LOCAL) { - dev_dbg(dev, "local interrupt received\n"); - sub_reg = rockchip_pcie_read(rockchip, PCIE_CORE_INT_STATUS); - if (sub_reg & PCIE_CORE_INT_PRFPE) - dev_dbg(dev, "parity error detected while reading from the PNP receive FIFO RAM\n"); - - if (sub_reg & PCIE_CORE_INT_CRFPE) - dev_dbg(dev, "parity error detected while reading from the Completion Receive FIFO RAM\n"); - - if (sub_reg & PCIE_CORE_INT_RRPE) - dev_dbg(dev, "parity error detected while reading from replay buffer RAM\n"); - - if (sub_reg & PCIE_CORE_INT_PRFO) - dev_dbg(dev, "overflow occurred in the PNP receive FIFO\n"); - - if (sub_reg & PCIE_CORE_INT_CRFO) - dev_dbg(dev, "overflow occurred in the completion receive FIFO\n"); - - if (sub_reg & PCIE_CORE_INT_RT) - dev_dbg(dev, "replay timer timed out\n"); - - if (sub_reg & PCIE_CORE_INT_RTR) - dev_dbg(dev, "replay timer rolled over after 4 transmissions of the same TLP\n"); - - if (sub_reg & PCIE_CORE_INT_PE) - dev_dbg(dev, "phy error detected on receive side\n"); - - if (sub_reg & PCIE_CORE_INT_MTR) - dev_dbg(dev, "malformed TLP received from the link\n"); - - if (sub_reg & PCIE_CORE_INT_UCR) - dev_dbg(dev, "malformed TLP received from the link\n"); - - if (sub_reg & PCIE_CORE_INT_FCE) - dev_dbg(dev, "an error was observed in the flow control advertisements from the other side\n"); - - if (sub_reg & PCIE_CORE_INT_CT) - dev_dbg(dev, "a request timed out waiting for completion\n"); - - if (sub_reg & PCIE_CORE_INT_UTC) - dev_dbg(dev, "unmapped TC error\n"); - - if (sub_reg & PCIE_CORE_INT_MMVC) - dev_dbg(dev, "MSI mask register changes\n"); - - rockchip_pcie_write(rockchip, sub_reg, PCIE_CORE_INT_STATUS); - } else if (reg & PCIE_CLIENT_INT_PHY) { - dev_dbg(dev, "phy link changes\n"); - rockchip_pcie_update_txcredit_mui(rockchip); - rockchip_pcie_clr_bw_int(rockchip); - } - - rockchip_pcie_write(rockchip, reg & PCIE_CLIENT_INT_LOCAL, - PCIE_CLIENT_INT_STATUS); - - return IRQ_HANDLED; -} - -static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg) -{ - struct rockchip_pcie *rockchip = arg; - struct device *dev = rockchip->dev; - u32 reg; - - reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS); - if (reg & PCIE_CLIENT_INT_LEGACY_DONE) - dev_dbg(dev, "legacy done interrupt received\n"); - - if (reg & PCIE_CLIENT_INT_MSG) - dev_dbg(dev, "message done interrupt received\n"); - - if (reg & PCIE_CLIENT_INT_HOT_RST) - dev_dbg(dev, "hot reset interrupt received\n"); - - if (reg & PCIE_CLIENT_INT_DPA) - dev_dbg(dev, "dpa interrupt received\n"); - - if (reg & PCIE_CLIENT_INT_FATAL_ERR) - dev_dbg(dev, "fatal error interrupt received\n"); - - if (reg & PCIE_CLIENT_INT_NFATAL_ERR) - dev_dbg(dev, "no fatal error interrupt received\n"); - - if (reg & PCIE_CLIENT_INT_CORR_ERR) - dev_dbg(dev, "correctable error interrupt received\n"); - - if (reg & PCIE_CLIENT_INT_PHY) - dev_dbg(dev, "phy interrupt received\n"); - - rockchip_pcie_write(rockchip, reg & (PCIE_CLIENT_INT_LEGACY_DONE | - PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_HOT_RST | - PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_FATAL_ERR | - PCIE_CLIENT_INT_NFATAL_ERR | - PCIE_CLIENT_INT_CORR_ERR | - PCIE_CLIENT_INT_PHY), - PCIE_CLIENT_INT_STATUS); - - return IRQ_HANDLED; -} - -static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc); - struct device *dev = rockchip->dev; - u32 reg; - u32 hwirq; - u32 virq; - - chained_irq_enter(chip, desc); - - reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS); - reg = (reg & PCIE_CLIENT_INTR_MASK) >> PCIE_CLIENT_INTR_SHIFT; - - while (reg) { - hwirq = ffs(reg) - 1; - reg &= ~BIT(hwirq); - - virq = irq_find_mapping(rockchip->irq_domain, hwirq); - if (virq) - generic_handle_irq(virq); - else - dev_err(dev, "unexpected IRQ, INT%d\n", hwirq); - } - - chained_irq_exit(chip, desc); -} - -static int rockchip_pcie_setup_irq(struct rockchip_pcie *rockchip) -{ - int irq, err; - struct device *dev = rockchip->dev; - struct platform_device *pdev = to_platform_device(dev); - - irq = platform_get_irq_byname(pdev, "sys"); - if (irq < 0) { - dev_err(dev, "missing sys IRQ resource\n"); - return irq; - } - - err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler, - IRQF_SHARED, "pcie-sys", rockchip); - if (err) { - dev_err(dev, "failed to request PCIe subsystem IRQ\n"); - return err; - } - - irq = platform_get_irq_byname(pdev, "legacy"); - if (irq < 0) { - dev_err(dev, "missing legacy IRQ resource\n"); - return irq; - } - - irq_set_chained_handler_and_data(irq, - rockchip_pcie_legacy_int_handler, - rockchip); - - irq = platform_get_irq_byname(pdev, "client"); - if (irq < 0) { - dev_err(dev, "missing client IRQ resource\n"); - return irq; - } - - err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler, - IRQF_SHARED, "pcie-client", rockchip); - if (err) { - dev_err(dev, "failed to request PCIe client IRQ\n"); - return err; - } - - return 0; -} - -/** - * rockchip_pcie_parse_host_dt - Parse Device Tree - * @rockchip: PCIe port information - * - * Return: '0' on success and error value on failure - */ -static int rockchip_pcie_parse_host_dt(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - int err; - - err = rockchip_pcie_parse_dt(rockchip); - if (err) - return err; - - err = rockchip_pcie_setup_irq(rockchip); - if (err) - return err; - - rockchip->vpcie12v = devm_regulator_get_optional(dev, "vpcie12v"); - if (IS_ERR(rockchip->vpcie12v)) { - if (PTR_ERR(rockchip->vpcie12v) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_info(dev, "no vpcie12v regulator found\n"); - } - - rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3"); - if (IS_ERR(rockchip->vpcie3v3)) { - if (PTR_ERR(rockchip->vpcie3v3) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_info(dev, "no vpcie3v3 regulator found\n"); - } - - rockchip->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8"); - if (IS_ERR(rockchip->vpcie1v8)) { - if (PTR_ERR(rockchip->vpcie1v8) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_info(dev, "no vpcie1v8 regulator found\n"); - } - - rockchip->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9"); - if (IS_ERR(rockchip->vpcie0v9)) { - if (PTR_ERR(rockchip->vpcie0v9) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_info(dev, "no vpcie0v9 regulator found\n"); - } - - return 0; -} - -static int rockchip_pcie_set_vpcie(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - int err; - - if (!IS_ERR(rockchip->vpcie12v)) { - err = regulator_enable(rockchip->vpcie12v); - if (err) { - dev_err(dev, "fail to enable vpcie12v regulator\n"); - goto err_out; - } - } - - if (!IS_ERR(rockchip->vpcie3v3)) { - err = regulator_enable(rockchip->vpcie3v3); - if (err) { - dev_err(dev, "fail to enable vpcie3v3 regulator\n"); - goto err_disable_12v; - } - } - - if (!IS_ERR(rockchip->vpcie1v8)) { - err = regulator_enable(rockchip->vpcie1v8); - if (err) { - dev_err(dev, "fail to enable vpcie1v8 regulator\n"); - goto err_disable_3v3; - } - } - - if (!IS_ERR(rockchip->vpcie0v9)) { - err = regulator_enable(rockchip->vpcie0v9); - if (err) { - dev_err(dev, "fail to enable vpcie0v9 regulator\n"); - goto err_disable_1v8; - } - } - - return 0; - -err_disable_1v8: - if (!IS_ERR(rockchip->vpcie1v8)) - regulator_disable(rockchip->vpcie1v8); -err_disable_3v3: - if (!IS_ERR(rockchip->vpcie3v3)) - regulator_disable(rockchip->vpcie3v3); -err_disable_12v: - if (!IS_ERR(rockchip->vpcie12v)) - regulator_disable(rockchip->vpcie12v); -err_out: - return err; -} - -static void rockchip_pcie_enable_interrupts(struct rockchip_pcie *rockchip) -{ - rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) & - (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK); - rockchip_pcie_write(rockchip, (u32)(~PCIE_CORE_INT), - PCIE_CORE_INT_MASK); - - rockchip_pcie_enable_bw_int(rockchip); -} - -static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -static const struct irq_domain_ops intx_domain_ops = { - .map = rockchip_pcie_intx_map, -}; - -static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - struct device_node *intc = of_get_next_child(dev->of_node, NULL); - - if (!intc) { - dev_err(dev, "missing child interrupt-controller node\n"); - return -EINVAL; - } - - rockchip->irq_domain = irq_domain_add_linear(intc, PCI_NUM_INTX, - &intx_domain_ops, rockchip); - if (!rockchip->irq_domain) { - dev_err(dev, "failed to get a INTx IRQ domain\n"); - return -EINVAL; - } - - return 0; -} - -static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie *rockchip, - int region_no, int type, u8 num_pass_bits, - u32 lower_addr, u32 upper_addr) -{ - u32 ob_addr_0; - u32 ob_addr_1; - u32 ob_desc_0; - u32 aw_offset; - - if (region_no >= MAX_AXI_WRAPPER_REGION_NUM) - return -EINVAL; - if (num_pass_bits + 1 < 8) - return -EINVAL; - if (num_pass_bits > 63) - return -EINVAL; - if (region_no == 0) { - if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits)) - return -EINVAL; - } - if (region_no != 0) { - if (AXI_REGION_SIZE < (2ULL << num_pass_bits)) - return -EINVAL; - } - - aw_offset = (region_no << OB_REG_SIZE_SHIFT); - - ob_addr_0 = num_pass_bits & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS; - ob_addr_0 |= lower_addr & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR; - ob_addr_1 = upper_addr; - ob_desc_0 = (1 << 23 | type); - - rockchip_pcie_write(rockchip, ob_addr_0, - PCIE_CORE_OB_REGION_ADDR0 + aw_offset); - rockchip_pcie_write(rockchip, ob_addr_1, - PCIE_CORE_OB_REGION_ADDR1 + aw_offset); - rockchip_pcie_write(rockchip, ob_desc_0, - PCIE_CORE_OB_REGION_DESC0 + aw_offset); - rockchip_pcie_write(rockchip, 0, - PCIE_CORE_OB_REGION_DESC1 + aw_offset); - - return 0; -} - -static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie *rockchip, - int region_no, u8 num_pass_bits, - u32 lower_addr, u32 upper_addr) -{ - u32 ib_addr_0; - u32 ib_addr_1; - u32 aw_offset; - - if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM) - return -EINVAL; - if (num_pass_bits + 1 < MIN_AXI_ADDR_BITS_PASSED) - return -EINVAL; - if (num_pass_bits > 63) - return -EINVAL; - - aw_offset = (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT); - - ib_addr_0 = num_pass_bits & PCIE_CORE_IB_REGION_ADDR0_NUM_BITS; - ib_addr_0 |= (lower_addr << 8) & PCIE_CORE_IB_REGION_ADDR0_LO_ADDR; - ib_addr_1 = upper_addr; - - rockchip_pcie_write(rockchip, ib_addr_0, PCIE_RP_IB_ADDR0 + aw_offset); - rockchip_pcie_write(rockchip, ib_addr_1, PCIE_RP_IB_ADDR1 + aw_offset); - - return 0; -} - -static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - int offset; - int err; - int reg_no; - - rockchip_pcie_cfg_configuration_accesses(rockchip, - AXI_WRAPPER_TYPE0_CFG); - - for (reg_no = 0; reg_no < (rockchip->mem_size >> 20); reg_no++) { - err = rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1, - AXI_WRAPPER_MEM_WRITE, - 20 - 1, - rockchip->mem_bus_addr + - (reg_no << 20), - 0); - if (err) { - dev_err(dev, "program RC mem outbound ATU failed\n"); - return err; - } - } - - err = rockchip_pcie_prog_ib_atu(rockchip, 2, 32 - 1, 0x0, 0); - if (err) { - dev_err(dev, "program RC mem inbound ATU failed\n"); - return err; - } - - offset = rockchip->mem_size >> 20; - for (reg_no = 0; reg_no < (rockchip->io_size >> 20); reg_no++) { - err = rockchip_pcie_prog_ob_atu(rockchip, - reg_no + 1 + offset, - AXI_WRAPPER_IO_WRITE, - 20 - 1, - rockchip->io_bus_addr + - (reg_no << 20), - 0); - if (err) { - dev_err(dev, "program RC io outbound ATU failed\n"); - return err; - } - } - - /* assign message regions */ - rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset, - AXI_WRAPPER_NOR_MSG, - 20 - 1, 0, 0); - - rockchip->msg_bus_addr = rockchip->mem_bus_addr + - ((reg_no + offset) << 20); - return err; -} - -static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip) -{ - u32 value; - int err; - - /* send PME_TURN_OFF message */ - writel(0x0, rockchip->msg_region + PCIE_RC_SEND_PME_OFF); - - /* read LTSSM and wait for falling into L2 link state */ - err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0, - value, PCIE_LINK_IS_L2(value), 20, - jiffies_to_usecs(5 * HZ)); - if (err) { - dev_err(rockchip->dev, "PCIe link enter L2 timeout!\n"); - return err; - } - - return 0; -} - -static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) -{ - struct rockchip_pcie *rockchip = dev_get_drvdata(dev); - int ret; - - /* disable core and cli int since we don't need to ack PME_ACK */ - rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) | - PCIE_CLIENT_INT_CLI, PCIE_CLIENT_INT_MASK); - rockchip_pcie_write(rockchip, (u32)PCIE_CORE_INT, PCIE_CORE_INT_MASK); - - ret = rockchip_pcie_wait_l2(rockchip); - if (ret) { - rockchip_pcie_enable_interrupts(rockchip); - return ret; - } - - rockchip_pcie_deinit_phys(rockchip); - - rockchip_pcie_disable_clocks(rockchip); - - if (!IS_ERR(rockchip->vpcie0v9)) - regulator_disable(rockchip->vpcie0v9); - - return ret; -} - -static int __maybe_unused rockchip_pcie_resume_noirq(struct device *dev) -{ - struct rockchip_pcie *rockchip = dev_get_drvdata(dev); - int err; - - if (!IS_ERR(rockchip->vpcie0v9)) { - err = regulator_enable(rockchip->vpcie0v9); - if (err) { - dev_err(dev, "fail to enable vpcie0v9 regulator\n"); - return err; - } - } - - err = rockchip_pcie_enable_clocks(rockchip); - if (err) - goto err_disable_0v9; - - err = rockchip_pcie_host_init_port(rockchip); - if (err) - goto err_pcie_resume; - - err = rockchip_pcie_cfg_atu(rockchip); - if (err) - goto err_err_deinit_port; - - /* Need this to enter L1 again */ - rockchip_pcie_update_txcredit_mui(rockchip); - rockchip_pcie_enable_interrupts(rockchip); - - return 0; - -err_err_deinit_port: - rockchip_pcie_deinit_phys(rockchip); -err_pcie_resume: - rockchip_pcie_disable_clocks(rockchip); -err_disable_0v9: - if (!IS_ERR(rockchip->vpcie0v9)) - regulator_disable(rockchip->vpcie0v9); - return err; -} - -static int rockchip_pcie_probe(struct platform_device *pdev) -{ - struct rockchip_pcie *rockchip; - struct device *dev = &pdev->dev; - struct pci_bus *bus, *child; - struct pci_host_bridge *bridge; - struct resource_entry *win; - resource_size_t io_base; - struct resource *mem; - struct resource *io; - int err; - - LIST_HEAD(res); - - if (!dev->of_node) - return -ENODEV; - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rockchip)); - if (!bridge) - return -ENOMEM; - - rockchip = pci_host_bridge_priv(bridge); - - platform_set_drvdata(pdev, rockchip); - rockchip->dev = dev; - rockchip->is_rc = true; - - err = rockchip_pcie_parse_host_dt(rockchip); - if (err) - return err; - - err = rockchip_pcie_enable_clocks(rockchip); - if (err) - return err; - - err = rockchip_pcie_set_vpcie(rockchip); - if (err) { - dev_err(dev, "failed to set vpcie regulator\n"); - goto err_set_vpcie; - } - - err = rockchip_pcie_host_init_port(rockchip); - if (err) - goto err_vpcie; - - rockchip_pcie_enable_interrupts(rockchip); - - err = rockchip_pcie_init_irq_domain(rockchip); - if (err < 0) - goto err_deinit_port; - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, - &res, &io_base); - if (err) - goto err_remove_irq_domain; - - err = devm_request_pci_bus_resources(dev, &res); - if (err) - goto err_free_res; - - /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry(win, &res) { - switch (resource_type(win->res)) { - case IORESOURCE_IO: - io = win->res; - io->name = "I/O"; - rockchip->io_size = resource_size(io); - rockchip->io_bus_addr = io->start - win->offset; - err = pci_remap_iospace(io, io_base); - if (err) { - dev_warn(dev, "error %d: failed to map resource %pR\n", - err, io); - continue; - } - rockchip->io = io; - break; - case IORESOURCE_MEM: - mem = win->res; - mem->name = "MEM"; - rockchip->mem_size = resource_size(mem); - rockchip->mem_bus_addr = mem->start - win->offset; - break; - case IORESOURCE_BUS: - rockchip->root_bus_nr = win->res->start; - break; - default: - continue; - } - } - - err = rockchip_pcie_cfg_atu(rockchip); - if (err) - goto err_unmap_iospace; - - rockchip->msg_region = devm_ioremap(dev, rockchip->msg_bus_addr, SZ_1M); - if (!rockchip->msg_region) { - err = -ENOMEM; - goto err_unmap_iospace; - } - - list_splice_init(&res, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = rockchip; - bridge->busnr = 0; - bridge->ops = &rockchip_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - err = pci_scan_root_bus_bridge(bridge); - if (err < 0) - goto err_unmap_iospace; - - bus = bridge->bus; - - rockchip->root_bus = 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; - -err_unmap_iospace: - pci_unmap_iospace(rockchip->io); -err_free_res: - pci_free_resource_list(&res); -err_remove_irq_domain: - irq_domain_remove(rockchip->irq_domain); -err_deinit_port: - rockchip_pcie_deinit_phys(rockchip); -err_vpcie: - if (!IS_ERR(rockchip->vpcie12v)) - regulator_disable(rockchip->vpcie12v); - if (!IS_ERR(rockchip->vpcie3v3)) - regulator_disable(rockchip->vpcie3v3); - if (!IS_ERR(rockchip->vpcie1v8)) - regulator_disable(rockchip->vpcie1v8); - if (!IS_ERR(rockchip->vpcie0v9)) - regulator_disable(rockchip->vpcie0v9); -err_set_vpcie: - rockchip_pcie_disable_clocks(rockchip); - return err; -} - -static int rockchip_pcie_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rockchip_pcie *rockchip = dev_get_drvdata(dev); - - pci_stop_root_bus(rockchip->root_bus); - pci_remove_root_bus(rockchip->root_bus); - pci_unmap_iospace(rockchip->io); - irq_domain_remove(rockchip->irq_domain); - - rockchip_pcie_deinit_phys(rockchip); - - rockchip_pcie_disable_clocks(rockchip); - - if (!IS_ERR(rockchip->vpcie12v)) - regulator_disable(rockchip->vpcie12v); - if (!IS_ERR(rockchip->vpcie3v3)) - regulator_disable(rockchip->vpcie3v3); - if (!IS_ERR(rockchip->vpcie1v8)) - regulator_disable(rockchip->vpcie1v8); - if (!IS_ERR(rockchip->vpcie0v9)) - regulator_disable(rockchip->vpcie0v9); - - return 0; -} - -static const struct dev_pm_ops rockchip_pcie_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq, - rockchip_pcie_resume_noirq) -}; - -static const struct of_device_id rockchip_pcie_of_match[] = { - { .compatible = "rockchip,rk3399-pcie", }, - {} -}; -MODULE_DEVICE_TABLE(of, rockchip_pcie_of_match); - -static struct platform_driver rockchip_pcie_driver = { - .driver = { - .name = "rockchip-pcie", - .of_match_table = rockchip_pcie_of_match, - .pm = &rockchip_pcie_pm_ops, - }, - .probe = rockchip_pcie_probe, - .remove = rockchip_pcie_remove, -}; -module_platform_driver(rockchip_pcie_driver); - -MODULE_AUTHOR("Rockchip Inc"); -MODULE_DESCRIPTION("Rockchip AXI PCIe driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c deleted file mode 100644 index c53d1322a3d6..000000000000 --- a/drivers/pci/host/pcie-rockchip.c +++ /dev/null @@ -1,424 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Rockchip AXI PCIe host controller driver - * - * Copyright (c) 2016 Rockchip, Inc. - * - * Author: Shawn Lin - * Wenrui Li - * - * Bits taken from Synopsys DesignWare Host controller driver and - * ARM PCI Host generic driver. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" -#include "pcie-rockchip.h" - -int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - struct platform_device *pdev = to_platform_device(dev); - struct device_node *node = dev->of_node; - struct resource *regs; - int err; - - if (rockchip->is_rc) { - regs = platform_get_resource_byname(pdev, - IORESOURCE_MEM, - "axi-base"); - rockchip->reg_base = devm_pci_remap_cfg_resource(dev, regs); - if (IS_ERR(rockchip->reg_base)) - return PTR_ERR(rockchip->reg_base); - } else { - rockchip->mem_res = - platform_get_resource_byname(pdev, IORESOURCE_MEM, - "mem-base"); - if (!rockchip->mem_res) - return -EINVAL; - } - - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "apb-base"); - rockchip->apb_base = devm_ioremap_resource(dev, regs); - if (IS_ERR(rockchip->apb_base)) - return PTR_ERR(rockchip->apb_base); - - err = rockchip_pcie_get_phys(rockchip); - if (err) - return err; - - rockchip->lanes = 1; - err = of_property_read_u32(node, "num-lanes", &rockchip->lanes); - if (!err && (rockchip->lanes == 0 || - rockchip->lanes == 3 || - rockchip->lanes > 4)) { - dev_warn(dev, "invalid num-lanes, default to use one lane\n"); - rockchip->lanes = 1; - } - - rockchip->link_gen = of_pci_get_max_link_speed(node); - if (rockchip->link_gen < 0 || rockchip->link_gen > 2) - rockchip->link_gen = 2; - - rockchip->core_rst = devm_reset_control_get_exclusive(dev, "core"); - if (IS_ERR(rockchip->core_rst)) { - if (PTR_ERR(rockchip->core_rst) != -EPROBE_DEFER) - dev_err(dev, "missing core reset property in node\n"); - return PTR_ERR(rockchip->core_rst); - } - - rockchip->mgmt_rst = devm_reset_control_get_exclusive(dev, "mgmt"); - if (IS_ERR(rockchip->mgmt_rst)) { - if (PTR_ERR(rockchip->mgmt_rst) != -EPROBE_DEFER) - dev_err(dev, "missing mgmt reset property in node\n"); - return PTR_ERR(rockchip->mgmt_rst); - } - - rockchip->mgmt_sticky_rst = devm_reset_control_get_exclusive(dev, - "mgmt-sticky"); - if (IS_ERR(rockchip->mgmt_sticky_rst)) { - if (PTR_ERR(rockchip->mgmt_sticky_rst) != -EPROBE_DEFER) - dev_err(dev, "missing mgmt-sticky reset property in node\n"); - return PTR_ERR(rockchip->mgmt_sticky_rst); - } - - rockchip->pipe_rst = devm_reset_control_get_exclusive(dev, "pipe"); - if (IS_ERR(rockchip->pipe_rst)) { - if (PTR_ERR(rockchip->pipe_rst) != -EPROBE_DEFER) - dev_err(dev, "missing pipe reset property in node\n"); - return PTR_ERR(rockchip->pipe_rst); - } - - rockchip->pm_rst = devm_reset_control_get_exclusive(dev, "pm"); - if (IS_ERR(rockchip->pm_rst)) { - if (PTR_ERR(rockchip->pm_rst) != -EPROBE_DEFER) - dev_err(dev, "missing pm reset property in node\n"); - return PTR_ERR(rockchip->pm_rst); - } - - rockchip->pclk_rst = devm_reset_control_get_exclusive(dev, "pclk"); - if (IS_ERR(rockchip->pclk_rst)) { - if (PTR_ERR(rockchip->pclk_rst) != -EPROBE_DEFER) - dev_err(dev, "missing pclk reset property in node\n"); - return PTR_ERR(rockchip->pclk_rst); - } - - rockchip->aclk_rst = devm_reset_control_get_exclusive(dev, "aclk"); - if (IS_ERR(rockchip->aclk_rst)) { - if (PTR_ERR(rockchip->aclk_rst) != -EPROBE_DEFER) - dev_err(dev, "missing aclk reset property in node\n"); - return PTR_ERR(rockchip->aclk_rst); - } - - if (rockchip->is_rc) { - rockchip->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH); - if (IS_ERR(rockchip->ep_gpio)) { - dev_err(dev, "missing ep-gpios property in node\n"); - return PTR_ERR(rockchip->ep_gpio); - } - } - - rockchip->aclk_pcie = devm_clk_get(dev, "aclk"); - if (IS_ERR(rockchip->aclk_pcie)) { - dev_err(dev, "aclk clock not found\n"); - return PTR_ERR(rockchip->aclk_pcie); - } - - rockchip->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf"); - if (IS_ERR(rockchip->aclk_perf_pcie)) { - dev_err(dev, "aclk_perf clock not found\n"); - return PTR_ERR(rockchip->aclk_perf_pcie); - } - - rockchip->hclk_pcie = devm_clk_get(dev, "hclk"); - if (IS_ERR(rockchip->hclk_pcie)) { - dev_err(dev, "hclk clock not found\n"); - return PTR_ERR(rockchip->hclk_pcie); - } - - rockchip->clk_pcie_pm = devm_clk_get(dev, "pm"); - if (IS_ERR(rockchip->clk_pcie_pm)) { - dev_err(dev, "pm clock not found\n"); - return PTR_ERR(rockchip->clk_pcie_pm); - } - - return 0; -} -EXPORT_SYMBOL_GPL(rockchip_pcie_parse_dt); - -int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - int err, i; - u32 regs; - - err = reset_control_assert(rockchip->aclk_rst); - if (err) { - dev_err(dev, "assert aclk_rst err %d\n", err); - return err; - } - - err = reset_control_assert(rockchip->pclk_rst); - if (err) { - dev_err(dev, "assert pclk_rst err %d\n", err); - return err; - } - - err = reset_control_assert(rockchip->pm_rst); - if (err) { - dev_err(dev, "assert pm_rst err %d\n", err); - return err; - } - - for (i = 0; i < MAX_LANE_NUM; i++) { - err = phy_init(rockchip->phys[i]); - if (err) { - dev_err(dev, "init phy%d err %d\n", i, err); - goto err_exit_phy; - } - } - - err = reset_control_assert(rockchip->core_rst); - if (err) { - dev_err(dev, "assert core_rst err %d\n", err); - goto err_exit_phy; - } - - err = reset_control_assert(rockchip->mgmt_rst); - if (err) { - dev_err(dev, "assert mgmt_rst err %d\n", err); - goto err_exit_phy; - } - - err = reset_control_assert(rockchip->mgmt_sticky_rst); - if (err) { - dev_err(dev, "assert mgmt_sticky_rst err %d\n", err); - goto err_exit_phy; - } - - err = reset_control_assert(rockchip->pipe_rst); - if (err) { - dev_err(dev, "assert pipe_rst err %d\n", err); - goto err_exit_phy; - } - - udelay(10); - - err = reset_control_deassert(rockchip->pm_rst); - if (err) { - dev_err(dev, "deassert pm_rst err %d\n", err); - goto err_exit_phy; - } - - err = reset_control_deassert(rockchip->aclk_rst); - if (err) { - dev_err(dev, "deassert aclk_rst err %d\n", err); - goto err_exit_phy; - } - - err = reset_control_deassert(rockchip->pclk_rst); - if (err) { - dev_err(dev, "deassert pclk_rst err %d\n", err); - goto err_exit_phy; - } - - if (rockchip->link_gen == 2) - rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_2, - PCIE_CLIENT_CONFIG); - else - rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1, - PCIE_CLIENT_CONFIG); - - regs = PCIE_CLIENT_LINK_TRAIN_ENABLE | PCIE_CLIENT_ARI_ENABLE | - PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes); - - if (rockchip->is_rc) - regs |= PCIE_CLIENT_CONF_ENABLE | PCIE_CLIENT_MODE_RC; - else - regs |= PCIE_CLIENT_CONF_DISABLE | PCIE_CLIENT_MODE_EP; - - rockchip_pcie_write(rockchip, regs, PCIE_CLIENT_CONFIG); - - for (i = 0; i < MAX_LANE_NUM; i++) { - err = phy_power_on(rockchip->phys[i]); - if (err) { - dev_err(dev, "power on phy%d err %d\n", i, err); - goto err_power_off_phy; - } - } - - /* - * Please don't reorder the deassert sequence of the following - * four reset pins. - */ - err = reset_control_deassert(rockchip->mgmt_sticky_rst); - if (err) { - dev_err(dev, "deassert mgmt_sticky_rst err %d\n", err); - goto err_power_off_phy; - } - - err = reset_control_deassert(rockchip->core_rst); - if (err) { - dev_err(dev, "deassert core_rst err %d\n", err); - goto err_power_off_phy; - } - - err = reset_control_deassert(rockchip->mgmt_rst); - if (err) { - dev_err(dev, "deassert mgmt_rst err %d\n", err); - goto err_power_off_phy; - } - - err = reset_control_deassert(rockchip->pipe_rst); - if (err) { - dev_err(dev, "deassert pipe_rst err %d\n", err); - goto err_power_off_phy; - } - - return 0; -err_power_off_phy: - while (i--) - phy_power_off(rockchip->phys[i]); - i = MAX_LANE_NUM; -err_exit_phy: - while (i--) - phy_exit(rockchip->phys[i]); - return err; -} -EXPORT_SYMBOL_GPL(rockchip_pcie_init_port); - -int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - struct phy *phy; - char *name; - u32 i; - - phy = devm_phy_get(dev, "pcie-phy"); - if (!IS_ERR(phy)) { - rockchip->legacy_phy = true; - rockchip->phys[0] = phy; - dev_warn(dev, "legacy phy model is deprecated!\n"); - return 0; - } - - if (PTR_ERR(phy) == -EPROBE_DEFER) - return PTR_ERR(phy); - - dev_dbg(dev, "missing legacy phy; search for per-lane PHY\n"); - - for (i = 0; i < MAX_LANE_NUM; i++) { - name = kasprintf(GFP_KERNEL, "pcie-phy-%u", i); - if (!name) - return -ENOMEM; - - phy = devm_of_phy_get(dev, dev->of_node, name); - kfree(name); - - if (IS_ERR(phy)) { - if (PTR_ERR(phy) != -EPROBE_DEFER) - dev_err(dev, "missing phy for lane %d: %ld\n", - i, PTR_ERR(phy)); - return PTR_ERR(phy); - } - - rockchip->phys[i] = phy; - } - - return 0; -} -EXPORT_SYMBOL_GPL(rockchip_pcie_get_phys); - -void rockchip_pcie_deinit_phys(struct rockchip_pcie *rockchip) -{ - int i; - - for (i = 0; i < MAX_LANE_NUM; i++) { - /* inactive lanes are already powered off */ - if (rockchip->lanes_map & BIT(i)) - phy_power_off(rockchip->phys[i]); - phy_exit(rockchip->phys[i]); - } -} -EXPORT_SYMBOL_GPL(rockchip_pcie_deinit_phys); - -int rockchip_pcie_enable_clocks(struct rockchip_pcie *rockchip) -{ - struct device *dev = rockchip->dev; - int err; - - err = clk_prepare_enable(rockchip->aclk_pcie); - if (err) { - dev_err(dev, "unable to enable aclk_pcie clock\n"); - return err; - } - - err = clk_prepare_enable(rockchip->aclk_perf_pcie); - if (err) { - dev_err(dev, "unable to enable aclk_perf_pcie clock\n"); - goto err_aclk_perf_pcie; - } - - err = clk_prepare_enable(rockchip->hclk_pcie); - if (err) { - dev_err(dev, "unable to enable hclk_pcie clock\n"); - goto err_hclk_pcie; - } - - err = clk_prepare_enable(rockchip->clk_pcie_pm); - if (err) { - dev_err(dev, "unable to enable clk_pcie_pm clock\n"); - goto err_clk_pcie_pm; - } - - return 0; - -err_clk_pcie_pm: - clk_disable_unprepare(rockchip->hclk_pcie); -err_hclk_pcie: - clk_disable_unprepare(rockchip->aclk_perf_pcie); -err_aclk_perf_pcie: - clk_disable_unprepare(rockchip->aclk_pcie); - return err; -} -EXPORT_SYMBOL_GPL(rockchip_pcie_enable_clocks); - -void rockchip_pcie_disable_clocks(void *data) -{ - struct rockchip_pcie *rockchip = data; - - clk_disable_unprepare(rockchip->clk_pcie_pm); - clk_disable_unprepare(rockchip->hclk_pcie); - clk_disable_unprepare(rockchip->aclk_perf_pcie); - clk_disable_unprepare(rockchip->aclk_pcie); -} -EXPORT_SYMBOL_GPL(rockchip_pcie_disable_clocks); - -void rockchip_pcie_cfg_configuration_accesses( - struct rockchip_pcie *rockchip, u32 type) -{ - u32 ob_desc_0; - - /* Configuration Accesses for region 0 */ - rockchip_pcie_write(rockchip, 0x0, PCIE_RC_BAR_CONF); - - rockchip_pcie_write(rockchip, - (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS), - PCIE_CORE_OB_REGION_ADDR0); - rockchip_pcie_write(rockchip, RC_REGION_0_ADDR_TRANS_H, - PCIE_CORE_OB_REGION_ADDR1); - ob_desc_0 = rockchip_pcie_read(rockchip, PCIE_CORE_OB_REGION_DESC0); - ob_desc_0 &= ~(RC_REGION_0_TYPE_MASK); - ob_desc_0 |= (type | (0x1 << 23)); - rockchip_pcie_write(rockchip, ob_desc_0, PCIE_CORE_OB_REGION_DESC0); - rockchip_pcie_write(rockchip, 0x0, PCIE_CORE_OB_REGION_DESC1); -} -EXPORT_SYMBOL_GPL(rockchip_pcie_cfg_configuration_accesses); diff --git a/drivers/pci/host/pcie-rockchip.h b/drivers/pci/host/pcie-rockchip.h deleted file mode 100644 index 8e87a059ce73..000000000000 --- a/drivers/pci/host/pcie-rockchip.h +++ /dev/null @@ -1,338 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Rockchip AXI PCIe controller driver - * - * Copyright (c) 2018 Rockchip, Inc. - * - * Author: Shawn Lin - * - */ - -#ifndef _PCIE_ROCKCHIP_H -#define _PCIE_ROCKCHIP_H - -#include -#include - -/* - * The upper 16 bits of PCIE_CLIENT_CONFIG are a write mask for the lower 16 - * bits. This allows atomic updates of the register without locking. - */ -#define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val)) -#define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val) - -#define ENCODE_LANES(x) ((((x) >> 1) & 3) << 4) -#define MAX_LANE_NUM 4 -#define MAX_REGION_LIMIT 32 -#define MIN_EP_APERTURE 28 - -#define PCIE_CLIENT_BASE 0x0 -#define PCIE_CLIENT_CONFIG (PCIE_CLIENT_BASE + 0x00) -#define PCIE_CLIENT_CONF_ENABLE HIWORD_UPDATE_BIT(0x0001) -#define PCIE_CLIENT_CONF_DISABLE HIWORD_UPDATE(0x0001, 0) -#define PCIE_CLIENT_LINK_TRAIN_ENABLE HIWORD_UPDATE_BIT(0x0002) -#define PCIE_CLIENT_ARI_ENABLE HIWORD_UPDATE_BIT(0x0008) -#define PCIE_CLIENT_CONF_LANE_NUM(x) HIWORD_UPDATE(0x0030, ENCODE_LANES(x)) -#define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040) -#define PCIE_CLIENT_MODE_EP HIWORD_UPDATE(0x0040, 0) -#define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0) -#define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080) -#define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c) -#define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0) -#define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18 -#define PCIE_CLIENT_DEBUG_LTSSM_L2 0x19 -#define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48) -#define PCIE_CLIENT_LINK_STATUS_UP 0x00300000 -#define PCIE_CLIENT_LINK_STATUS_MASK 0x00300000 -#define PCIE_CLIENT_INT_MASK (PCIE_CLIENT_BASE + 0x4c) -#define PCIE_CLIENT_INT_STATUS (PCIE_CLIENT_BASE + 0x50) -#define PCIE_CLIENT_INTR_MASK GENMASK(8, 5) -#define PCIE_CLIENT_INTR_SHIFT 5 -#define PCIE_CLIENT_INT_LEGACY_DONE BIT(15) -#define PCIE_CLIENT_INT_MSG BIT(14) -#define PCIE_CLIENT_INT_HOT_RST BIT(13) -#define PCIE_CLIENT_INT_DPA BIT(12) -#define PCIE_CLIENT_INT_FATAL_ERR BIT(11) -#define PCIE_CLIENT_INT_NFATAL_ERR BIT(10) -#define PCIE_CLIENT_INT_CORR_ERR BIT(9) -#define PCIE_CLIENT_INT_INTD BIT(8) -#define PCIE_CLIENT_INT_INTC BIT(7) -#define PCIE_CLIENT_INT_INTB BIT(6) -#define PCIE_CLIENT_INT_INTA BIT(5) -#define PCIE_CLIENT_INT_LOCAL BIT(4) -#define PCIE_CLIENT_INT_UDMA BIT(3) -#define PCIE_CLIENT_INT_PHY BIT(2) -#define PCIE_CLIENT_INT_HOT_PLUG BIT(1) -#define PCIE_CLIENT_INT_PWR_STCG BIT(0) - -#define PCIE_CLIENT_INT_LEGACY \ - (PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \ - PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD) - -#define PCIE_CLIENT_INT_CLI \ - (PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \ - PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \ - PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \ - PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY | \ - PCIE_CLIENT_INT_PHY) - -#define PCIE_CORE_CTRL_MGMT_BASE 0x900000 -#define PCIE_CORE_CTRL (PCIE_CORE_CTRL_MGMT_BASE + 0x000) -#define PCIE_CORE_PL_CONF_SPEED_5G 0x00000008 -#define PCIE_CORE_PL_CONF_SPEED_MASK 0x00000018 -#define PCIE_CORE_PL_CONF_LANE_MASK 0x00000006 -#define PCIE_CORE_PL_CONF_LANE_SHIFT 1 -#define PCIE_CORE_CTRL_PLC1 (PCIE_CORE_CTRL_MGMT_BASE + 0x004) -#define PCIE_CORE_CTRL_PLC1_FTS_MASK GENMASK(23, 8) -#define PCIE_CORE_CTRL_PLC1_FTS_SHIFT 8 -#define PCIE_CORE_CTRL_PLC1_FTS_CNT 0xffff -#define PCIE_CORE_TXCREDIT_CFG1 (PCIE_CORE_CTRL_MGMT_BASE + 0x020) -#define PCIE_CORE_TXCREDIT_CFG1_MUI_MASK 0xFFFF0000 -#define PCIE_CORE_TXCREDIT_CFG1_MUI_SHIFT 16 -#define PCIE_CORE_TXCREDIT_CFG1_MUI_ENCODE(x) \ - (((x) >> 3) << PCIE_CORE_TXCREDIT_CFG1_MUI_SHIFT) -#define PCIE_CORE_LANE_MAP (PCIE_CORE_CTRL_MGMT_BASE + 0x200) -#define PCIE_CORE_LANE_MAP_MASK 0x0000000f -#define PCIE_CORE_LANE_MAP_REVERSE BIT(16) -#define PCIE_CORE_INT_STATUS (PCIE_CORE_CTRL_MGMT_BASE + 0x20c) -#define PCIE_CORE_INT_PRFPE BIT(0) -#define PCIE_CORE_INT_CRFPE BIT(1) -#define PCIE_CORE_INT_RRPE BIT(2) -#define PCIE_CORE_INT_PRFO BIT(3) -#define PCIE_CORE_INT_CRFO BIT(4) -#define PCIE_CORE_INT_RT BIT(5) -#define PCIE_CORE_INT_RTR BIT(6) -#define PCIE_CORE_INT_PE BIT(7) -#define PCIE_CORE_INT_MTR BIT(8) -#define PCIE_CORE_INT_UCR BIT(9) -#define PCIE_CORE_INT_FCE BIT(10) -#define PCIE_CORE_INT_CT BIT(11) -#define PCIE_CORE_INT_UTC BIT(18) -#define PCIE_CORE_INT_MMVC BIT(19) -#define PCIE_CORE_CONFIG_VENDOR (PCIE_CORE_CTRL_MGMT_BASE + 0x44) -#define PCIE_CORE_INT_MASK (PCIE_CORE_CTRL_MGMT_BASE + 0x210) -#define PCIE_CORE_PHY_FUNC_CFG (PCIE_CORE_CTRL_MGMT_BASE + 0x2c0) -#define PCIE_RC_BAR_CONF (PCIE_CORE_CTRL_MGMT_BASE + 0x300) -#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_DISABLED 0x0 -#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_IO_32BITS 0x1 -#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_32BITS 0x4 -#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5 -#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_MEM_64BITS 0x6 -#define ROCKCHIP_PCIE_CORE_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7 - -#define PCIE_CORE_INT \ - (PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \ - PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \ - PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \ - PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \ - PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \ - PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \ - PCIE_CORE_INT_MMVC) - -#define PCIE_RC_RP_ATS_BASE 0x400000 -#define PCIE_RC_CONFIG_NORMAL_BASE 0x800000 -#define PCIE_RC_CONFIG_BASE 0xa00000 -#define PCIE_RC_CONFIG_RID_CCR (PCIE_RC_CONFIG_BASE + 0x08) -#define PCIE_RC_CONFIG_SCC_SHIFT 16 -#define PCIE_RC_CONFIG_DCR (PCIE_RC_CONFIG_BASE + 0xc4) -#define PCIE_RC_CONFIG_DCR_CSPL_SHIFT 18 -#define PCIE_RC_CONFIG_DCR_CSPL_LIMIT 0xff -#define PCIE_RC_CONFIG_DCR_CPLS_SHIFT 26 -#define PCIE_RC_CONFIG_DCSR (PCIE_RC_CONFIG_BASE + 0xc8) -#define PCIE_RC_CONFIG_DCSR_MPS_MASK GENMASK(7, 5) -#define PCIE_RC_CONFIG_DCSR_MPS_256 (0x1 << 5) -#define PCIE_RC_CONFIG_LINK_CAP (PCIE_RC_CONFIG_BASE + 0xcc) -#define PCIE_RC_CONFIG_LINK_CAP_L0S BIT(10) -#define PCIE_RC_CONFIG_LCS (PCIE_RC_CONFIG_BASE + 0xd0) -#define PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2 (PCIE_RC_CONFIG_BASE + 0x90c) -#define PCIE_RC_CONFIG_THP_CAP (PCIE_RC_CONFIG_BASE + 0x274) -#define PCIE_RC_CONFIG_THP_CAP_NEXT_MASK GENMASK(31, 20) - -#define PCIE_CORE_AXI_CONF_BASE 0xc00000 -#define PCIE_CORE_OB_REGION_ADDR0 (PCIE_CORE_AXI_CONF_BASE + 0x0) -#define PCIE_CORE_OB_REGION_ADDR0_NUM_BITS 0x3f -#define PCIE_CORE_OB_REGION_ADDR0_LO_ADDR 0xffffff00 -#define PCIE_CORE_OB_REGION_ADDR1 (PCIE_CORE_AXI_CONF_BASE + 0x4) -#define PCIE_CORE_OB_REGION_DESC0 (PCIE_CORE_AXI_CONF_BASE + 0x8) -#define PCIE_CORE_OB_REGION_DESC1 (PCIE_CORE_AXI_CONF_BASE + 0xc) - -#define PCIE_CORE_AXI_INBOUND_BASE 0xc00800 -#define PCIE_RP_IB_ADDR0 (PCIE_CORE_AXI_INBOUND_BASE + 0x0) -#define PCIE_CORE_IB_REGION_ADDR0_NUM_BITS 0x3f -#define PCIE_CORE_IB_REGION_ADDR0_LO_ADDR 0xffffff00 -#define PCIE_RP_IB_ADDR1 (PCIE_CORE_AXI_INBOUND_BASE + 0x4) - -/* Size of one AXI Region (not Region 0) */ -#define AXI_REGION_SIZE BIT(20) -/* Size of Region 0, equal to sum of sizes of other regions */ -#define AXI_REGION_0_SIZE (32 * (0x1 << 20)) -#define OB_REG_SIZE_SHIFT 5 -#define IB_ROOT_PORT_REG_SIZE_SHIFT 3 -#define AXI_WRAPPER_IO_WRITE 0x6 -#define AXI_WRAPPER_MEM_WRITE 0x2 -#define AXI_WRAPPER_TYPE0_CFG 0xa -#define AXI_WRAPPER_TYPE1_CFG 0xb -#define AXI_WRAPPER_NOR_MSG 0xc - -#define MAX_AXI_IB_ROOTPORT_REGION_NUM 3 -#define MIN_AXI_ADDR_BITS_PASSED 8 -#define PCIE_RC_SEND_PME_OFF 0x11960 -#define ROCKCHIP_VENDOR_ID 0x1d87 -#define PCIE_ECAM_BUS(x) (((x) & 0xff) << 20) -#define PCIE_ECAM_DEV(x) (((x) & 0x1f) << 15) -#define PCIE_ECAM_FUNC(x) (((x) & 0x7) << 12) -#define PCIE_ECAM_REG(x) (((x) & 0xfff) << 0) -#define PCIE_ECAM_ADDR(bus, dev, func, reg) \ - (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \ - PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg)) -#define PCIE_LINK_IS_L2(x) \ - (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2) -#define PCIE_LINK_UP(x) \ - (((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP) -#define PCIE_LINK_IS_GEN2(x) \ - (((x) & PCIE_CORE_PL_CONF_SPEED_MASK) == PCIE_CORE_PL_CONF_SPEED_5G) - -#define RC_REGION_0_ADDR_TRANS_H 0x00000000 -#define RC_REGION_0_ADDR_TRANS_L 0x00000000 -#define RC_REGION_0_PASS_BITS (25 - 1) -#define RC_REGION_0_TYPE_MASK GENMASK(3, 0) -#define MAX_AXI_WRAPPER_REGION_NUM 33 - -#define ROCKCHIP_PCIE_MSG_ROUTING_TO_RC 0x0 -#define ROCKCHIP_PCIE_MSG_ROUTING_VIA_ADDR 0x1 -#define ROCKCHIP_PCIE_MSG_ROUTING_VIA_ID 0x2 -#define ROCKCHIP_PCIE_MSG_ROUTING_BROADCAST 0x3 -#define ROCKCHIP_PCIE_MSG_ROUTING_LOCAL_INTX 0x4 -#define ROCKCHIP_PCIE_MSG_ROUTING_PME_ACK 0x5 -#define ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTA 0x20 -#define ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTB 0x21 -#define ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTC 0x22 -#define ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTD 0x23 -#define ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTA 0x24 -#define ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTB 0x25 -#define ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTC 0x26 -#define ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTD 0x27 -#define ROCKCHIP_PCIE_MSG_ROUTING_MASK GENMASK(7, 5) -#define ROCKCHIP_PCIE_MSG_ROUTING(route) \ - (((route) << 5) & ROCKCHIP_PCIE_MSG_ROUTING_MASK) -#define ROCKCHIP_PCIE_MSG_CODE_MASK GENMASK(15, 8) -#define ROCKCHIP_PCIE_MSG_CODE(code) \ - (((code) << 8) & ROCKCHIP_PCIE_MSG_CODE_MASK) -#define ROCKCHIP_PCIE_MSG_NO_DATA BIT(16) - -#define ROCKCHIP_PCIE_EP_CMD_STATUS 0x4 -#define ROCKCHIP_PCIE_EP_CMD_STATUS_IS BIT(19) -#define ROCKCHIP_PCIE_EP_MSI_CTRL_REG 0x90 -#define ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET 17 -#define ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_MASK GENMASK(19, 17) -#define ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET 20 -#define ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK GENMASK(22, 20) -#define ROCKCHIP_PCIE_EP_MSI_CTRL_ME BIT(16) -#define ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP BIT(24) -#define ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR 0x1 -#define ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR 0x3 -#define ROCKCHIP_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12)) -#define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ - (PCIE_RC_RP_ATS_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008) -#define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \ - (PCIE_RC_RP_ATS_BASE + 0x0844 + (fn) * 0x0040 + (bar) * 0x0008) -#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r) \ - (PCIE_RC_RP_ATS_BASE + 0x0000 + ((r) & 0x1f) * 0x0020) -#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(19, 12) -#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \ - (((devfn) << 12) & \ - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK) -#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(27, 20) -#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(bus) \ - (((bus) << 20) & ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK) -#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r) \ - (PCIE_RC_RP_ATS_BASE + 0x0004 + ((r) & 0x1f) * 0x0020) -#define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID BIT(23) -#define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK GENMASK(31, 24) -#define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN(devfn) \ - (((devfn) << 24) & ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK) -#define ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r) \ - (PCIE_RC_RP_ATS_BASE + 0x0008 + ((r) & 0x1f) * 0x0020) -#define ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r) \ - (PCIE_RC_RP_ATS_BASE + 0x000c + ((r) & 0x1f) * 0x0020) -#define ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(r) \ - (PCIE_RC_RP_ATS_BASE + 0x0018 + ((r) & 0x1f) * 0x0020) -#define ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(r) \ - (PCIE_RC_RP_ATS_BASE + 0x001c + ((r) & 0x1f) * 0x0020) - -#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0(fn) \ - (PCIE_CORE_CTRL_MGMT_BASE + 0x0240 + (fn) * 0x0008) -#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG1(fn) \ - (PCIE_CORE_CTRL_MGMT_BASE + 0x0244 + (fn) * 0x0008) -#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \ - (GENMASK(4, 0) << ((b) * 8)) -#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \ - (((a) << ((b) * 8)) & \ - ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b)) -#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b) \ - (GENMASK(7, 5) << ((b) * 8)) -#define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL(b, c) \ - (((c) << ((b) * 8 + 5)) & \ - ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)) - -struct rockchip_pcie { - void __iomem *reg_base; /* DT axi-base */ - void __iomem *apb_base; /* DT apb-base */ - bool legacy_phy; - struct phy *phys[MAX_LANE_NUM]; - struct reset_control *core_rst; - struct reset_control *mgmt_rst; - struct reset_control *mgmt_sticky_rst; - struct reset_control *pipe_rst; - struct reset_control *pm_rst; - struct reset_control *aclk_rst; - struct reset_control *pclk_rst; - struct clk *aclk_pcie; - struct clk *aclk_perf_pcie; - struct clk *hclk_pcie; - struct clk *clk_pcie_pm; - struct regulator *vpcie12v; /* 12V power supply */ - struct regulator *vpcie3v3; /* 3.3V power supply */ - struct regulator *vpcie1v8; /* 1.8V power supply */ - struct regulator *vpcie0v9; /* 0.9V power supply */ - struct gpio_desc *ep_gpio; - u32 lanes; - u8 lanes_map; - u8 root_bus_nr; - int link_gen; - struct device *dev; - struct irq_domain *irq_domain; - int offset; - struct pci_bus *root_bus; - struct resource *io; - phys_addr_t io_bus_addr; - u32 io_size; - void __iomem *msg_region; - u32 mem_size; - phys_addr_t msg_bus_addr; - phys_addr_t mem_bus_addr; - bool is_rc; - struct resource *mem_res; -}; - -static u32 rockchip_pcie_read(struct rockchip_pcie *rockchip, u32 reg) -{ - return readl(rockchip->apb_base + reg); -} - -static void rockchip_pcie_write(struct rockchip_pcie *rockchip, u32 val, - u32 reg) -{ - writel(val, rockchip->apb_base + reg); -} - -int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip); -int rockchip_pcie_init_port(struct rockchip_pcie *rockchip); -int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip); -void rockchip_pcie_deinit_phys(struct rockchip_pcie *rockchip); -int rockchip_pcie_enable_clocks(struct rockchip_pcie *rockchip); -void rockchip_pcie_disable_clocks(void *data); -void rockchip_pcie_cfg_configuration_accesses( - struct rockchip_pcie *rockchip, u32 type); - -#endif /* _PCIE_ROCKCHIP_H */ diff --git a/drivers/pci/host/pcie-tango.c b/drivers/pci/host/pcie-tango.c deleted file mode 100644 index 21a208da3f59..000000000000 --- a/drivers/pci/host/pcie-tango.c +++ /dev/null @@ -1,341 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include - -#define MSI_MAX 256 - -#define SMP8759_MUX 0x48 -#define SMP8759_TEST_OUT 0x74 -#define SMP8759_DOORBELL 0x7c -#define SMP8759_STATUS 0x80 -#define SMP8759_ENABLE 0xa0 - -struct tango_pcie { - DECLARE_BITMAP(used_msi, MSI_MAX); - u64 msi_doorbell; - spinlock_t used_msi_lock; - void __iomem *base; - struct irq_domain *dom; -}; - -static void tango_msi_isr(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct tango_pcie *pcie = irq_desc_get_handler_data(desc); - unsigned long status, base, virq, idx, pos = 0; - - chained_irq_enter(chip, desc); - spin_lock(&pcie->used_msi_lock); - - while ((pos = find_next_bit(pcie->used_msi, MSI_MAX, pos)) < MSI_MAX) { - base = round_down(pos, 32); - status = readl_relaxed(pcie->base + SMP8759_STATUS + base / 8); - for_each_set_bit(idx, &status, 32) { - virq = irq_find_mapping(pcie->dom, base + idx); - generic_handle_irq(virq); - } - pos = base + 32; - } - - spin_unlock(&pcie->used_msi_lock); - chained_irq_exit(chip, desc); -} - -static void tango_ack(struct irq_data *d) -{ - struct tango_pcie *pcie = d->chip_data; - u32 offset = (d->hwirq / 32) * 4; - u32 bit = BIT(d->hwirq % 32); - - writel_relaxed(bit, pcie->base + SMP8759_STATUS + offset); -} - -static void update_msi_enable(struct irq_data *d, bool unmask) -{ - unsigned long flags; - struct tango_pcie *pcie = d->chip_data; - u32 offset = (d->hwirq / 32) * 4; - u32 bit = BIT(d->hwirq % 32); - u32 val; - - spin_lock_irqsave(&pcie->used_msi_lock, flags); - val = readl_relaxed(pcie->base + SMP8759_ENABLE + offset); - val = unmask ? val | bit : val & ~bit; - writel_relaxed(val, pcie->base + SMP8759_ENABLE + offset); - spin_unlock_irqrestore(&pcie->used_msi_lock, flags); -} - -static void tango_mask(struct irq_data *d) -{ - update_msi_enable(d, false); -} - -static void tango_unmask(struct irq_data *d) -{ - update_msi_enable(d, true); -} - -static int tango_set_affinity(struct irq_data *d, const struct cpumask *mask, - bool force) -{ - return -EINVAL; -} - -static void tango_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) -{ - struct tango_pcie *pcie = d->chip_data; - msg->address_lo = lower_32_bits(pcie->msi_doorbell); - msg->address_hi = upper_32_bits(pcie->msi_doorbell); - msg->data = d->hwirq; -} - -static struct irq_chip tango_chip = { - .irq_ack = tango_ack, - .irq_mask = tango_mask, - .irq_unmask = tango_unmask, - .irq_set_affinity = tango_set_affinity, - .irq_compose_msi_msg = tango_compose_msi_msg, -}; - -static void msi_ack(struct irq_data *d) -{ - irq_chip_ack_parent(d); -} - -static void msi_mask(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void msi_unmask(struct irq_data *d) -{ - pci_msi_unmask_irq(d); - irq_chip_unmask_parent(d); -} - -static struct irq_chip msi_chip = { - .name = "MSI", - .irq_ack = msi_ack, - .irq_mask = msi_mask, - .irq_unmask = msi_unmask, -}; - -static struct msi_domain_info msi_dom_info = { - .flags = MSI_FLAG_PCI_MSIX - | MSI_FLAG_USE_DEF_DOM_OPS - | MSI_FLAG_USE_DEF_CHIP_OPS, - .chip = &msi_chip, -}; - -static int tango_irq_domain_alloc(struct irq_domain *dom, unsigned int virq, - unsigned int nr_irqs, void *args) -{ - struct tango_pcie *pcie = dom->host_data; - unsigned long flags; - int pos; - - spin_lock_irqsave(&pcie->used_msi_lock, flags); - pos = find_first_zero_bit(pcie->used_msi, MSI_MAX); - if (pos >= MSI_MAX) { - spin_unlock_irqrestore(&pcie->used_msi_lock, flags); - return -ENOSPC; - } - __set_bit(pos, pcie->used_msi); - spin_unlock_irqrestore(&pcie->used_msi_lock, flags); - irq_domain_set_info(dom, virq, pos, &tango_chip, - pcie, handle_edge_irq, NULL, NULL); - - return 0; -} - -static void tango_irq_domain_free(struct irq_domain *dom, unsigned int virq, - unsigned int nr_irqs) -{ - unsigned long flags; - struct irq_data *d = irq_domain_get_irq_data(dom, virq); - struct tango_pcie *pcie = d->chip_data; - - spin_lock_irqsave(&pcie->used_msi_lock, flags); - __clear_bit(d->hwirq, pcie->used_msi); - spin_unlock_irqrestore(&pcie->used_msi_lock, flags); -} - -static const struct irq_domain_ops dom_ops = { - .alloc = tango_irq_domain_alloc, - .free = tango_irq_domain_free, -}; - -static int smp8759_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct pci_config_window *cfg = bus->sysdata; - struct tango_pcie *pcie = dev_get_drvdata(cfg->parent); - int ret; - - /* Reads in configuration space outside devfn 0 return garbage */ - if (devfn != 0) - return PCIBIOS_FUNC_NOT_SUPPORTED; - - /* - * PCI config and MMIO accesses are muxed. Linux doesn't have a - * mutual exclusion mechanism for config vs. MMIO accesses, so - * concurrent accesses may cause corruption. - */ - writel_relaxed(1, pcie->base + SMP8759_MUX); - ret = pci_generic_config_read(bus, devfn, where, size, val); - writel_relaxed(0, pcie->base + SMP8759_MUX); - - return ret; -} - -static int smp8759_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct pci_config_window *cfg = bus->sysdata; - struct tango_pcie *pcie = dev_get_drvdata(cfg->parent); - int ret; - - writel_relaxed(1, pcie->base + SMP8759_MUX); - ret = pci_generic_config_write(bus, devfn, where, size, val); - writel_relaxed(0, pcie->base + SMP8759_MUX); - - return ret; -} - -static struct pci_ecam_ops smp8759_ecam_ops = { - .bus_shift = 20, - .pci_ops = { - .map_bus = pci_ecam_map_bus, - .read = smp8759_config_read, - .write = smp8759_config_write, - } -}; - -static int tango_pcie_link_up(struct tango_pcie *pcie) -{ - void __iomem *test_out = pcie->base + SMP8759_TEST_OUT; - int i; - - writel_relaxed(16, test_out); - for (i = 0; i < 10; ++i) { - u32 ltssm_state = readl_relaxed(test_out) >> 8; - if ((ltssm_state & 0x1f) == 0xf) /* L0 */ - return 1; - usleep_range(3000, 4000); - } - - return 0; -} - -static int tango_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct tango_pcie *pcie; - struct resource *res; - struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); - struct irq_domain *msi_dom, *irq_dom; - struct of_pci_range_parser parser; - struct of_pci_range range; - int virq, offset; - - dev_warn(dev, "simultaneous PCI config and MMIO accesses may cause data corruption\n"); - add_taint(TAINT_CRAP, LOCKDEP_STILL_OK); - - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); - if (!pcie) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - pcie->base = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->base)) - return PTR_ERR(pcie->base); - - platform_set_drvdata(pdev, pcie); - - if (!tango_pcie_link_up(pcie)) - return -ENODEV; - - if (of_pci_dma_range_parser_init(&parser, dev->of_node) < 0) - return -ENOENT; - - if (of_pci_range_parser_one(&parser, &range) == NULL) - return -ENOENT; - - range.pci_addr += range.size; - pcie->msi_doorbell = range.pci_addr + res->start + SMP8759_DOORBELL; - - for (offset = 0; offset < MSI_MAX / 8; offset += 4) - writel_relaxed(0, pcie->base + SMP8759_ENABLE + offset); - - virq = platform_get_irq(pdev, 1); - if (virq <= 0) { - dev_err(dev, "Failed to map IRQ\n"); - return -ENXIO; - } - - irq_dom = irq_domain_create_linear(fwnode, MSI_MAX, &dom_ops, pcie); - if (!irq_dom) { - dev_err(dev, "Failed to create IRQ domain\n"); - return -ENOMEM; - } - - msi_dom = pci_msi_create_irq_domain(fwnode, &msi_dom_info, irq_dom); - if (!msi_dom) { - dev_err(dev, "Failed to create MSI domain\n"); - irq_domain_remove(irq_dom); - return -ENOMEM; - } - - pcie->dom = irq_dom; - 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); -} - -static const struct of_device_id tango_pcie_ids[] = { - { .compatible = "sigma,smp8759-pcie" }, - { }, -}; - -static struct platform_driver tango_pcie_driver = { - .probe = tango_pcie_probe, - .driver = { - .name = KBUILD_MODNAME, - .of_match_table = tango_pcie_ids, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(tango_pcie_driver); - -/* - * The root complex advertises the wrong device class. - * Header Type 1 is for PCI-to-PCI bridges. - */ -static void tango_fixup_class(struct pci_dev *dev) -{ - dev->class = PCI_CLASS_BRIDGE_PCI << 8; -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_class); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_class); - -/* - * The root complex exposes a "fake" BAR, which is used to filter - * bus-to-system accesses. Only accesses within the range defined by this - * BAR are forwarded to the host, others are ignored. - * - * By default, the DMA framework expects an identity mapping, and DRAM0 is - * mapped at 0x80000000. - */ -static void tango_fixup_bar(struct pci_dev *dev) -{ - dev->non_compliant_bars = true; - pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0x80000000); -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_bar); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_bar); diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c deleted file mode 100644 index 6a4bbb5b3de0..000000000000 --- a/drivers/pci/host/pcie-xilinx-nwl.c +++ /dev/null @@ -1,917 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * PCIe host controller driver for NWL PCIe Bridge - * Based on pcie-xilinx.c, pci-tegra.c - * - * (C) Copyright 2014 - 2015, Xilinx, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -/* Bridge core config registers */ -#define BRCFG_PCIE_RX0 0x00000000 -#define BRCFG_INTERRUPT 0x00000010 -#define BRCFG_PCIE_RX_MSG_FILTER 0x00000020 - -/* Egress - Bridge translation registers */ -#define E_BREG_CAPABILITIES 0x00000200 -#define E_BREG_CONTROL 0x00000208 -#define E_BREG_BASE_LO 0x00000210 -#define E_BREG_BASE_HI 0x00000214 -#define E_ECAM_CAPABILITIES 0x00000220 -#define E_ECAM_CONTROL 0x00000228 -#define E_ECAM_BASE_LO 0x00000230 -#define E_ECAM_BASE_HI 0x00000234 - -/* Ingress - address translations */ -#define I_MSII_CAPABILITIES 0x00000300 -#define I_MSII_CONTROL 0x00000308 -#define I_MSII_BASE_LO 0x00000310 -#define I_MSII_BASE_HI 0x00000314 - -#define I_ISUB_CONTROL 0x000003E8 -#define SET_ISUB_CONTROL BIT(0) -/* Rxed msg fifo - Interrupt status registers */ -#define MSGF_MISC_STATUS 0x00000400 -#define MSGF_MISC_MASK 0x00000404 -#define MSGF_LEG_STATUS 0x00000420 -#define MSGF_LEG_MASK 0x00000424 -#define MSGF_MSI_STATUS_LO 0x00000440 -#define MSGF_MSI_STATUS_HI 0x00000444 -#define MSGF_MSI_MASK_LO 0x00000448 -#define MSGF_MSI_MASK_HI 0x0000044C - -/* Msg filter mask bits */ -#define CFG_ENABLE_PM_MSG_FWD BIT(1) -#define CFG_ENABLE_INT_MSG_FWD BIT(2) -#define CFG_ENABLE_ERR_MSG_FWD BIT(3) -#define CFG_ENABLE_MSG_FILTER_MASK (CFG_ENABLE_PM_MSG_FWD | \ - CFG_ENABLE_INT_MSG_FWD | \ - CFG_ENABLE_ERR_MSG_FWD) - -/* Misc interrupt status mask bits */ -#define MSGF_MISC_SR_RXMSG_AVAIL BIT(0) -#define MSGF_MISC_SR_RXMSG_OVER BIT(1) -#define MSGF_MISC_SR_SLAVE_ERR BIT(4) -#define MSGF_MISC_SR_MASTER_ERR BIT(5) -#define MSGF_MISC_SR_I_ADDR_ERR BIT(6) -#define MSGF_MISC_SR_E_ADDR_ERR BIT(7) -#define MSGF_MISC_SR_FATAL_AER BIT(16) -#define MSGF_MISC_SR_NON_FATAL_AER BIT(17) -#define MSGF_MISC_SR_CORR_AER BIT(18) -#define MSGF_MISC_SR_UR_DETECT BIT(20) -#define MSGF_MISC_SR_NON_FATAL_DEV BIT(22) -#define MSGF_MISC_SR_FATAL_DEV BIT(23) -#define MSGF_MISC_SR_LINK_DOWN BIT(24) -#define MSGF_MSIC_SR_LINK_AUTO_BWIDTH BIT(25) -#define MSGF_MSIC_SR_LINK_BWIDTH BIT(26) - -#define MSGF_MISC_SR_MASKALL (MSGF_MISC_SR_RXMSG_AVAIL | \ - MSGF_MISC_SR_RXMSG_OVER | \ - MSGF_MISC_SR_SLAVE_ERR | \ - MSGF_MISC_SR_MASTER_ERR | \ - MSGF_MISC_SR_I_ADDR_ERR | \ - MSGF_MISC_SR_E_ADDR_ERR | \ - MSGF_MISC_SR_FATAL_AER | \ - MSGF_MISC_SR_NON_FATAL_AER | \ - MSGF_MISC_SR_CORR_AER | \ - MSGF_MISC_SR_UR_DETECT | \ - MSGF_MISC_SR_NON_FATAL_DEV | \ - MSGF_MISC_SR_FATAL_DEV | \ - MSGF_MISC_SR_LINK_DOWN | \ - MSGF_MSIC_SR_LINK_AUTO_BWIDTH | \ - MSGF_MSIC_SR_LINK_BWIDTH) - -/* Legacy interrupt status mask bits */ -#define MSGF_LEG_SR_INTA BIT(0) -#define MSGF_LEG_SR_INTB BIT(1) -#define MSGF_LEG_SR_INTC BIT(2) -#define MSGF_LEG_SR_INTD BIT(3) -#define MSGF_LEG_SR_MASKALL (MSGF_LEG_SR_INTA | MSGF_LEG_SR_INTB | \ - MSGF_LEG_SR_INTC | MSGF_LEG_SR_INTD) - -/* MSI interrupt status mask bits */ -#define MSGF_MSI_SR_LO_MASK GENMASK(31, 0) -#define MSGF_MSI_SR_HI_MASK GENMASK(31, 0) - -#define MSII_PRESENT BIT(0) -#define MSII_ENABLE BIT(0) -#define MSII_STATUS_ENABLE BIT(15) - -/* Bridge config interrupt mask */ -#define BRCFG_INTERRUPT_MASK BIT(0) -#define BREG_PRESENT BIT(0) -#define BREG_ENABLE BIT(0) -#define BREG_ENABLE_FORCE BIT(1) - -/* E_ECAM status mask bits */ -#define E_ECAM_PRESENT BIT(0) -#define E_ECAM_CR_ENABLE BIT(0) -#define E_ECAM_SIZE_LOC GENMASK(20, 16) -#define E_ECAM_SIZE_SHIFT 16 -#define ECAM_BUS_LOC_SHIFT 20 -#define ECAM_DEV_LOC_SHIFT 12 -#define NWL_ECAM_VALUE_DEFAULT 12 - -#define CFG_DMA_REG_BAR GENMASK(2, 0) - -#define INT_PCI_MSI_NR (2 * 32) - -/* Readin the PS_LINKUP */ -#define PS_LINKUP_OFFSET 0x00000238 -#define PCIE_PHY_LINKUP_BIT BIT(0) -#define PHY_RDY_LINKUP_BIT BIT(1) - -/* Parameters for the waiting for link up routine */ -#define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_USLEEP_MIN 90000 -#define LINK_WAIT_USLEEP_MAX 100000 - -struct nwl_msi { /* MSI information */ - struct irq_domain *msi_domain; - unsigned long *bitmap; - struct irq_domain *dev_domain; - struct mutex lock; /* protect bitmap variable */ - int irq_msi0; - int irq_msi1; -}; - -struct nwl_pcie { - struct device *dev; - void __iomem *breg_base; - void __iomem *pcireg_base; - void __iomem *ecam_base; - phys_addr_t phys_breg_base; /* Physical Bridge Register Base */ - phys_addr_t phys_pcie_reg_base; /* Physical PCIe Controller Base */ - phys_addr_t phys_ecam_base; /* Physical Configuration Base */ - u32 breg_size; - u32 pcie_reg_size; - u32 ecam_size; - int irq_intx; - int irq_misc; - u32 ecam_value; - u8 last_busno; - u8 root_busno; - struct nwl_msi msi; - struct irq_domain *legacy_irq_domain; - raw_spinlock_t leg_mask_lock; -}; - -static inline u32 nwl_bridge_readl(struct nwl_pcie *pcie, u32 off) -{ - return readl(pcie->breg_base + off); -} - -static inline void nwl_bridge_writel(struct nwl_pcie *pcie, u32 val, u32 off) -{ - writel(val, pcie->breg_base + off); -} - -static bool nwl_pcie_link_up(struct nwl_pcie *pcie) -{ - if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PCIE_PHY_LINKUP_BIT) - return true; - return false; -} - -static bool nwl_phy_link_up(struct nwl_pcie *pcie) -{ - if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PHY_RDY_LINKUP_BIT) - return true; - return false; -} - -static int nwl_wait_for_link(struct nwl_pcie *pcie) -{ - struct device *dev = pcie->dev; - int retries; - - /* check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (nwl_phy_link_up(pcie)) - return 0; - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); - } - - dev_err(dev, "PHY link never came up\n"); - return -ETIMEDOUT; -} - -static bool nwl_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) -{ - struct nwl_pcie *pcie = bus->sysdata; - - /* Check link before accessing downstream ports */ - if (bus->number != pcie->root_busno) { - if (!nwl_pcie_link_up(pcie)) - return false; - } - - /* Only one device down on each root port */ - if (bus->number == pcie->root_busno && devfn > 0) - return false; - - return true; -} - -/** - * nwl_pcie_map_bus - Get configuration base - * - * @bus: Bus structure of current bus - * @devfn: Device/function - * @where: Offset from base - * - * Return: Base address of the configuration space needed to be - * accessed. - */ -static void __iomem *nwl_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, - int where) -{ - struct nwl_pcie *pcie = bus->sysdata; - int relbus; - - if (!nwl_pcie_valid_device(bus, devfn)) - return NULL; - - relbus = (bus->number << ECAM_BUS_LOC_SHIFT) | - (devfn << ECAM_DEV_LOC_SHIFT); - - return pcie->ecam_base + relbus + where; -} - -/* PCIe operations */ -static struct pci_ops nwl_pcie_ops = { - .map_bus = nwl_pcie_map_bus, - .read = pci_generic_config_read, - .write = pci_generic_config_write, -}; - -static irqreturn_t nwl_pcie_misc_handler(int irq, void *data) -{ - struct nwl_pcie *pcie = data; - struct device *dev = pcie->dev; - u32 misc_stat; - - /* Checking for misc interrupts */ - misc_stat = nwl_bridge_readl(pcie, MSGF_MISC_STATUS) & - MSGF_MISC_SR_MASKALL; - if (!misc_stat) - return IRQ_NONE; - - if (misc_stat & MSGF_MISC_SR_RXMSG_OVER) - dev_err(dev, "Received Message FIFO Overflow\n"); - - if (misc_stat & MSGF_MISC_SR_SLAVE_ERR) - dev_err(dev, "Slave error\n"); - - if (misc_stat & MSGF_MISC_SR_MASTER_ERR) - dev_err(dev, "Master error\n"); - - if (misc_stat & MSGF_MISC_SR_I_ADDR_ERR) - dev_err(dev, "In Misc Ingress address translation error\n"); - - if (misc_stat & MSGF_MISC_SR_E_ADDR_ERR) - dev_err(dev, "In Misc Egress address translation error\n"); - - if (misc_stat & MSGF_MISC_SR_FATAL_AER) - dev_err(dev, "Fatal Error in AER Capability\n"); - - if (misc_stat & MSGF_MISC_SR_NON_FATAL_AER) - dev_err(dev, "Non-Fatal Error in AER Capability\n"); - - if (misc_stat & MSGF_MISC_SR_CORR_AER) - dev_err(dev, "Correctable Error in AER Capability\n"); - - if (misc_stat & MSGF_MISC_SR_UR_DETECT) - dev_err(dev, "Unsupported request Detected\n"); - - if (misc_stat & MSGF_MISC_SR_NON_FATAL_DEV) - dev_err(dev, "Non-Fatal Error Detected\n"); - - if (misc_stat & MSGF_MISC_SR_FATAL_DEV) - dev_err(dev, "Fatal Error Detected\n"); - - if (misc_stat & MSGF_MSIC_SR_LINK_AUTO_BWIDTH) - dev_info(dev, "Link Autonomous Bandwidth Management Status bit set\n"); - - if (misc_stat & MSGF_MSIC_SR_LINK_BWIDTH) - dev_info(dev, "Link Bandwidth Management Status bit set\n"); - - /* Clear misc interrupt status */ - nwl_bridge_writel(pcie, misc_stat, MSGF_MISC_STATUS); - - return IRQ_HANDLED; -} - -static void nwl_pcie_leg_handler(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct nwl_pcie *pcie; - unsigned long status; - u32 bit; - u32 virq; - - chained_irq_enter(chip, desc); - pcie = irq_desc_get_handler_data(desc); - - while ((status = nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & - MSGF_LEG_SR_MASKALL) != 0) { - for_each_set_bit(bit, &status, PCI_NUM_INTX) { - virq = irq_find_mapping(pcie->legacy_irq_domain, bit); - if (virq) - generic_handle_irq(virq); - } - } - - chained_irq_exit(chip, desc); -} - -static void nwl_pcie_handle_msi_irq(struct nwl_pcie *pcie, u32 status_reg) -{ - struct nwl_msi *msi; - unsigned long status; - u32 bit; - u32 virq; - - msi = &pcie->msi; - - while ((status = nwl_bridge_readl(pcie, status_reg)) != 0) { - for_each_set_bit(bit, &status, 32) { - nwl_bridge_writel(pcie, 1 << bit, status_reg); - virq = irq_find_mapping(msi->dev_domain, bit); - if (virq) - generic_handle_irq(virq); - } - } -} - -static void nwl_pcie_msi_handler_high(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct nwl_pcie *pcie = irq_desc_get_handler_data(desc); - - chained_irq_enter(chip, desc); - nwl_pcie_handle_msi_irq(pcie, MSGF_MSI_STATUS_HI); - chained_irq_exit(chip, desc); -} - -static void nwl_pcie_msi_handler_low(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct nwl_pcie *pcie = irq_desc_get_handler_data(desc); - - chained_irq_enter(chip, desc); - nwl_pcie_handle_msi_irq(pcie, MSGF_MSI_STATUS_LO); - chained_irq_exit(chip, desc); -} - -static void nwl_mask_leg_irq(struct irq_data *data) -{ - struct irq_desc *desc = irq_to_desc(data->irq); - struct nwl_pcie *pcie; - unsigned long flags; - u32 mask; - u32 val; - - pcie = irq_desc_get_chip_data(desc); - mask = 1 << (data->hwirq - 1); - raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags); - val = nwl_bridge_readl(pcie, MSGF_LEG_MASK); - nwl_bridge_writel(pcie, (val & (~mask)), MSGF_LEG_MASK); - raw_spin_unlock_irqrestore(&pcie->leg_mask_lock, flags); -} - -static void nwl_unmask_leg_irq(struct irq_data *data) -{ - struct irq_desc *desc = irq_to_desc(data->irq); - struct nwl_pcie *pcie; - unsigned long flags; - u32 mask; - u32 val; - - pcie = irq_desc_get_chip_data(desc); - mask = 1 << (data->hwirq - 1); - raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags); - val = nwl_bridge_readl(pcie, MSGF_LEG_MASK); - nwl_bridge_writel(pcie, (val | mask), MSGF_LEG_MASK); - raw_spin_unlock_irqrestore(&pcie->leg_mask_lock, flags); -} - -static struct irq_chip nwl_leg_irq_chip = { - .name = "nwl_pcie:legacy", - .irq_enable = nwl_unmask_leg_irq, - .irq_disable = nwl_mask_leg_irq, - .irq_mask = nwl_mask_leg_irq, - .irq_unmask = nwl_unmask_leg_irq, -}; - -static int nwl_legacy_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &nwl_leg_irq_chip, handle_level_irq); - irq_set_chip_data(irq, domain->host_data); - irq_set_status_flags(irq, IRQ_LEVEL); - - return 0; -} - -static const struct irq_domain_ops legacy_domain_ops = { - .map = nwl_legacy_map, - .xlate = pci_irqd_intx_xlate, -}; - -#ifdef CONFIG_PCI_MSI -static struct irq_chip nwl_msi_irq_chip = { - .name = "nwl_pcie:msi", - .irq_enable = unmask_msi_irq, - .irq_disable = mask_msi_irq, - .irq_mask = mask_msi_irq, - .irq_unmask = unmask_msi_irq, - -}; - -static struct msi_domain_info nwl_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI), - .chip = &nwl_msi_irq_chip, -}; -#endif - -static void nwl_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); - phys_addr_t msi_addr = pcie->phys_pcie_reg_base; - - msg->address_lo = lower_32_bits(msi_addr); - msg->address_hi = upper_32_bits(msi_addr); - msg->data = data->hwirq; -} - -static int nwl_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) -{ - return -EINVAL; -} - -static struct irq_chip nwl_irq_chip = { - .name = "Xilinx MSI", - .irq_compose_msi_msg = nwl_compose_msi_msg, - .irq_set_affinity = nwl_msi_set_affinity, -}; - -static int nwl_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *args) -{ - struct nwl_pcie *pcie = domain->host_data; - struct nwl_msi *msi = &pcie->msi; - int bit; - int i; - - mutex_lock(&msi->lock); - bit = bitmap_find_next_zero_area(msi->bitmap, INT_PCI_MSI_NR, 0, - nr_irqs, 0); - if (bit >= INT_PCI_MSI_NR) { - mutex_unlock(&msi->lock); - return -ENOSPC; - } - - bitmap_set(msi->bitmap, bit, nr_irqs); - - for (i = 0; i < nr_irqs; i++) { - irq_domain_set_info(domain, virq + i, bit + i, &nwl_irq_chip, - domain->host_data, handle_simple_irq, - NULL, NULL); - } - mutex_unlock(&msi->lock); - return 0; -} - -static void nwl_irq_domain_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) -{ - struct irq_data *data = irq_domain_get_irq_data(domain, virq); - struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); - struct nwl_msi *msi = &pcie->msi; - - mutex_lock(&msi->lock); - bitmap_clear(msi->bitmap, data->hwirq, nr_irqs); - mutex_unlock(&msi->lock); -} - -static const struct irq_domain_ops dev_msi_domain_ops = { - .alloc = nwl_irq_domain_alloc, - .free = nwl_irq_domain_free, -}; - -static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie) -{ -#ifdef CONFIG_PCI_MSI - struct device *dev = pcie->dev; - struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); - struct nwl_msi *msi = &pcie->msi; - - msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR, - &dev_msi_domain_ops, pcie); - if (!msi->dev_domain) { - dev_err(dev, "failed to create dev IRQ domain\n"); - return -ENOMEM; - } - msi->msi_domain = pci_msi_create_irq_domain(fwnode, - &nwl_msi_domain_info, - msi->dev_domain); - if (!msi->msi_domain) { - dev_err(dev, "failed to create msi IRQ domain\n"); - irq_domain_remove(msi->dev_domain); - return -ENOMEM; - } -#endif - return 0; -} - -static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct device_node *node = dev->of_node; - struct device_node *legacy_intc_node; - - legacy_intc_node = of_get_next_child(node, NULL); - if (!legacy_intc_node) { - dev_err(dev, "No legacy intc node found\n"); - return -EINVAL; - } - - pcie->legacy_irq_domain = irq_domain_add_linear(legacy_intc_node, - PCI_NUM_INTX, - &legacy_domain_ops, - pcie); - - if (!pcie->legacy_irq_domain) { - dev_err(dev, "failed to create IRQ domain\n"); - return -ENOMEM; - } - - raw_spin_lock_init(&pcie->leg_mask_lock); - nwl_pcie_init_msi_irq_domain(pcie); - return 0; -} - -static int nwl_pcie_enable_msi(struct nwl_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct platform_device *pdev = to_platform_device(dev); - struct nwl_msi *msi = &pcie->msi; - unsigned long base; - int ret; - int size = BITS_TO_LONGS(INT_PCI_MSI_NR) * sizeof(long); - - mutex_init(&msi->lock); - - msi->bitmap = kzalloc(size, GFP_KERNEL); - if (!msi->bitmap) - return -ENOMEM; - - /* Get msi_1 IRQ number */ - msi->irq_msi1 = platform_get_irq_byname(pdev, "msi1"); - if (msi->irq_msi1 < 0) { - dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi1); - ret = -EINVAL; - goto err; - } - - irq_set_chained_handler_and_data(msi->irq_msi1, - nwl_pcie_msi_handler_high, pcie); - - /* Get msi_0 IRQ number */ - msi->irq_msi0 = platform_get_irq_byname(pdev, "msi0"); - if (msi->irq_msi0 < 0) { - dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi0); - ret = -EINVAL; - goto err; - } - - irq_set_chained_handler_and_data(msi->irq_msi0, - nwl_pcie_msi_handler_low, pcie); - - /* Check for msii_present bit */ - ret = nwl_bridge_readl(pcie, I_MSII_CAPABILITIES) & MSII_PRESENT; - if (!ret) { - dev_err(dev, "MSI not present\n"); - ret = -EIO; - goto err; - } - - /* Enable MSII */ - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, I_MSII_CONTROL) | - MSII_ENABLE, I_MSII_CONTROL); - - /* Enable MSII status */ - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, I_MSII_CONTROL) | - MSII_STATUS_ENABLE, I_MSII_CONTROL); - - /* setup AFI/FPCI range */ - base = pcie->phys_pcie_reg_base; - nwl_bridge_writel(pcie, lower_32_bits(base), I_MSII_BASE_LO); - nwl_bridge_writel(pcie, upper_32_bits(base), I_MSII_BASE_HI); - - /* - * For high range MSI interrupts: disable, clear any pending, - * and enable - */ - nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_HI); - - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_HI) & - MSGF_MSI_SR_HI_MASK, MSGF_MSI_STATUS_HI); - - nwl_bridge_writel(pcie, MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI); - - /* - * For low range MSI interrupts: disable, clear any pending, - * and enable - */ - nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_LO); - - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_LO) & - MSGF_MSI_SR_LO_MASK, MSGF_MSI_STATUS_LO); - - nwl_bridge_writel(pcie, MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO); - - return 0; -err: - kfree(msi->bitmap); - msi->bitmap = NULL; - return ret; -} - -static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) -{ - struct device *dev = pcie->dev; - struct platform_device *pdev = to_platform_device(dev); - u32 breg_val, ecam_val, first_busno = 0; - int err; - - breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT; - if (!breg_val) { - dev_err(dev, "BREG is not present\n"); - return breg_val; - } - - /* Write bridge_off to breg base */ - nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_breg_base), - E_BREG_BASE_LO); - nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_breg_base), - E_BREG_BASE_HI); - - /* Enable BREG */ - nwl_bridge_writel(pcie, ~BREG_ENABLE_FORCE & BREG_ENABLE, - E_BREG_CONTROL); - - /* Disable DMA channel registers */ - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_PCIE_RX0) | - CFG_DMA_REG_BAR, BRCFG_PCIE_RX0); - - /* Enable Ingress subtractive decode translation */ - nwl_bridge_writel(pcie, SET_ISUB_CONTROL, I_ISUB_CONTROL); - - /* Enable msg filtering details */ - nwl_bridge_writel(pcie, CFG_ENABLE_MSG_FILTER_MASK, - BRCFG_PCIE_RX_MSG_FILTER); - - err = nwl_wait_for_link(pcie); - if (err) - return err; - - ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT; - if (!ecam_val) { - dev_err(dev, "ECAM is not present\n"); - return ecam_val; - } - - /* Enable ECAM */ - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | - E_ECAM_CR_ENABLE, E_ECAM_CONTROL); - - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | - (pcie->ecam_value << E_ECAM_SIZE_SHIFT), - E_ECAM_CONTROL); - - nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_ecam_base), - E_ECAM_BASE_LO); - nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_ecam_base), - E_ECAM_BASE_HI); - - /* Get bus range */ - ecam_val = nwl_bridge_readl(pcie, E_ECAM_CONTROL); - pcie->last_busno = (ecam_val & E_ECAM_SIZE_LOC) >> E_ECAM_SIZE_SHIFT; - /* Write primary, secondary and subordinate bus numbers */ - ecam_val = first_busno; - ecam_val |= (first_busno + 1) << 8; - ecam_val |= (pcie->last_busno << E_ECAM_SIZE_SHIFT); - writel(ecam_val, (pcie->ecam_base + PCI_PRIMARY_BUS)); - - if (nwl_pcie_link_up(pcie)) - dev_info(dev, "Link is UP\n"); - else - dev_info(dev, "Link is DOWN\n"); - - /* Get misc IRQ number */ - pcie->irq_misc = platform_get_irq_byname(pdev, "misc"); - if (pcie->irq_misc < 0) { - dev_err(dev, "failed to get misc IRQ %d\n", - pcie->irq_misc); - return -EINVAL; - } - - err = devm_request_irq(dev, pcie->irq_misc, - nwl_pcie_misc_handler, IRQF_SHARED, - "nwl_pcie:misc", pcie); - if (err) { - dev_err(dev, "fail to register misc IRQ#%d\n", - pcie->irq_misc); - return err; - } - - /* Disable all misc interrupts */ - nwl_bridge_writel(pcie, (u32)~MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); - - /* Clear pending misc interrupts */ - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MISC_STATUS) & - MSGF_MISC_SR_MASKALL, MSGF_MISC_STATUS); - - /* Enable all misc interrupts */ - nwl_bridge_writel(pcie, MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); - - - /* Disable all legacy interrupts */ - nwl_bridge_writel(pcie, (u32)~MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); - - /* Clear pending legacy interrupts */ - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & - MSGF_LEG_SR_MASKALL, MSGF_LEG_STATUS); - - /* Enable all legacy interrupts */ - nwl_bridge_writel(pcie, MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); - - /* Enable the bridge config interrupt */ - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_INTERRUPT) | - BRCFG_INTERRUPT_MASK, BRCFG_INTERRUPT); - - return 0; -} - -static int nwl_pcie_parse_dt(struct nwl_pcie *pcie, - struct platform_device *pdev) -{ - struct device *dev = pcie->dev; - struct device_node *node = dev->of_node; - struct resource *res; - const char *type; - - /* Check for device type */ - type = of_get_property(node, "device_type", NULL); - if (!type || strcmp(type, "pci")) { - dev_err(dev, "invalid \"device_type\" %s\n", type); - return -EINVAL; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg"); - pcie->breg_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->breg_base)) - return PTR_ERR(pcie->breg_base); - pcie->phys_breg_base = res->start; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcireg"); - pcie->pcireg_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->pcireg_base)) - return PTR_ERR(pcie->pcireg_base); - pcie->phys_pcie_reg_base = res->start; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); - pcie->ecam_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pcie->ecam_base)) - return PTR_ERR(pcie->ecam_base); - pcie->phys_ecam_base = res->start; - - /* Get intx IRQ number */ - pcie->irq_intx = platform_get_irq_byname(pdev, "intx"); - if (pcie->irq_intx < 0) { - dev_err(dev, "failed to get intx IRQ %d\n", pcie->irq_intx); - return pcie->irq_intx; - } - - irq_set_chained_handler_and_data(pcie->irq_intx, - nwl_pcie_leg_handler, pcie); - - return 0; -} - -static const struct of_device_id nwl_pcie_of_match[] = { - { .compatible = "xlnx,nwl-pcie-2.11", }, - {} -}; - -static int nwl_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct nwl_pcie *pcie; - struct pci_bus *bus; - struct pci_bus *child; - struct pci_host_bridge *bridge; - int err; - resource_size_t iobase = 0; - LIST_HEAD(res); - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); - if (!bridge) - return -ENODEV; - - pcie = pci_host_bridge_priv(bridge); - - pcie->dev = dev; - pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT; - - err = nwl_pcie_parse_dt(pcie, pdev); - if (err) { - dev_err(dev, "Parsing DT failed\n"); - return err; - } - - err = nwl_pcie_bridge_init(pcie); - if (err) { - dev_err(dev, "HW Initialization failed\n"); - return err; - } - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, - &iobase); - if (err) { - dev_err(dev, "Getting bridge resources failed\n"); - return err; - } - - err = devm_request_pci_bus_resources(dev, &res); - if (err) - goto error; - - err = nwl_pcie_init_irq_domain(pcie); - if (err) { - dev_err(dev, "Failed creating IRQ Domain\n"); - goto error; - } - - list_splice_init(&res, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = pcie; - bridge->busnr = pcie->root_busno; - bridge->ops = &nwl_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - err = nwl_pcie_enable_msi(pcie); - if (err < 0) { - dev_err(dev, "failed to enable MSI support: %d\n", err); - goto error; - } - } - - err = pci_scan_root_bus_bridge(bridge); - if (err) - goto error; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - return 0; - -error: - pci_free_resource_list(&res); - return err; -} - -static struct platform_driver nwl_pcie_driver = { - .driver = { - .name = "nwl-pcie", - .suppress_bind_attrs = true, - .of_match_table = nwl_pcie_of_match, - }, - .probe = nwl_pcie_probe, -}; -builtin_platform_driver(nwl_pcie_driver); diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c deleted file mode 100644 index b110a3a814e3..000000000000 --- a/drivers/pci/host/pcie-xilinx.c +++ /dev/null @@ -1,702 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * PCIe host controller driver for Xilinx AXI PCIe Bridge - * - * Copyright (c) 2012 - 2014 Xilinx, Inc. - * - * Based on the Tegra PCIe driver - * - * Bits taken from Synopsys DesignWare Host controller driver and - * ARM PCI Host generic driver. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../pci.h" - -/* Register definitions */ -#define XILINX_PCIE_REG_BIR 0x00000130 -#define XILINX_PCIE_REG_IDR 0x00000138 -#define XILINX_PCIE_REG_IMR 0x0000013c -#define XILINX_PCIE_REG_PSCR 0x00000144 -#define XILINX_PCIE_REG_RPSC 0x00000148 -#define XILINX_PCIE_REG_MSIBASE1 0x0000014c -#define XILINX_PCIE_REG_MSIBASE2 0x00000150 -#define XILINX_PCIE_REG_RPEFR 0x00000154 -#define XILINX_PCIE_REG_RPIFR1 0x00000158 -#define XILINX_PCIE_REG_RPIFR2 0x0000015c - -/* Interrupt registers definitions */ -#define XILINX_PCIE_INTR_LINK_DOWN BIT(0) -#define XILINX_PCIE_INTR_ECRC_ERR BIT(1) -#define XILINX_PCIE_INTR_STR_ERR BIT(2) -#define XILINX_PCIE_INTR_HOT_RESET BIT(3) -#define XILINX_PCIE_INTR_CFG_TIMEOUT BIT(8) -#define XILINX_PCIE_INTR_CORRECTABLE BIT(9) -#define XILINX_PCIE_INTR_NONFATAL BIT(10) -#define XILINX_PCIE_INTR_FATAL BIT(11) -#define XILINX_PCIE_INTR_INTX BIT(16) -#define XILINX_PCIE_INTR_MSI BIT(17) -#define XILINX_PCIE_INTR_SLV_UNSUPP BIT(20) -#define XILINX_PCIE_INTR_SLV_UNEXP BIT(21) -#define XILINX_PCIE_INTR_SLV_COMPL BIT(22) -#define XILINX_PCIE_INTR_SLV_ERRP BIT(23) -#define XILINX_PCIE_INTR_SLV_CMPABT BIT(24) -#define XILINX_PCIE_INTR_SLV_ILLBUR BIT(25) -#define XILINX_PCIE_INTR_MST_DECERR BIT(26) -#define XILINX_PCIE_INTR_MST_SLVERR BIT(27) -#define XILINX_PCIE_INTR_MST_ERRP BIT(28) -#define XILINX_PCIE_IMR_ALL_MASK 0x1FF30FED -#define XILINX_PCIE_IMR_ENABLE_MASK 0x1FF30F0D -#define XILINX_PCIE_IDR_ALL_MASK 0xFFFFFFFF - -/* Root Port Error FIFO Read Register definitions */ -#define XILINX_PCIE_RPEFR_ERR_VALID BIT(18) -#define XILINX_PCIE_RPEFR_REQ_ID GENMASK(15, 0) -#define XILINX_PCIE_RPEFR_ALL_MASK 0xFFFFFFFF - -/* Root Port Interrupt FIFO Read Register 1 definitions */ -#define XILINX_PCIE_RPIFR1_INTR_VALID BIT(31) -#define XILINX_PCIE_RPIFR1_MSI_INTR BIT(30) -#define XILINX_PCIE_RPIFR1_INTR_MASK GENMASK(28, 27) -#define XILINX_PCIE_RPIFR1_ALL_MASK 0xFFFFFFFF -#define XILINX_PCIE_RPIFR1_INTR_SHIFT 27 - -/* Bridge Info Register definitions */ -#define XILINX_PCIE_BIR_ECAM_SZ_MASK GENMASK(18, 16) -#define XILINX_PCIE_BIR_ECAM_SZ_SHIFT 16 - -/* Root Port Interrupt FIFO Read Register 2 definitions */ -#define XILINX_PCIE_RPIFR2_MSG_DATA GENMASK(15, 0) - -/* Root Port Status/control Register definitions */ -#define XILINX_PCIE_REG_RPSC_BEN BIT(0) - -/* Phy Status/Control Register definitions */ -#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11) - -/* ECAM definitions */ -#define ECAM_BUS_NUM_SHIFT 20 -#define ECAM_DEV_NUM_SHIFT 12 - -/* Number of MSI IRQs */ -#define XILINX_NUM_MSI_IRQS 128 - -/** - * struct xilinx_pcie_port - PCIe port information - * @reg_base: IO Mapped Register Base - * @irq: Interrupt number - * @msi_pages: MSI pages - * @root_busno: Root Bus number - * @dev: Device pointer - * @msi_domain: MSI IRQ domain pointer - * @leg_domain: Legacy IRQ domain pointer - * @resources: Bus Resources - */ -struct xilinx_pcie_port { - void __iomem *reg_base; - u32 irq; - unsigned long msi_pages; - u8 root_busno; - struct device *dev; - struct irq_domain *msi_domain; - struct irq_domain *leg_domain; - struct list_head resources; -}; - -static DECLARE_BITMAP(msi_irq_in_use, XILINX_NUM_MSI_IRQS); - -static inline u32 pcie_read(struct xilinx_pcie_port *port, u32 reg) -{ - return readl(port->reg_base + reg); -} - -static inline void pcie_write(struct xilinx_pcie_port *port, u32 val, u32 reg) -{ - writel(val, port->reg_base + reg); -} - -static inline bool xilinx_pcie_link_up(struct xilinx_pcie_port *port) -{ - return (pcie_read(port, XILINX_PCIE_REG_PSCR) & - XILINX_PCIE_REG_PSCR_LNKUP) ? 1 : 0; -} - -/** - * xilinx_pcie_clear_err_interrupts - Clear Error Interrupts - * @port: PCIe port information - */ -static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port) -{ - struct device *dev = port->dev; - unsigned long val = pcie_read(port, XILINX_PCIE_REG_RPEFR); - - if (val & XILINX_PCIE_RPEFR_ERR_VALID) { - dev_dbg(dev, "Requester ID %lu\n", - val & XILINX_PCIE_RPEFR_REQ_ID); - pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK, - XILINX_PCIE_REG_RPEFR); - } -} - -/** - * xilinx_pcie_valid_device - Check if a valid device is present on bus - * @bus: PCI Bus structure - * @devfn: device/function - * - * Return: 'true' on success and 'false' if invalid device is found - */ -static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) -{ - struct xilinx_pcie_port *port = bus->sysdata; - - /* Check if link is up when trying to access downstream ports */ - if (bus->number != port->root_busno) - if (!xilinx_pcie_link_up(port)) - return false; - - /* Only one device down on each root port */ - if (bus->number == port->root_busno && devfn > 0) - return false; - - return true; -} - -/** - * xilinx_pcie_map_bus - Get configuration base - * @bus: PCI Bus structure - * @devfn: Device/function - * @where: Offset from base - * - * Return: Base address of the configuration space needed to be - * accessed. - */ -static void __iomem *xilinx_pcie_map_bus(struct pci_bus *bus, - unsigned int devfn, int where) -{ - struct xilinx_pcie_port *port = bus->sysdata; - int relbus; - - if (!xilinx_pcie_valid_device(bus, devfn)) - return NULL; - - relbus = (bus->number << ECAM_BUS_NUM_SHIFT) | - (devfn << ECAM_DEV_NUM_SHIFT); - - return port->reg_base + relbus + where; -} - -/* PCIe operations */ -static struct pci_ops xilinx_pcie_ops = { - .map_bus = xilinx_pcie_map_bus, - .read = pci_generic_config_read, - .write = pci_generic_config_write, -}; - -/* MSI functions */ - -/** - * xilinx_pcie_destroy_msi - Free MSI number - * @irq: IRQ to be freed - */ -static void xilinx_pcie_destroy_msi(unsigned int irq) -{ - struct msi_desc *msi; - struct xilinx_pcie_port *port; - struct irq_data *d = irq_get_irq_data(irq); - irq_hw_number_t hwirq = irqd_to_hwirq(d); - - if (!test_bit(hwirq, msi_irq_in_use)) { - msi = irq_get_msi_desc(irq); - port = msi_desc_to_pci_sysdata(msi); - dev_err(port->dev, "Trying to free unused MSI#%d\n", irq); - } else { - clear_bit(hwirq, msi_irq_in_use); - } -} - -/** - * xilinx_pcie_assign_msi - Allocate MSI number - * - * Return: A valid IRQ on success and error value on failure. - */ -static int xilinx_pcie_assign_msi(void) -{ - int pos; - - pos = find_first_zero_bit(msi_irq_in_use, XILINX_NUM_MSI_IRQS); - if (pos < XILINX_NUM_MSI_IRQS) - set_bit(pos, msi_irq_in_use); - else - return -ENOSPC; - - return pos; -} - -/** - * xilinx_msi_teardown_irq - Destroy the MSI - * @chip: MSI Chip descriptor - * @irq: MSI IRQ to destroy - */ -static void xilinx_msi_teardown_irq(struct msi_controller *chip, - unsigned int irq) -{ - xilinx_pcie_destroy_msi(irq); - irq_dispose_mapping(irq); -} - -/** - * xilinx_pcie_msi_setup_irq - Setup MSI request - * @chip: MSI chip pointer - * @pdev: PCIe device pointer - * @desc: MSI descriptor pointer - * - * Return: '0' on success and error value on failure - */ -static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip, - struct pci_dev *pdev, - struct msi_desc *desc) -{ - struct xilinx_pcie_port *port = pdev->bus->sysdata; - unsigned int irq; - int hwirq; - struct msi_msg msg; - phys_addr_t msg_addr; - - hwirq = xilinx_pcie_assign_msi(); - if (hwirq < 0) - return hwirq; - - irq = irq_create_mapping(port->msi_domain, hwirq); - if (!irq) - return -EINVAL; - - irq_set_msi_desc(irq, desc); - - msg_addr = virt_to_phys((void *)port->msi_pages); - - msg.address_hi = 0; - msg.address_lo = msg_addr; - msg.data = irq; - - pci_write_msi_msg(irq, &msg); - - return 0; -} - -/* MSI Chip Descriptor */ -static struct msi_controller xilinx_pcie_msi_chip = { - .setup_irq = xilinx_pcie_msi_setup_irq, - .teardown_irq = xilinx_msi_teardown_irq, -}; - -/* HW Interrupt Chip Descriptor */ -static struct irq_chip xilinx_msi_irq_chip = { - .name = "Xilinx PCIe MSI", - .irq_enable = pci_msi_unmask_irq, - .irq_disable = pci_msi_mask_irq, - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; - -/** - * xilinx_pcie_msi_map - Set the handler for the MSI and mark IRQ as valid - * @domain: IRQ domain - * @irq: Virtual IRQ number - * @hwirq: HW interrupt number - * - * Return: Always returns 0. - */ -static int xilinx_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &xilinx_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -/* IRQ Domain operations */ -static const struct irq_domain_ops msi_domain_ops = { - .map = xilinx_pcie_msi_map, -}; - -/** - * xilinx_pcie_enable_msi - Enable MSI support - * @port: PCIe port information - */ -static void xilinx_pcie_enable_msi(struct xilinx_pcie_port *port) -{ - phys_addr_t msg_addr; - - port->msi_pages = __get_free_pages(GFP_KERNEL, 0); - msg_addr = virt_to_phys((void *)port->msi_pages); - pcie_write(port, 0x0, XILINX_PCIE_REG_MSIBASE1); - pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2); -} - -/* INTx Functions */ - -/** - * xilinx_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid - * @domain: IRQ domain - * @irq: Virtual IRQ number - * @hwirq: HW interrupt number - * - * Return: Always returns 0. - */ -static int xilinx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -/* INTx IRQ Domain operations */ -static const struct irq_domain_ops intx_domain_ops = { - .map = xilinx_pcie_intx_map, - .xlate = pci_irqd_intx_xlate, -}; - -/* PCIe HW Functions */ - -/** - * xilinx_pcie_intr_handler - Interrupt Service Handler - * @irq: IRQ number - * @data: PCIe port information - * - * Return: IRQ_HANDLED on success and IRQ_NONE on failure - */ -static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) -{ - struct xilinx_pcie_port *port = (struct xilinx_pcie_port *)data; - struct device *dev = port->dev; - u32 val, mask, status; - - /* Read interrupt decode and mask registers */ - val = pcie_read(port, XILINX_PCIE_REG_IDR); - mask = pcie_read(port, XILINX_PCIE_REG_IMR); - - status = val & mask; - if (!status) - return IRQ_NONE; - - if (status & XILINX_PCIE_INTR_LINK_DOWN) - dev_warn(dev, "Link Down\n"); - - if (status & XILINX_PCIE_INTR_ECRC_ERR) - dev_warn(dev, "ECRC failed\n"); - - if (status & XILINX_PCIE_INTR_STR_ERR) - dev_warn(dev, "Streaming error\n"); - - if (status & XILINX_PCIE_INTR_HOT_RESET) - dev_info(dev, "Hot reset\n"); - - if (status & XILINX_PCIE_INTR_CFG_TIMEOUT) - dev_warn(dev, "ECAM access timeout\n"); - - if (status & XILINX_PCIE_INTR_CORRECTABLE) { - dev_warn(dev, "Correctable error message\n"); - xilinx_pcie_clear_err_interrupts(port); - } - - if (status & XILINX_PCIE_INTR_NONFATAL) { - dev_warn(dev, "Non fatal error message\n"); - xilinx_pcie_clear_err_interrupts(port); - } - - if (status & XILINX_PCIE_INTR_FATAL) { - dev_warn(dev, "Fatal error message\n"); - xilinx_pcie_clear_err_interrupts(port); - } - - if (status & (XILINX_PCIE_INTR_INTX | XILINX_PCIE_INTR_MSI)) { - val = pcie_read(port, XILINX_PCIE_REG_RPIFR1); - - /* Check whether interrupt valid */ - if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) { - dev_warn(dev, "RP Intr FIFO1 read error\n"); - goto error; - } - - /* Decode the IRQ number */ - if (val & XILINX_PCIE_RPIFR1_MSI_INTR) { - val = pcie_read(port, XILINX_PCIE_REG_RPIFR2) & - XILINX_PCIE_RPIFR2_MSG_DATA; - } else { - val = (val & XILINX_PCIE_RPIFR1_INTR_MASK) >> - XILINX_PCIE_RPIFR1_INTR_SHIFT; - val = irq_find_mapping(port->leg_domain, val); - } - - /* Clear interrupt FIFO register 1 */ - pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK, - XILINX_PCIE_REG_RPIFR1); - - /* Handle the interrupt */ - if (IS_ENABLED(CONFIG_PCI_MSI) || - !(val & XILINX_PCIE_RPIFR1_MSI_INTR)) - generic_handle_irq(val); - } - - if (status & XILINX_PCIE_INTR_SLV_UNSUPP) - dev_warn(dev, "Slave unsupported request\n"); - - if (status & XILINX_PCIE_INTR_SLV_UNEXP) - dev_warn(dev, "Slave unexpected completion\n"); - - if (status & XILINX_PCIE_INTR_SLV_COMPL) - dev_warn(dev, "Slave completion timeout\n"); - - if (status & XILINX_PCIE_INTR_SLV_ERRP) - dev_warn(dev, "Slave Error Poison\n"); - - if (status & XILINX_PCIE_INTR_SLV_CMPABT) - dev_warn(dev, "Slave Completer Abort\n"); - - if (status & XILINX_PCIE_INTR_SLV_ILLBUR) - dev_warn(dev, "Slave Illegal Burst\n"); - - if (status & XILINX_PCIE_INTR_MST_DECERR) - dev_warn(dev, "Master decode error\n"); - - if (status & XILINX_PCIE_INTR_MST_SLVERR) - dev_warn(dev, "Master slave error\n"); - - if (status & XILINX_PCIE_INTR_MST_ERRP) - dev_warn(dev, "Master error poison\n"); - -error: - /* Clear the Interrupt Decode register */ - pcie_write(port, status, XILINX_PCIE_REG_IDR); - - return IRQ_HANDLED; -} - -/** - * xilinx_pcie_init_irq_domain - Initialize IRQ domain - * @port: PCIe port information - * - * Return: '0' on success and error value on failure - */ -static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port) -{ - struct device *dev = port->dev; - struct device_node *node = dev->of_node; - struct device_node *pcie_intc_node; - - /* Setup INTx */ - pcie_intc_node = of_get_next_child(node, NULL); - if (!pcie_intc_node) { - dev_err(dev, "No PCIe Intc node found\n"); - return -ENODEV; - } - - port->leg_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, - &intx_domain_ops, - port); - if (!port->leg_domain) { - dev_err(dev, "Failed to get a INTx IRQ domain\n"); - return -ENODEV; - } - - /* Setup MSI */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - port->msi_domain = irq_domain_add_linear(node, - XILINX_NUM_MSI_IRQS, - &msi_domain_ops, - &xilinx_pcie_msi_chip); - if (!port->msi_domain) { - dev_err(dev, "Failed to get a MSI IRQ domain\n"); - return -ENODEV; - } - - xilinx_pcie_enable_msi(port); - } - - return 0; -} - -/** - * xilinx_pcie_init_port - Initialize hardware - * @port: PCIe port information - */ -static void xilinx_pcie_init_port(struct xilinx_pcie_port *port) -{ - struct device *dev = port->dev; - - if (xilinx_pcie_link_up(port)) - dev_info(dev, "PCIe Link is UP\n"); - else - dev_info(dev, "PCIe Link is DOWN\n"); - - /* Disable all interrupts */ - pcie_write(port, ~XILINX_PCIE_IDR_ALL_MASK, - XILINX_PCIE_REG_IMR); - - /* Clear pending interrupts */ - pcie_write(port, pcie_read(port, XILINX_PCIE_REG_IDR) & - XILINX_PCIE_IMR_ALL_MASK, - XILINX_PCIE_REG_IDR); - - /* Enable all interrupts we handle */ - pcie_write(port, XILINX_PCIE_IMR_ENABLE_MASK, XILINX_PCIE_REG_IMR); - - /* Enable the Bridge enable bit */ - pcie_write(port, pcie_read(port, XILINX_PCIE_REG_RPSC) | - XILINX_PCIE_REG_RPSC_BEN, - XILINX_PCIE_REG_RPSC); -} - -/** - * xilinx_pcie_parse_dt - Parse Device tree - * @port: PCIe port information - * - * Return: '0' on success and error value on failure - */ -static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port) -{ - struct device *dev = port->dev; - struct device_node *node = dev->of_node; - struct resource regs; - const char *type; - int err; - - type = of_get_property(node, "device_type", NULL); - if (!type || strcmp(type, "pci")) { - dev_err(dev, "invalid \"device_type\" %s\n", type); - return -EINVAL; - } - - err = of_address_to_resource(node, 0, ®s); - if (err) { - dev_err(dev, "missing \"reg\" property\n"); - return err; - } - - port->reg_base = devm_pci_remap_cfg_resource(dev, ®s); - if (IS_ERR(port->reg_base)) - return PTR_ERR(port->reg_base); - - port->irq = irq_of_parse_and_map(node, 0); - err = devm_request_irq(dev, port->irq, xilinx_pcie_intr_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "xilinx-pcie", port); - if (err) { - dev_err(dev, "unable to request irq %d\n", port->irq); - return err; - } - - return 0; -} - -/** - * xilinx_pcie_probe - Probe function - * @pdev: Platform device pointer - * - * Return: '0' on success and error value on failure - */ -static int xilinx_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct xilinx_pcie_port *port; - struct pci_bus *bus, *child; - struct pci_host_bridge *bridge; - int err; - resource_size_t iobase = 0; - LIST_HEAD(res); - - if (!dev->of_node) - return -ENODEV; - - bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port)); - if (!bridge) - return -ENODEV; - - port = pci_host_bridge_priv(bridge); - - port->dev = dev; - - err = xilinx_pcie_parse_dt(port); - if (err) { - dev_err(dev, "Parsing DT failed\n"); - return err; - } - - xilinx_pcie_init_port(port); - - err = xilinx_pcie_init_irq_domain(port); - if (err) { - dev_err(dev, "Failed creating IRQ Domain\n"); - return err; - } - - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res, - &iobase); - if (err) { - dev_err(dev, "Getting bridge resources failed\n"); - return err; - } - - err = devm_request_pci_bus_resources(dev, &res); - if (err) - goto error; - - - list_splice_init(&res, &bridge->windows); - bridge->dev.parent = dev; - bridge->sysdata = port; - bridge->busnr = 0; - bridge->ops = &xilinx_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; - -#ifdef CONFIG_PCI_MSI - xilinx_pcie_msi_chip.dev = dev; - bridge->msi = &xilinx_pcie_msi_chip; -#endif - err = pci_scan_root_bus_bridge(bridge); - if (err < 0) - goto error; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - return 0; - -error: - pci_free_resource_list(&res); - return err; -} - -static const struct of_device_id xilinx_pcie_of_match[] = { - { .compatible = "xlnx,axi-pcie-host-1.00.a", }, - {} -}; - -static struct platform_driver xilinx_pcie_driver = { - .driver = { - .name = "xilinx-pcie", - .of_match_table = xilinx_pcie_of_match, - .suppress_bind_attrs = true, - }, - .probe = xilinx_pcie_probe, -}; -builtin_platform_driver(xilinx_pcie_driver); diff --git a/drivers/pci/host/vmd.c b/drivers/pci/host/vmd.c deleted file mode 100644 index 942b64fc7f1f..000000000000 --- a/drivers/pci/host/vmd.c +++ /dev/null @@ -1,870 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Volume Management Device driver - * Copyright (c) 2015, Intel Corporation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define VMD_CFGBAR 0 -#define VMD_MEMBAR1 2 -#define VMD_MEMBAR2 4 - -#define PCI_REG_VMCAP 0x40 -#define BUS_RESTRICT_CAP(vmcap) (vmcap & 0x1) -#define PCI_REG_VMCONFIG 0x44 -#define BUS_RESTRICT_CFG(vmcfg) ((vmcfg >> 8) & 0x3) -#define PCI_REG_VMLOCK 0x70 -#define MB2_SHADOW_EN(vmlock) (vmlock & 0x2) - -enum vmd_features { - /* - * Device may contain registers which hint the physical location of the - * membars, in order to allow proper address translation during - * resource assignment to enable guest virtualization - */ - VMD_FEAT_HAS_MEMBAR_SHADOW = (1 << 0), - - /* - * Device may provide root port configuration information which limits - * bus numbering - */ - VMD_FEAT_HAS_BUS_RESTRICTIONS = (1 << 1), -}; - -/* - * Lock for manipulating VMD IRQ lists. - */ -static DEFINE_RAW_SPINLOCK(list_lock); - -/** - * struct vmd_irq - private data to map driver IRQ to the VMD shared vector - * @node: list item for parent traversal. - * @irq: back pointer to parent. - * @enabled: true if driver enabled IRQ - * @virq: the virtual IRQ value provided to the requesting driver. - * - * Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to - * a VMD IRQ using this structure. - */ -struct vmd_irq { - struct list_head node; - struct vmd_irq_list *irq; - bool enabled; - unsigned int virq; -}; - -/** - * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector - * @irq_list: the list of irq's the VMD one demuxes to. - * @srcu: SRCU struct for local synchronization. - * @count: number of child IRQs assigned to this vector; used to track - * sharing. - */ -struct vmd_irq_list { - struct list_head irq_list; - struct srcu_struct srcu; - unsigned int count; -}; - -struct vmd_dev { - struct pci_dev *dev; - - spinlock_t cfg_lock; - char __iomem *cfgbar; - - int msix_count; - struct vmd_irq_list *irqs; - - struct pci_sysdata sysdata; - struct resource resources[3]; - struct irq_domain *irq_domain; - struct pci_bus *bus; - -#ifdef CONFIG_X86_DEV_DMA_OPS - struct dma_map_ops dma_ops; - struct dma_domain dma_domain; -#endif -}; - -static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) -{ - return container_of(bus->sysdata, struct vmd_dev, sysdata); -} - -static inline unsigned int index_from_irqs(struct vmd_dev *vmd, - struct vmd_irq_list *irqs) -{ - return irqs - vmd->irqs; -} - -/* - * Drivers managing a device in a VMD domain allocate their own IRQs as before, - * but the MSI entry for the hardware it's driving will be programmed with a - * destination ID for the VMD MSI-X table. The VMD muxes interrupts in its - * domain into one of its own, and the VMD driver de-muxes these for the - * handlers sharing that VMD IRQ. The vmd irq_domain provides the operations - * and irq_chip to set this up. - */ -static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct vmd_irq *vmdirq = data->chip_data; - struct vmd_irq_list *irq = vmdirq->irq; - struct vmd_dev *vmd = irq_data_get_irq_handler_data(data); - - msg->address_hi = MSI_ADDR_BASE_HI; - msg->address_lo = MSI_ADDR_BASE_LO | - MSI_ADDR_DEST_ID(index_from_irqs(vmd, irq)); - msg->data = 0; -} - -/* - * We rely on MSI_FLAG_USE_DEF_CHIP_OPS to set the IRQ mask/unmask ops. - */ -static void vmd_irq_enable(struct irq_data *data) -{ - struct vmd_irq *vmdirq = data->chip_data; - unsigned long flags; - - raw_spin_lock_irqsave(&list_lock, flags); - WARN_ON(vmdirq->enabled); - list_add_tail_rcu(&vmdirq->node, &vmdirq->irq->irq_list); - vmdirq->enabled = true; - raw_spin_unlock_irqrestore(&list_lock, flags); - - data->chip->irq_unmask(data); -} - -static void vmd_irq_disable(struct irq_data *data) -{ - struct vmd_irq *vmdirq = data->chip_data; - unsigned long flags; - - data->chip->irq_mask(data); - - raw_spin_lock_irqsave(&list_lock, flags); - if (vmdirq->enabled) { - list_del_rcu(&vmdirq->node); - vmdirq->enabled = false; - } - raw_spin_unlock_irqrestore(&list_lock, flags); -} - -/* - * XXX: Stubbed until we develop acceptable way to not create conflicts with - * other devices sharing the same vector. - */ -static int vmd_irq_set_affinity(struct irq_data *data, - const struct cpumask *dest, bool force) -{ - return -EINVAL; -} - -static struct irq_chip vmd_msi_controller = { - .name = "VMD-MSI", - .irq_enable = vmd_irq_enable, - .irq_disable = vmd_irq_disable, - .irq_compose_msi_msg = vmd_compose_msi_msg, - .irq_set_affinity = vmd_irq_set_affinity, -}; - -static irq_hw_number_t vmd_get_hwirq(struct msi_domain_info *info, - msi_alloc_info_t *arg) -{ - return 0; -} - -/* - * XXX: We can be even smarter selecting the best IRQ once we solve the - * affinity problem. - */ -static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd, struct msi_desc *desc) -{ - int i, best = 1; - unsigned long flags; - - if (pci_is_bridge(msi_desc_to_pci_dev(desc)) || vmd->msix_count == 1) - return &vmd->irqs[0]; - - raw_spin_lock_irqsave(&list_lock, flags); - for (i = 1; i < vmd->msix_count; i++) - if (vmd->irqs[i].count < vmd->irqs[best].count) - best = i; - vmd->irqs[best].count++; - raw_spin_unlock_irqrestore(&list_lock, flags); - - return &vmd->irqs[best]; -} - -static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info, - unsigned int virq, irq_hw_number_t hwirq, - msi_alloc_info_t *arg) -{ - struct msi_desc *desc = arg->desc; - struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(desc)->bus); - struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL); - unsigned int index, vector; - - if (!vmdirq) - return -ENOMEM; - - INIT_LIST_HEAD(&vmdirq->node); - vmdirq->irq = vmd_next_irq(vmd, desc); - vmdirq->virq = virq; - index = index_from_irqs(vmd, vmdirq->irq); - vector = pci_irq_vector(vmd->dev, index); - - irq_domain_set_info(domain, virq, vector, info->chip, vmdirq, - handle_untracked_irq, vmd, NULL); - return 0; -} - -static void vmd_msi_free(struct irq_domain *domain, - struct msi_domain_info *info, unsigned int virq) -{ - struct vmd_irq *vmdirq = irq_get_chip_data(virq); - unsigned long flags; - - synchronize_srcu(&vmdirq->irq->srcu); - - /* XXX: Potential optimization to rebalance */ - raw_spin_lock_irqsave(&list_lock, flags); - vmdirq->irq->count--; - raw_spin_unlock_irqrestore(&list_lock, flags); - - kfree(vmdirq); -} - -static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev, - int nvec, msi_alloc_info_t *arg) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct vmd_dev *vmd = vmd_from_bus(pdev->bus); - - if (nvec > vmd->msix_count) - return vmd->msix_count; - - memset(arg, 0, sizeof(*arg)); - return 0; -} - -static void vmd_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) -{ - arg->desc = desc; -} - -static struct msi_domain_ops vmd_msi_domain_ops = { - .get_hwirq = vmd_get_hwirq, - .msi_init = vmd_msi_init, - .msi_free = vmd_msi_free, - .msi_prepare = vmd_msi_prepare, - .set_desc = vmd_set_desc, -}; - -static struct msi_domain_info vmd_msi_domain_info = { - .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX, - .ops = &vmd_msi_domain_ops, - .chip = &vmd_msi_controller, -}; - -#ifdef CONFIG_X86_DEV_DMA_OPS -/* - * VMD replaces the requester ID with its own. DMA mappings for devices in a - * VMD domain need to be mapped for the VMD, not the device requiring - * the mapping. - */ -static struct device *to_vmd_dev(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct vmd_dev *vmd = vmd_from_bus(pdev->bus); - - return &vmd->dev->dev; -} - -static const struct dma_map_ops *vmd_dma_ops(struct device *dev) -{ - return get_dma_ops(to_vmd_dev(dev)); -} - -static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr, - gfp_t flag, unsigned long attrs) -{ - return vmd_dma_ops(dev)->alloc(to_vmd_dev(dev), size, addr, flag, - attrs); -} - -static void vmd_free(struct device *dev, size_t size, void *vaddr, - dma_addr_t addr, unsigned long attrs) -{ - return vmd_dma_ops(dev)->free(to_vmd_dev(dev), size, vaddr, addr, - attrs); -} - -static int vmd_mmap(struct device *dev, struct vm_area_struct *vma, - void *cpu_addr, dma_addr_t addr, size_t size, - unsigned long attrs) -{ - return vmd_dma_ops(dev)->mmap(to_vmd_dev(dev), vma, cpu_addr, addr, - size, attrs); -} - -static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt, - void *cpu_addr, dma_addr_t addr, size_t size, - unsigned long attrs) -{ - return vmd_dma_ops(dev)->get_sgtable(to_vmd_dev(dev), sgt, cpu_addr, - addr, size, attrs); -} - -static dma_addr_t vmd_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, - unsigned long attrs) -{ - return vmd_dma_ops(dev)->map_page(to_vmd_dev(dev), page, offset, size, - dir, attrs); -} - -static void vmd_unmap_page(struct device *dev, dma_addr_t addr, size_t size, - enum dma_data_direction dir, unsigned long attrs) -{ - vmd_dma_ops(dev)->unmap_page(to_vmd_dev(dev), addr, size, dir, attrs); -} - -static int vmd_map_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir, unsigned long attrs) -{ - return vmd_dma_ops(dev)->map_sg(to_vmd_dev(dev), sg, nents, dir, attrs); -} - -static void vmd_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir, unsigned long attrs) -{ - vmd_dma_ops(dev)->unmap_sg(to_vmd_dev(dev), sg, nents, dir, attrs); -} - -static void vmd_sync_single_for_cpu(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir) -{ - vmd_dma_ops(dev)->sync_single_for_cpu(to_vmd_dev(dev), addr, size, dir); -} - -static void vmd_sync_single_for_device(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir) -{ - vmd_dma_ops(dev)->sync_single_for_device(to_vmd_dev(dev), addr, size, - dir); -} - -static void vmd_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - vmd_dma_ops(dev)->sync_sg_for_cpu(to_vmd_dev(dev), sg, nents, dir); -} - -static void vmd_sync_sg_for_device(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - vmd_dma_ops(dev)->sync_sg_for_device(to_vmd_dev(dev), sg, nents, dir); -} - -static int vmd_mapping_error(struct device *dev, dma_addr_t addr) -{ - return vmd_dma_ops(dev)->mapping_error(to_vmd_dev(dev), addr); -} - -static int vmd_dma_supported(struct device *dev, u64 mask) -{ - return vmd_dma_ops(dev)->dma_supported(to_vmd_dev(dev), mask); -} - -#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK -static u64 vmd_get_required_mask(struct device *dev) -{ - return vmd_dma_ops(dev)->get_required_mask(to_vmd_dev(dev)); -} -#endif - -static void vmd_teardown_dma_ops(struct vmd_dev *vmd) -{ - struct dma_domain *domain = &vmd->dma_domain; - - if (get_dma_ops(&vmd->dev->dev)) - del_dma_domain(domain); -} - -#define ASSIGN_VMD_DMA_OPS(source, dest, fn) \ - do { \ - if (source->fn) \ - dest->fn = vmd_##fn; \ - } while (0) - -static void vmd_setup_dma_ops(struct vmd_dev *vmd) -{ - const struct dma_map_ops *source = get_dma_ops(&vmd->dev->dev); - struct dma_map_ops *dest = &vmd->dma_ops; - struct dma_domain *domain = &vmd->dma_domain; - - domain->domain_nr = vmd->sysdata.domain; - domain->dma_ops = dest; - - if (!source) - return; - ASSIGN_VMD_DMA_OPS(source, dest, alloc); - ASSIGN_VMD_DMA_OPS(source, dest, free); - ASSIGN_VMD_DMA_OPS(source, dest, mmap); - ASSIGN_VMD_DMA_OPS(source, dest, get_sgtable); - ASSIGN_VMD_DMA_OPS(source, dest, map_page); - ASSIGN_VMD_DMA_OPS(source, dest, unmap_page); - ASSIGN_VMD_DMA_OPS(source, dest, map_sg); - ASSIGN_VMD_DMA_OPS(source, dest, unmap_sg); - ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_cpu); - ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_device); - ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_cpu); - ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_device); - ASSIGN_VMD_DMA_OPS(source, dest, mapping_error); - ASSIGN_VMD_DMA_OPS(source, dest, dma_supported); -#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK - ASSIGN_VMD_DMA_OPS(source, dest, get_required_mask); -#endif - add_dma_domain(domain); -} -#undef ASSIGN_VMD_DMA_OPS -#else -static void vmd_teardown_dma_ops(struct vmd_dev *vmd) {} -static void vmd_setup_dma_ops(struct vmd_dev *vmd) {} -#endif - -static char __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, - unsigned int devfn, int reg, int len) -{ - char __iomem *addr = vmd->cfgbar + - (bus->number << 20) + (devfn << 12) + reg; - - if ((addr - vmd->cfgbar) + len >= - resource_size(&vmd->dev->resource[VMD_CFGBAR])) - return NULL; - - return addr; -} - -/* - * CPU may deadlock if config space is not serialized on some versions of this - * hardware, so all config space access is done under a spinlock. - */ -static int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg, - int len, u32 *value) -{ - struct vmd_dev *vmd = vmd_from_bus(bus); - char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len); - unsigned long flags; - int ret = 0; - - if (!addr) - return -EFAULT; - - spin_lock_irqsave(&vmd->cfg_lock, flags); - switch (len) { - case 1: - *value = readb(addr); - break; - case 2: - *value = readw(addr); - break; - case 4: - *value = readl(addr); - break; - default: - ret = -EINVAL; - break; - } - spin_unlock_irqrestore(&vmd->cfg_lock, flags); - return ret; -} - -/* - * VMD h/w converts non-posted config writes to posted memory writes. The - * read-back in this function forces the completion so it returns only after - * the config space was written, as expected. - */ -static int vmd_pci_write(struct pci_bus *bus, unsigned int devfn, int reg, - int len, u32 value) -{ - struct vmd_dev *vmd = vmd_from_bus(bus); - char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len); - unsigned long flags; - int ret = 0; - - if (!addr) - return -EFAULT; - - spin_lock_irqsave(&vmd->cfg_lock, flags); - switch (len) { - case 1: - writeb(value, addr); - readb(addr); - break; - case 2: - writew(value, addr); - readw(addr); - break; - case 4: - writel(value, addr); - readl(addr); - break; - default: - ret = -EINVAL; - break; - } - spin_unlock_irqrestore(&vmd->cfg_lock, flags); - return ret; -} - -static struct pci_ops vmd_ops = { - .read = vmd_pci_read, - .write = vmd_pci_write, -}; - -static void vmd_attach_resources(struct vmd_dev *vmd) -{ - vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1]; - vmd->dev->resource[VMD_MEMBAR2].child = &vmd->resources[2]; -} - -static void vmd_detach_resources(struct vmd_dev *vmd) -{ - vmd->dev->resource[VMD_MEMBAR1].child = NULL; - vmd->dev->resource[VMD_MEMBAR2].child = NULL; -} - -/* - * VMD domains start at 0x10000 to not clash with ACPI _SEG domains. - * Per ACPI r6.0, sec 6.5.6, _SEG returns an integer, of which the lower - * 16 bits are the PCI Segment Group (domain) number. Other bits are - * currently reserved. - */ -static int vmd_find_free_domain(void) -{ - int domain = 0xffff; - struct pci_bus *bus = NULL; - - while ((bus = pci_find_next_bus(bus)) != NULL) - domain = max_t(int, domain, pci_domain_nr(bus)); - return domain + 1; -} - -static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) -{ - struct pci_sysdata *sd = &vmd->sysdata; - struct fwnode_handle *fn; - struct resource *res; - u32 upper_bits; - unsigned long flags; - LIST_HEAD(resources); - resource_size_t offset[2] = {0}; - resource_size_t membar2_offset = 0x2000, busn_start = 0; - - /* - * Shadow registers may exist in certain VMD device ids which allow - * guests to correctly assign host physical addresses to the root ports - * and child devices. These registers will either return the host value - * or 0, depending on an enable bit in the VMD device. - */ - if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) { - u32 vmlock; - int ret; - - membar2_offset = 0x2018; - ret = pci_read_config_dword(vmd->dev, PCI_REG_VMLOCK, &vmlock); - if (ret || vmlock == ~0) - return -ENODEV; - - if (MB2_SHADOW_EN(vmlock)) { - void __iomem *membar2; - - membar2 = pci_iomap(vmd->dev, VMD_MEMBAR2, 0); - if (!membar2) - return -ENOMEM; - offset[0] = vmd->dev->resource[VMD_MEMBAR1].start - - readq(membar2 + 0x2008); - offset[1] = vmd->dev->resource[VMD_MEMBAR2].start - - readq(membar2 + 0x2010); - pci_iounmap(vmd->dev, membar2); - } - } - - /* - * Certain VMD devices may have a root port configuration option which - * limits the bus range to between 0-127 or 128-255 - */ - if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { - u32 vmcap, vmconfig; - - pci_read_config_dword(vmd->dev, PCI_REG_VMCAP, &vmcap); - pci_read_config_dword(vmd->dev, PCI_REG_VMCONFIG, &vmconfig); - if (BUS_RESTRICT_CAP(vmcap) && - (BUS_RESTRICT_CFG(vmconfig) == 0x1)) - busn_start = 128; - } - - res = &vmd->dev->resource[VMD_CFGBAR]; - vmd->resources[0] = (struct resource) { - .name = "VMD CFGBAR", - .start = busn_start, - .end = busn_start + (resource_size(res) >> 20) - 1, - .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED, - }; - - /* - * If the window is below 4GB, clear IORESOURCE_MEM_64 so we can - * put 32-bit resources in the window. - * - * There's no hardware reason why a 64-bit window *couldn't* - * contain a 32-bit resource, but pbus_size_mem() computes the - * bridge window size assuming a 64-bit window will contain no - * 32-bit resources. __pci_assign_resource() enforces that - * artificial restriction to make sure everything will fit. - * - * The only way we could use a 64-bit non-prefechable MEMBAR is - * if its address is <4GB so that we can convert it to a 32-bit - * resource. To be visible to the host OS, all VMD endpoints must - * be initially configured by platform BIOS, which includes setting - * up these resources. We can assume the device is configured - * according to the platform needs. - */ - res = &vmd->dev->resource[VMD_MEMBAR1]; - upper_bits = upper_32_bits(res->end); - flags = res->flags & ~IORESOURCE_SIZEALIGN; - if (!upper_bits) - flags &= ~IORESOURCE_MEM_64; - vmd->resources[1] = (struct resource) { - .name = "VMD MEMBAR1", - .start = res->start, - .end = res->end, - .flags = flags, - .parent = res, - }; - - res = &vmd->dev->resource[VMD_MEMBAR2]; - upper_bits = upper_32_bits(res->end); - flags = res->flags & ~IORESOURCE_SIZEALIGN; - if (!upper_bits) - flags &= ~IORESOURCE_MEM_64; - vmd->resources[2] = (struct resource) { - .name = "VMD MEMBAR2", - .start = res->start + membar2_offset, - .end = res->end, - .flags = flags, - .parent = res, - }; - - sd->vmd_domain = true; - sd->domain = vmd_find_free_domain(); - if (sd->domain < 0) - return sd->domain; - - sd->node = pcibus_to_node(vmd->dev->bus); - - fn = irq_domain_alloc_named_id_fwnode("VMD-MSI", vmd->sysdata.domain); - if (!fn) - return -ENODEV; - - vmd->irq_domain = pci_msi_create_irq_domain(fn, &vmd_msi_domain_info, - x86_vector_domain); - irq_domain_free_fwnode(fn); - if (!vmd->irq_domain) - return -ENODEV; - - pci_add_resource(&resources, &vmd->resources[0]); - pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]); - pci_add_resource_offset(&resources, &vmd->resources[2], offset[1]); - - vmd->bus = pci_create_root_bus(&vmd->dev->dev, busn_start, &vmd_ops, - sd, &resources); - if (!vmd->bus) { - pci_free_resource_list(&resources); - irq_domain_remove(vmd->irq_domain); - return -ENODEV; - } - - vmd_attach_resources(vmd); - vmd_setup_dma_ops(vmd); - dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain); - pci_rescan_bus(vmd->bus); - - WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj, - "domain"), "Can't create symlink to domain\n"); - return 0; -} - -static irqreturn_t vmd_irq(int irq, void *data) -{ - struct vmd_irq_list *irqs = data; - struct vmd_irq *vmdirq; - int idx; - - idx = srcu_read_lock(&irqs->srcu); - list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) - generic_handle_irq(vmdirq->virq); - srcu_read_unlock(&irqs->srcu, idx); - - return IRQ_HANDLED; -} - -static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - struct vmd_dev *vmd; - int i, err; - - if (resource_size(&dev->resource[VMD_CFGBAR]) < (1 << 20)) - return -ENOMEM; - - vmd = devm_kzalloc(&dev->dev, sizeof(*vmd), GFP_KERNEL); - if (!vmd) - return -ENOMEM; - - vmd->dev = dev; - err = pcim_enable_device(dev); - if (err < 0) - return err; - - vmd->cfgbar = pcim_iomap(dev, VMD_CFGBAR, 0); - if (!vmd->cfgbar) - return -ENOMEM; - - pci_set_master(dev); - if (dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64)) && - dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32))) - return -ENODEV; - - vmd->msix_count = pci_msix_vec_count(dev); - if (vmd->msix_count < 0) - return -ENODEV; - - vmd->msix_count = pci_alloc_irq_vectors(dev, 1, vmd->msix_count, - PCI_IRQ_MSIX); - if (vmd->msix_count < 0) - return vmd->msix_count; - - vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs), - GFP_KERNEL); - if (!vmd->irqs) - return -ENOMEM; - - for (i = 0; i < vmd->msix_count; i++) { - err = init_srcu_struct(&vmd->irqs[i].srcu); - if (err) - return err; - - INIT_LIST_HEAD(&vmd->irqs[i].irq_list); - err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i), - vmd_irq, IRQF_NO_THREAD, - "vmd", &vmd->irqs[i]); - if (err) - return err; - } - - spin_lock_init(&vmd->cfg_lock); - pci_set_drvdata(dev, vmd); - err = vmd_enable_domain(vmd, (unsigned long) id->driver_data); - if (err) - return err; - - dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n", - vmd->sysdata.domain); - return 0; -} - -static void vmd_cleanup_srcu(struct vmd_dev *vmd) -{ - int i; - - for (i = 0; i < vmd->msix_count; i++) - cleanup_srcu_struct(&vmd->irqs[i].srcu); -} - -static void vmd_remove(struct pci_dev *dev) -{ - struct vmd_dev *vmd = pci_get_drvdata(dev); - - vmd_detach_resources(vmd); - sysfs_remove_link(&vmd->dev->dev.kobj, "domain"); - pci_stop_root_bus(vmd->bus); - pci_remove_root_bus(vmd->bus); - vmd_cleanup_srcu(vmd); - vmd_teardown_dma_ops(vmd); - irq_domain_remove(vmd->irq_domain); -} - -#ifdef CONFIG_PM_SLEEP -static int vmd_suspend(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct vmd_dev *vmd = pci_get_drvdata(pdev); - int i; - - for (i = 0; i < vmd->msix_count; i++) - devm_free_irq(dev, pci_irq_vector(pdev, i), &vmd->irqs[i]); - - pci_save_state(pdev); - return 0; -} - -static int vmd_resume(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct vmd_dev *vmd = pci_get_drvdata(pdev); - int err, i; - - for (i = 0; i < vmd->msix_count; i++) { - err = devm_request_irq(dev, pci_irq_vector(pdev, i), - vmd_irq, IRQF_NO_THREAD, - "vmd", &vmd->irqs[i]); - if (err) - return err; - } - - pci_restore_state(pdev); - return 0; -} -#endif -static SIMPLE_DEV_PM_OPS(vmd_dev_pm_ops, vmd_suspend, vmd_resume); - -static const struct pci_device_id vmd_ids[] = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_201D),}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0), - .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | - VMD_FEAT_HAS_BUS_RESTRICTIONS,}, - {0,} -}; -MODULE_DEVICE_TABLE(pci, vmd_ids); - -static struct pci_driver vmd_drv = { - .name = "vmd", - .id_table = vmd_ids, - .probe = vmd_probe, - .remove = vmd_remove, - .driver = { - .pm = &vmd_dev_pm_ops, - }, -}; -module_pci_driver(vmd_drv); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.6"); -- cgit v1.2.3 From 0054ca8e108e6fb5391c4f72b5233ee0c1f0e47e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:31:42 -0500 Subject: PCI/AER: Remove forward declarations Reorder code to remove forward declarations. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/aerdrv.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 9735c19bf39c..c6d1b664a6ea 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -23,22 +23,6 @@ #include "aerdrv.h" #include "../../pci.h" -static int aer_probe(struct pcie_device *dev); -static void aer_remove(struct pcie_device *dev); -static void aer_error_resume(struct pci_dev *dev); -static pci_ers_result_t aer_root_reset(struct pci_dev *dev); - -static struct pcie_port_service_driver aerdriver = { - .name = "aer", - .port_type = PCI_EXP_TYPE_ROOT_PORT, - .service = PCIE_PORT_SERVICE_AER, - - .probe = aer_probe, - .remove = aer_remove, - .error_resume = aer_error_resume, - .reset_link = aer_root_reset, -}; - static int pcie_aer_disable; void pci_no_aer(void) @@ -357,6 +341,17 @@ static void aer_error_resume(struct pci_dev *dev) pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); } +static struct pcie_port_service_driver aerdriver = { + .name = "aer", + .port_type = PCI_EXP_TYPE_ROOT_PORT, + .service = PCIE_PORT_SERVICE_AER, + + .probe = aer_probe, + .remove = aer_remove, + .error_resume = aer_error_resume, + .reset_link = aer_root_reset, +}; + /** * aer_service_init - register AER root service driver * -- cgit v1.2.3 From 3c43a64cb3e092bec27c79b67b28bba4a9296a84 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:31:57 -0500 Subject: PCI/AER: Reorder code to group probe/remove stuff together Reorder code to group probe/remove stuff together. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/aerdrv.c | 116 +++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 58 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index c6d1b664a6ea..8d3bc28821ba 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -35,6 +35,64 @@ bool pci_aer_available(void) return !pcie_aer_disable && pci_msi_enabled(); } +/** + * aer_irq - Root Port's ISR + * @irq: IRQ assigned to Root Port + * @context: pointer to Root Port data structure + * + * Invoked when Root Port detects AER messages. + */ +irqreturn_t aer_irq(int irq, void *context) +{ + unsigned int status, id; + struct pcie_device *pdev = (struct pcie_device *)context; + struct aer_rpc *rpc = get_service_data(pdev); + int next_prod_idx; + unsigned long flags; + int pos; + + pos = pdev->port->aer_cap; + /* + * Must lock access to Root Error Status Reg, Root Error ID Reg, + * and Root error producer/consumer index + */ + spin_lock_irqsave(&rpc->e_lock, flags); + + /* Read error status */ + pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); + if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) { + spin_unlock_irqrestore(&rpc->e_lock, flags); + return IRQ_NONE; + } + + /* Read error source and clear error status */ + pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id); + pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); + + /* Store error source for later DPC handler */ + next_prod_idx = rpc->prod_idx + 1; + if (next_prod_idx == AER_ERROR_SOURCES_MAX) + next_prod_idx = 0; + if (next_prod_idx == rpc->cons_idx) { + /* + * Error Storm Condition - possibly the same error occurred. + * Drop the error. + */ + spin_unlock_irqrestore(&rpc->e_lock, flags); + return IRQ_HANDLED; + } + rpc->e_sources[rpc->prod_idx].status = status; + rpc->e_sources[rpc->prod_idx].id = id; + rpc->prod_idx = next_prod_idx; + spin_unlock_irqrestore(&rpc->e_lock, flags); + + /* Invoke DPC handler */ + schedule_work(&rpc->dpc_handler); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(aer_irq); + static int set_device_error_reporting(struct pci_dev *dev, void *data) { bool enable = *((bool *)data); @@ -141,64 +199,6 @@ static void aer_disable_rootport(struct aer_rpc *rpc) pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); } -/** - * aer_irq - Root Port's ISR - * @irq: IRQ assigned to Root Port - * @context: pointer to Root Port data structure - * - * Invoked when Root Port detects AER messages. - */ -irqreturn_t aer_irq(int irq, void *context) -{ - unsigned int status, id; - struct pcie_device *pdev = (struct pcie_device *)context; - struct aer_rpc *rpc = get_service_data(pdev); - int next_prod_idx; - unsigned long flags; - int pos; - - pos = pdev->port->aer_cap; - /* - * Must lock access to Root Error Status Reg, Root Error ID Reg, - * and Root error producer/consumer index - */ - spin_lock_irqsave(&rpc->e_lock, flags); - - /* Read error status */ - pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); - if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) { - spin_unlock_irqrestore(&rpc->e_lock, flags); - return IRQ_NONE; - } - - /* Read error source and clear error status */ - pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id); - pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); - - /* Store error source for later DPC handler */ - next_prod_idx = rpc->prod_idx + 1; - if (next_prod_idx == AER_ERROR_SOURCES_MAX) - next_prod_idx = 0; - if (next_prod_idx == rpc->cons_idx) { - /* - * Error Storm Condition - possibly the same error occurred. - * Drop the error. - */ - spin_unlock_irqrestore(&rpc->e_lock, flags); - return IRQ_HANDLED; - } - rpc->e_sources[rpc->prod_idx].status = status; - rpc->e_sources[rpc->prod_idx].id = id; - rpc->prod_idx = next_prod_idx; - spin_unlock_irqrestore(&rpc->e_lock, flags); - - /* Invoke DPC handler */ - schedule_work(&rpc->dpc_handler); - - return IRQ_HANDLED; -} -EXPORT_SYMBOL_GPL(aer_irq); - /** * aer_alloc_rpc - allocate Root Port data structure * @dev: pointer to the pcie_dev data structure -- cgit v1.2.3 From fd3362cb73dee65d3f45b5bb58955dd2325848f9 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:33:39 -0500 Subject: PCI/AER: Squash aerdrv_core.c into aerdrv.c Squash aerdrv_core.c into aerdrv.c. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/Makefile | 2 +- drivers/pci/pcie/aer/aerdrv.c | 474 +++++++++++++++++++++++++++++++++++ drivers/pci/pcie/aer/aerdrv.h | 1 - drivers/pci/pcie/aer/aerdrv_core.c | 496 ------------------------------------- 4 files changed, 475 insertions(+), 498 deletions(-) delete mode 100644 drivers/pci/pcie/aer/aerdrv_core.c (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile index 09bd890875a3..c85bd0851903 100644 --- a/drivers/pci/pcie/aer/Makefile +++ b/drivers/pci/pcie/aer/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_PCIEAER) += aerdriver.o obj-$(CONFIG_PCIE_ECRC) += ecrc.o -aerdriver-objs := aerdrv_errprint.o aerdrv_core.o aerdrv.o +aerdriver-objs := aerdrv_errprint.o aerdrv.o aerdriver-$(CONFIG_ACPI) += aerdrv_acpi.o obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 8d3bc28821ba..c185069ef880 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "aerdrv.h" @@ -35,6 +36,479 @@ bool pci_aer_available(void) return !pcie_aer_disable && pci_msi_enabled(); } +#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) + +int pci_enable_pcie_error_reporting(struct pci_dev *dev) +{ + if (pcie_aer_get_firmware_first(dev)) + return -EIO; + + if (!dev->aer_cap) + return -EIO; + + return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); +} +EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); + +int pci_disable_pcie_error_reporting(struct pci_dev *dev) +{ + if (pcie_aer_get_firmware_first(dev)) + return -EIO; + + return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL, + PCI_EXP_AER_FLAGS); +} +EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); + +int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) +{ + int pos; + u32 status; + + pos = dev->aer_cap; + if (!pos) + return -EIO; + + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + if (status) + pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); + + return 0; +} +EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); + +int pci_cleanup_aer_error_status_regs(struct pci_dev *dev) +{ + int pos; + u32 status; + int port_type; + + if (!pci_is_pcie(dev)) + return -ENODEV; + + pos = dev->aer_cap; + if (!pos) + return -EIO; + + port_type = pci_pcie_type(dev); + if (port_type == PCI_EXP_TYPE_ROOT_PORT) { + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); + pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); + } + + pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); + pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status); + + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); + + return 0; +} + +int pci_aer_init(struct pci_dev *dev) +{ + dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + return pci_cleanup_aer_error_status_regs(dev); +} + +/** + * add_error_device - list device to be handled + * @e_info: pointer to error info + * @dev: pointer to pci_dev to be added + */ +static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) +{ + if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { + e_info->dev[e_info->error_dev_num] = dev; + e_info->error_dev_num++; + return 0; + } + return -ENOSPC; +} + +/** + * is_error_source - check whether the device is source of reported error + * @dev: pointer to pci_dev to be checked + * @e_info: pointer to reported error info + */ +static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) +{ + int pos; + u32 status, mask; + u16 reg16; + + /* + * When bus id is equal to 0, it might be a bad id + * reported by root port. + */ + if ((PCI_BUS_NUM(e_info->id) != 0) && + !(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) { + /* Device ID match? */ + if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) + return true; + + /* Continue id comparing if there is no multiple error */ + if (!e_info->multi_error_valid) + return false; + } + + /* + * When either + * 1) bus id is equal to 0. Some ports might lose the bus + * id of error source id; + * 2) bus flag PCI_BUS_FLAGS_NO_AERSID is set + * 3) There are multiple errors and prior ID comparing fails; + * We check AER status registers to find possible reporter. + */ + if (atomic_read(&dev->enable_cnt) == 0) + return false; + + /* Check if AER is enabled */ + pcie_capability_read_word(dev, PCI_EXP_DEVCTL, ®16); + if (!(reg16 & PCI_EXP_AER_FLAGS)) + return false; + + pos = dev->aer_cap; + if (!pos) + return false; + + /* Check if error is recorded */ + if (e_info->severity == AER_CORRECTABLE) { + pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask); + } else { + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); + } + if (status & ~mask) + return true; + + return false; +} + +static int find_device_iter(struct pci_dev *dev, void *data) +{ + struct aer_err_info *e_info = (struct aer_err_info *)data; + + if (is_error_source(dev, e_info)) { + /* List this device */ + if (add_error_device(e_info, dev)) { + /* We cannot handle more... Stop iteration */ + /* TODO: Should print error message here? */ + return 1; + } + + /* If there is only a single error, stop iteration */ + if (!e_info->multi_error_valid) + return 1; + } + return 0; +} + +/** + * find_source_device - search through device hierarchy for source device + * @parent: pointer to Root Port pci_dev data structure + * @e_info: including detailed error information such like id + * + * Return true if found. + * + * Invoked by DPC when error is detected at the Root Port. + * Caller of this function must set id, severity, and multi_error_valid of + * struct aer_err_info pointed by @e_info properly. This function must fill + * e_info->error_dev_num and e_info->dev[], based on the given information. + */ +static bool find_source_device(struct pci_dev *parent, + struct aer_err_info *e_info) +{ + struct pci_dev *dev = parent; + int result; + + /* Must reset in this function */ + e_info->error_dev_num = 0; + + /* Is Root Port an agent that sends error message? */ + result = find_device_iter(dev, e_info); + if (result) + return true; + + pci_walk_bus(parent->subordinate, find_device_iter, e_info); + + if (!e_info->error_dev_num) { + pci_printk(KERN_DEBUG, parent, "can't find device of ID%04x\n", + e_info->id); + return false; + } + return true; +} + +/** + * handle_error_source - handle logging error into an event log + * @dev: pointer to pci_dev data structure of error source device + * @info: comprehensive error information + * + * Invoked when an error being detected by Root Port. + */ +static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info) +{ + int pos; + + if (info->severity == AER_CORRECTABLE) { + /* + * Correctable error does not need software intervention. + * No need to go through error recovery process. + */ + pos = dev->aer_cap; + if (pos) + pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, + info->status); + } else if (info->severity == AER_NONFATAL) + pcie_do_nonfatal_recovery(dev); + else if (info->severity == AER_FATAL) + pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER); +} + +#ifdef CONFIG_ACPI_APEI_PCIEAER + +#define AER_RECOVER_RING_ORDER 4 +#define AER_RECOVER_RING_SIZE (1 << AER_RECOVER_RING_ORDER) + +struct aer_recover_entry { + u8 bus; + u8 devfn; + u16 domain; + int severity; + struct aer_capability_regs *regs; +}; + +static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry, + AER_RECOVER_RING_SIZE); + +static void aer_recover_work_func(struct work_struct *work) +{ + struct aer_recover_entry entry; + struct pci_dev *pdev; + + while (kfifo_get(&aer_recover_ring, &entry)) { + pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus, + entry.devfn); + if (!pdev) { + pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n", + entry.domain, entry.bus, + PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn)); + continue; + } + cper_print_aer(pdev, entry.severity, entry.regs); + if (entry.severity == AER_NONFATAL) + pcie_do_nonfatal_recovery(pdev); + else if (entry.severity == AER_FATAL) + pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_AER); + pci_dev_put(pdev); + } +} + +/* + * Mutual exclusion for writers of aer_recover_ring, reader side don't + * need lock, because there is only one reader and lock is not needed + * between reader and writer. + */ +static DEFINE_SPINLOCK(aer_recover_ring_lock); +static DECLARE_WORK(aer_recover_work, aer_recover_work_func); + +void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, + int severity, struct aer_capability_regs *aer_regs) +{ + unsigned long flags; + struct aer_recover_entry entry = { + .bus = bus, + .devfn = devfn, + .domain = domain, + .severity = severity, + .regs = aer_regs, + }; + + spin_lock_irqsave(&aer_recover_ring_lock, flags); + if (kfifo_put(&aer_recover_ring, entry)) + schedule_work(&aer_recover_work); + else + pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n", + domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + spin_unlock_irqrestore(&aer_recover_ring_lock, flags); +} +EXPORT_SYMBOL_GPL(aer_recover_queue); +#endif + +/** + * get_device_error_info - read error status from dev and store it to info + * @dev: pointer to the device expected to have a error record + * @info: pointer to structure to store the error record + * + * Return 1 on success, 0 on error. + * + * Note that @info is reused among all error devices. Clear fields properly. + */ +static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) +{ + int pos, temp; + + /* Must reset in this function */ + info->status = 0; + info->tlp_header_valid = 0; + + pos = dev->aer_cap; + + /* The device might not support AER */ + if (!pos) + return 0; + + if (info->severity == AER_CORRECTABLE) { + pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, + &info->status); + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, + &info->mask); + if (!(info->status & ~info->mask)) + return 0; + } else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + info->severity == AER_NONFATAL) { + + /* Link is still healthy for IO reads */ + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, + &info->status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, + &info->mask); + if (!(info->status & ~info->mask)) + return 0; + + /* Get First Error Pointer */ + pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp); + info->first_error = PCI_ERR_CAP_FEP(temp); + + if (info->status & AER_LOG_TLP_MASKS) { + info->tlp_header_valid = 1; + pci_read_config_dword(dev, + pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); + pci_read_config_dword(dev, + pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); + pci_read_config_dword(dev, + pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); + pci_read_config_dword(dev, + pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); + } + } + + return 1; +} + +static inline void aer_process_err_devices(struct aer_err_info *e_info) +{ + int i; + + /* Report all before handle them, not to lost records by reset etc. */ + for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { + if (get_device_error_info(e_info->dev[i], e_info)) + aer_print_error(e_info->dev[i], e_info); + } + for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { + if (get_device_error_info(e_info->dev[i], e_info)) + handle_error_source(e_info->dev[i], e_info); + } +} + +/** + * aer_isr_one_error - consume an error detected by root port + * @rpc: pointer to the root port which holds an error + * @e_src: pointer to an error source + */ +static void aer_isr_one_error(struct aer_rpc *rpc, + struct aer_err_source *e_src) +{ + struct pci_dev *pdev = rpc->rpd; + struct aer_err_info *e_info = &rpc->e_info; + + /* + * There is a possibility that both correctable error and + * uncorrectable error being logged. Report correctable error first. + */ + if (e_src->status & PCI_ERR_ROOT_COR_RCV) { + e_info->id = ERR_COR_ID(e_src->id); + e_info->severity = AER_CORRECTABLE; + + if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV) + e_info->multi_error_valid = 1; + else + e_info->multi_error_valid = 0; + aer_print_port_info(pdev, e_info); + + if (find_source_device(pdev, e_info)) + aer_process_err_devices(e_info); + } + + if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) { + e_info->id = ERR_UNCOR_ID(e_src->id); + + if (e_src->status & PCI_ERR_ROOT_FATAL_RCV) + e_info->severity = AER_FATAL; + else + e_info->severity = AER_NONFATAL; + + if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV) + e_info->multi_error_valid = 1; + else + e_info->multi_error_valid = 0; + + aer_print_port_info(pdev, e_info); + + if (find_source_device(pdev, e_info)) + aer_process_err_devices(e_info); + } +} + +/** + * get_e_source - retrieve an error source + * @rpc: pointer to the root port which holds an error + * @e_src: pointer to store retrieved error source + * + * Return 1 if an error source is retrieved, otherwise 0. + * + * Invoked by DPC handler to consume an error. + */ +static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src) +{ + unsigned long flags; + + /* Lock access to Root error producer/consumer index */ + spin_lock_irqsave(&rpc->e_lock, flags); + if (rpc->prod_idx == rpc->cons_idx) { + spin_unlock_irqrestore(&rpc->e_lock, flags); + return 0; + } + + *e_src = rpc->e_sources[rpc->cons_idx]; + rpc->cons_idx++; + if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) + rpc->cons_idx = 0; + spin_unlock_irqrestore(&rpc->e_lock, flags); + + return 1; +} + +/** + * aer_isr - consume errors detected by root port + * @work: definition of this work item + * + * Invoked, as DPC, when root port records new detected error + */ +static void aer_isr(struct work_struct *work) +{ + struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); + struct aer_err_source uninitialized_var(e_src); + + mutex_lock(&rpc->rpc_mutex); + while (get_e_source(rpc, &e_src)) + aer_isr_one_error(rpc, &e_src); + mutex_unlock(&rpc->rpc_mutex); +} + /** * aer_irq - Root Port's ISR * @irq: IRQ assigned to Root Port diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 6e0ad9a68fd9..059b89441acb 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -77,7 +77,6 @@ struct aer_rpc { }; extern struct bus_type pcie_port_bus_type; -void aer_isr(struct work_struct *work); void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info); irqreturn_t aer_irq(int irq, void *context); diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c deleted file mode 100644 index 42d4f3f32282..000000000000 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ /dev/null @@ -1,496 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Implement the core part of PCIe AER. When a PCIe error is delivered, an - * error message will be collected and printed to console, then an error - * recovery procedure will be executed by following the PCI error recovery - * rules. - * - * Copyright (C) 2006 Intel Corp. - * Tom Long Nguyen (tom.l.nguyen@intel.com) - * Zhang Yanmin (yanmin.zhang@intel.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "aerdrv.h" -#include "../../pci.h" - -#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) - -int pci_enable_pcie_error_reporting(struct pci_dev *dev) -{ - if (pcie_aer_get_firmware_first(dev)) - return -EIO; - - if (!dev->aer_cap) - return -EIO; - - return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); -} -EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); - -int pci_disable_pcie_error_reporting(struct pci_dev *dev) -{ - if (pcie_aer_get_firmware_first(dev)) - return -EIO; - - return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL, - PCI_EXP_AER_FLAGS); -} -EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); - -int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) -{ - int pos; - u32 status; - - pos = dev->aer_cap; - if (!pos) - return -EIO; - - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); - if (status) - pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); - - return 0; -} -EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); - -int pci_cleanup_aer_error_status_regs(struct pci_dev *dev) -{ - int pos; - u32 status; - int port_type; - - if (!pci_is_pcie(dev)) - return -ENODEV; - - pos = dev->aer_cap; - if (!pos) - return -EIO; - - port_type = pci_pcie_type(dev); - if (port_type == PCI_EXP_TYPE_ROOT_PORT) { - pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); - pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); - } - - pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); - pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status); - - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); - pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); - - return 0; -} - -int pci_aer_init(struct pci_dev *dev) -{ - dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); - return pci_cleanup_aer_error_status_regs(dev); -} - -/** - * add_error_device - list device to be handled - * @e_info: pointer to error info - * @dev: pointer to pci_dev to be added - */ -static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) -{ - if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { - e_info->dev[e_info->error_dev_num] = dev; - e_info->error_dev_num++; - return 0; - } - return -ENOSPC; -} - -/** - * is_error_source - check whether the device is source of reported error - * @dev: pointer to pci_dev to be checked - * @e_info: pointer to reported error info - */ -static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) -{ - int pos; - u32 status, mask; - u16 reg16; - - /* - * When bus id is equal to 0, it might be a bad id - * reported by root port. - */ - if ((PCI_BUS_NUM(e_info->id) != 0) && - !(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) { - /* Device ID match? */ - if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) - return true; - - /* Continue id comparing if there is no multiple error */ - if (!e_info->multi_error_valid) - return false; - } - - /* - * When either - * 1) bus id is equal to 0. Some ports might lose the bus - * id of error source id; - * 2) bus flag PCI_BUS_FLAGS_NO_AERSID is set - * 3) There are multiple errors and prior ID comparing fails; - * We check AER status registers to find possible reporter. - */ - if (atomic_read(&dev->enable_cnt) == 0) - return false; - - /* Check if AER is enabled */ - pcie_capability_read_word(dev, PCI_EXP_DEVCTL, ®16); - if (!(reg16 & PCI_EXP_AER_FLAGS)) - return false; - - pos = dev->aer_cap; - if (!pos) - return false; - - /* Check if error is recorded */ - if (e_info->severity == AER_CORRECTABLE) { - pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); - pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask); - } else { - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); - } - if (status & ~mask) - return true; - - return false; -} - -static int find_device_iter(struct pci_dev *dev, void *data) -{ - struct aer_err_info *e_info = (struct aer_err_info *)data; - - if (is_error_source(dev, e_info)) { - /* List this device */ - if (add_error_device(e_info, dev)) { - /* We cannot handle more... Stop iteration */ - /* TODO: Should print error message here? */ - return 1; - } - - /* If there is only a single error, stop iteration */ - if (!e_info->multi_error_valid) - return 1; - } - return 0; -} - -/** - * find_source_device - search through device hierarchy for source device - * @parent: pointer to Root Port pci_dev data structure - * @e_info: including detailed error information such like id - * - * Return true if found. - * - * Invoked by DPC when error is detected at the Root Port. - * Caller of this function must set id, severity, and multi_error_valid of - * struct aer_err_info pointed by @e_info properly. This function must fill - * e_info->error_dev_num and e_info->dev[], based on the given information. - */ -static bool find_source_device(struct pci_dev *parent, - struct aer_err_info *e_info) -{ - struct pci_dev *dev = parent; - int result; - - /* Must reset in this function */ - e_info->error_dev_num = 0; - - /* Is Root Port an agent that sends error message? */ - result = find_device_iter(dev, e_info); - if (result) - return true; - - pci_walk_bus(parent->subordinate, find_device_iter, e_info); - - if (!e_info->error_dev_num) { - pci_printk(KERN_DEBUG, parent, "can't find device of ID%04x\n", - e_info->id); - return false; - } - return true; -} - -/** - * handle_error_source - handle logging error into an event log - * @dev: pointer to pci_dev data structure of error source device - * @info: comprehensive error information - * - * Invoked when an error being detected by Root Port. - */ -static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info) -{ - int pos; - - if (info->severity == AER_CORRECTABLE) { - /* - * Correctable error does not need software intervention. - * No need to go through error recovery process. - */ - pos = dev->aer_cap; - if (pos) - pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, - info->status); - } else if (info->severity == AER_NONFATAL) - pcie_do_nonfatal_recovery(dev); - else if (info->severity == AER_FATAL) - pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER); -} - -#ifdef CONFIG_ACPI_APEI_PCIEAER - -#define AER_RECOVER_RING_ORDER 4 -#define AER_RECOVER_RING_SIZE (1 << AER_RECOVER_RING_ORDER) - -struct aer_recover_entry { - u8 bus; - u8 devfn; - u16 domain; - int severity; - struct aer_capability_regs *regs; -}; - -static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry, - AER_RECOVER_RING_SIZE); - -static void aer_recover_work_func(struct work_struct *work) -{ - struct aer_recover_entry entry; - struct pci_dev *pdev; - - while (kfifo_get(&aer_recover_ring, &entry)) { - pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus, - entry.devfn); - if (!pdev) { - pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n", - entry.domain, entry.bus, - PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn)); - continue; - } - cper_print_aer(pdev, entry.severity, entry.regs); - if (entry.severity == AER_NONFATAL) - pcie_do_nonfatal_recovery(pdev); - else if (entry.severity == AER_FATAL) - pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_AER); - pci_dev_put(pdev); - } -} - -/* - * Mutual exclusion for writers of aer_recover_ring, reader side don't - * need lock, because there is only one reader and lock is not needed - * between reader and writer. - */ -static DEFINE_SPINLOCK(aer_recover_ring_lock); -static DECLARE_WORK(aer_recover_work, aer_recover_work_func); - -void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, - int severity, struct aer_capability_regs *aer_regs) -{ - unsigned long flags; - struct aer_recover_entry entry = { - .bus = bus, - .devfn = devfn, - .domain = domain, - .severity = severity, - .regs = aer_regs, - }; - - spin_lock_irqsave(&aer_recover_ring_lock, flags); - if (kfifo_put(&aer_recover_ring, entry)) - schedule_work(&aer_recover_work); - else - pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n", - domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - spin_unlock_irqrestore(&aer_recover_ring_lock, flags); -} -EXPORT_SYMBOL_GPL(aer_recover_queue); -#endif - -/** - * get_device_error_info - read error status from dev and store it to info - * @dev: pointer to the device expected to have a error record - * @info: pointer to structure to store the error record - * - * Return 1 on success, 0 on error. - * - * Note that @info is reused among all error devices. Clear fields properly. - */ -static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) -{ - int pos, temp; - - /* Must reset in this function */ - info->status = 0; - info->tlp_header_valid = 0; - - pos = dev->aer_cap; - - /* The device might not support AER */ - if (!pos) - return 0; - - if (info->severity == AER_CORRECTABLE) { - pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, - &info->status); - pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, - &info->mask); - if (!(info->status & ~info->mask)) - return 0; - } else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || - info->severity == AER_NONFATAL) { - - /* Link is still healthy for IO reads */ - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, - &info->status); - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, - &info->mask); - if (!(info->status & ~info->mask)) - return 0; - - /* Get First Error Pointer */ - pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp); - info->first_error = PCI_ERR_CAP_FEP(temp); - - if (info->status & AER_LOG_TLP_MASKS) { - info->tlp_header_valid = 1; - pci_read_config_dword(dev, - pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); - pci_read_config_dword(dev, - pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); - pci_read_config_dword(dev, - pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); - pci_read_config_dword(dev, - pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); - } - } - - return 1; -} - -static inline void aer_process_err_devices(struct aer_err_info *e_info) -{ - int i; - - /* Report all before handle them, not to lost records by reset etc. */ - for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { - if (get_device_error_info(e_info->dev[i], e_info)) - aer_print_error(e_info->dev[i], e_info); - } - for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { - if (get_device_error_info(e_info->dev[i], e_info)) - handle_error_source(e_info->dev[i], e_info); - } -} - -/** - * aer_isr_one_error - consume an error detected by root port - * @rpc: pointer to the root port which holds an error - * @e_src: pointer to an error source - */ -static void aer_isr_one_error(struct aer_rpc *rpc, - struct aer_err_source *e_src) -{ - struct pci_dev *pdev = rpc->rpd; - struct aer_err_info *e_info = &rpc->e_info; - - /* - * There is a possibility that both correctable error and - * uncorrectable error being logged. Report correctable error first. - */ - if (e_src->status & PCI_ERR_ROOT_COR_RCV) { - e_info->id = ERR_COR_ID(e_src->id); - e_info->severity = AER_CORRECTABLE; - - if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV) - e_info->multi_error_valid = 1; - else - e_info->multi_error_valid = 0; - aer_print_port_info(pdev, e_info); - - if (find_source_device(pdev, e_info)) - aer_process_err_devices(e_info); - } - - if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) { - e_info->id = ERR_UNCOR_ID(e_src->id); - - if (e_src->status & PCI_ERR_ROOT_FATAL_RCV) - e_info->severity = AER_FATAL; - else - e_info->severity = AER_NONFATAL; - - if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV) - e_info->multi_error_valid = 1; - else - e_info->multi_error_valid = 0; - - aer_print_port_info(pdev, e_info); - - if (find_source_device(pdev, e_info)) - aer_process_err_devices(e_info); - } -} - -/** - * get_e_source - retrieve an error source - * @rpc: pointer to the root port which holds an error - * @e_src: pointer to store retrieved error source - * - * Return 1 if an error source is retrieved, otherwise 0. - * - * Invoked by DPC handler to consume an error. - */ -static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src) -{ - unsigned long flags; - - /* Lock access to Root error producer/consumer index */ - spin_lock_irqsave(&rpc->e_lock, flags); - if (rpc->prod_idx == rpc->cons_idx) { - spin_unlock_irqrestore(&rpc->e_lock, flags); - return 0; - } - - *e_src = rpc->e_sources[rpc->cons_idx]; - rpc->cons_idx++; - if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) - rpc->cons_idx = 0; - spin_unlock_irqrestore(&rpc->e_lock, flags); - - return 1; -} - -/** - * aer_isr - consume errors detected by root port - * @work: definition of this work item - * - * Invoked, as DPC, when root port records new detected error - */ -void aer_isr(struct work_struct *work) -{ - struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); - struct aer_err_source uninitialized_var(e_src); - - mutex_lock(&rpc->rpc_mutex); - while (get_e_source(rpc, &e_src)) - aer_isr_one_error(rpc, &e_src); - mutex_unlock(&rpc->rpc_mutex); -} -- cgit v1.2.3 From 0319c9a0ef4a07d5cc0f8f42d71fb33e5cfc612d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:39:38 -0500 Subject: PCI/AER: Squash aerdrv_errprint.c into aerdrv.c Squash aerdrv_errprint.c into aerdrv.c. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/Makefile | 2 +- drivers/pci/pcie/aer/aerdrv.c | 243 ++++++++++++++++++++++++++++++ drivers/pci/pcie/aer/aerdrv.h | 2 - drivers/pci/pcie/aer/aerdrv_errprint.c | 260 --------------------------------- 4 files changed, 244 insertions(+), 263 deletions(-) delete mode 100644 drivers/pci/pcie/aer/aerdrv_errprint.c (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile index c85bd0851903..662fb0d2abfe 100644 --- a/drivers/pci/pcie/aer/Makefile +++ b/drivers/pci/pcie/aer/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_PCIEAER) += aerdriver.o obj-$(CONFIG_PCIE_ECRC) += ecrc.o -aerdriver-objs := aerdrv_errprint.o aerdrv.o +aerdriver-objs := aerdrv.o aerdriver-$(CONFIG_ACPI) += aerdrv_acpi.o obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index c185069ef880..3dca26681eec 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -9,6 +9,7 @@ * Zhang Yanmin (yanmin.zhang@intel.com) */ +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include "aerdrv.h" #include "../../pci.h" @@ -112,6 +114,247 @@ int pci_aer_init(struct pci_dev *dev) return pci_cleanup_aer_error_status_regs(dev); } +#define AER_AGENT_RECEIVER 0 +#define AER_AGENT_REQUESTER 1 +#define AER_AGENT_COMPLETER 2 +#define AER_AGENT_TRANSMITTER 3 + +#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ + 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) +#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ + 0 : PCI_ERR_UNC_COMP_ABORT) +#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) + +#define AER_GET_AGENT(t, e) \ + ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ + (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ + (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ + AER_AGENT_RECEIVER) + +#define AER_PHYSICAL_LAYER_ERROR 0 +#define AER_DATA_LINK_LAYER_ERROR 1 +#define AER_TRANSACTION_LAYER_ERROR 2 + +#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + PCI_ERR_COR_RCVR : 0) +#define AER_DATA_LINK_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_BAD_TLP| \ + PCI_ERR_COR_BAD_DLLP| \ + PCI_ERR_COR_REP_ROLL| \ + PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) + +#define AER_GET_LAYER_ERROR(t, e) \ + ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ + (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ + AER_TRANSACTION_LAYER_ERROR) + +/* + * AER error strings + */ +static const char *aer_error_severity_string[] = { + "Uncorrected (Non-Fatal)", + "Uncorrected (Fatal)", + "Corrected" +}; + +static const char *aer_error_layer[] = { + "Physical Layer", + "Data Link Layer", + "Transaction Layer" +}; + +static const char *aer_correctable_error_string[] = { + "Receiver Error", /* Bit Position 0 */ + NULL, + NULL, + NULL, + NULL, + NULL, + "Bad TLP", /* Bit Position 6 */ + "Bad DLLP", /* Bit Position 7 */ + "RELAY_NUM Rollover", /* Bit Position 8 */ + NULL, + NULL, + NULL, + "Replay Timer Timeout", /* Bit Position 12 */ + "Advisory Non-Fatal", /* Bit Position 13 */ + "Corrected Internal Error", /* Bit Position 14 */ + "Header Log Overflow", /* Bit Position 15 */ +}; + +static const char *aer_uncorrectable_error_string[] = { + "Undefined", /* Bit Position 0 */ + NULL, + NULL, + NULL, + "Data Link Protocol", /* Bit Position 4 */ + "Surprise Down Error", /* Bit Position 5 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Poisoned TLP", /* Bit Position 12 */ + "Flow Control Protocol", /* Bit Position 13 */ + "Completion Timeout", /* Bit Position 14 */ + "Completer Abort", /* Bit Position 15 */ + "Unexpected Completion", /* Bit Position 16 */ + "Receiver Overflow", /* Bit Position 17 */ + "Malformed TLP", /* Bit Position 18 */ + "ECRC", /* Bit Position 19 */ + "Unsupported Request", /* Bit Position 20 */ + "ACS Violation", /* Bit Position 21 */ + "Uncorrectable Internal Error", /* Bit Position 22 */ + "MC Blocked TLP", /* Bit Position 23 */ + "AtomicOp Egress Blocked", /* Bit Position 24 */ + "TLP Prefix Blocked Error", /* Bit Position 25 */ +}; + +static const char *aer_agent_string[] = { + "Receiver ID", + "Requester ID", + "Completer ID", + "Transmitter ID" +}; + +static void __print_tlp_header(struct pci_dev *dev, + struct aer_header_log_regs *t) +{ + pci_err(dev, " TLP Header: %08x %08x %08x %08x\n", + t->dw0, t->dw1, t->dw2, t->dw3); +} + +static void __aer_print_error(struct pci_dev *dev, + struct aer_err_info *info) +{ + int i, status; + const char *errmsg = NULL; + status = (info->status & ~info->mask); + + for (i = 0; i < 32; i++) { + if (!(status & (1 << i))) + continue; + + if (info->severity == AER_CORRECTABLE) + errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ? + aer_correctable_error_string[i] : NULL; + else + errmsg = i < ARRAY_SIZE(aer_uncorrectable_error_string) ? + aer_uncorrectable_error_string[i] : NULL; + + if (errmsg) + pci_err(dev, " [%2d] %-22s%s\n", i, errmsg, + info->first_error == i ? " (First)" : ""); + else + pci_err(dev, " [%2d] Unknown Error Bit%s\n", + i, info->first_error == i ? " (First)" : ""); + } +} + +static void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) +{ + int layer, agent; + int id = ((dev->bus->number << 8) | dev->devfn); + + if (!info->status) { + pci_err(dev, "PCIe Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n", + aer_error_severity_string[info->severity]); + goto out; + } + + layer = AER_GET_LAYER_ERROR(info->severity, info->status); + agent = AER_GET_AGENT(info->severity, info->status); + + pci_err(dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n", + aer_error_severity_string[info->severity], + aer_error_layer[layer], aer_agent_string[agent]); + + pci_err(dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", + dev->vendor, dev->device, + info->status, info->mask); + + __aer_print_error(dev, info); + + if (info->tlp_header_valid) + __print_tlp_header(dev, &info->tlp); + +out: + if (info->id && info->error_dev_num > 1 && info->id == id) + pci_err(dev, " Error of this Agent is reported first\n"); + + trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask), + info->severity, info->tlp_header_valid, &info->tlp); +} + +static void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) +{ + u8 bus = info->id >> 8; + u8 devfn = info->id & 0xff; + + pci_info(dev, "AER: %s%s error received: %04x:%02x:%02x.%d\n", + info->multi_error_valid ? "Multiple " : "", + aer_error_severity_string[info->severity], + pci_domain_nr(dev->bus), bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); +} + +#ifdef CONFIG_ACPI_APEI_PCIEAER +int cper_severity_to_aer(int cper_severity) +{ + switch (cper_severity) { + case CPER_SEV_RECOVERABLE: + return AER_NONFATAL; + case CPER_SEV_FATAL: + return AER_FATAL; + default: + return AER_CORRECTABLE; + } +} +EXPORT_SYMBOL_GPL(cper_severity_to_aer); + +void cper_print_aer(struct pci_dev *dev, int aer_severity, + struct aer_capability_regs *aer) +{ + int layer, agent, tlp_header_valid = 0; + u32 status, mask; + struct aer_err_info info; + + if (aer_severity == AER_CORRECTABLE) { + status = aer->cor_status; + mask = aer->cor_mask; + } else { + status = aer->uncor_status; + mask = aer->uncor_mask; + tlp_header_valid = status & AER_LOG_TLP_MASKS; + } + + layer = AER_GET_LAYER_ERROR(aer_severity, status); + agent = AER_GET_AGENT(aer_severity, status); + + memset(&info, 0, sizeof(info)); + info.severity = aer_severity; + info.status = status; + info.mask = mask; + info.first_error = PCI_ERR_CAP_FEP(aer->cap_control); + + pci_err(dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask); + __aer_print_error(dev, &info); + pci_err(dev, "aer_layer=%s, aer_agent=%s\n", + aer_error_layer[layer], aer_agent_string[agent]); + + if (aer_severity != AER_CORRECTABLE) + pci_err(dev, "aer_uncor_severity: 0x%08x\n", + aer->uncor_severity); + + if (tlp_header_valid) + __print_tlp_header(dev, &aer->header_log); + + trace_aer_event(dev_name(&dev->dev), (status & ~mask), + aer_severity, tlp_header_valid, &aer->header_log); +} +#endif + /** * add_error_device - list device to be handled * @e_info: pointer to error info diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 059b89441acb..7fa902b1f7c1 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -77,8 +77,6 @@ struct aer_rpc { }; extern struct bus_type pcie_port_bus_type; -void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); -void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info); irqreturn_t aer_irq(int irq, void *context); #ifdef CONFIG_ACPI_APEI diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c deleted file mode 100644 index 4985bdf64c2e..000000000000 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Format error messages and print them to console. - * - * Copyright (C) 2006 Intel Corp. - * Tom Long Nguyen (tom.l.nguyen@intel.com) - * Zhang Yanmin (yanmin.zhang@intel.com) - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "aerdrv.h" -#include - -#define AER_AGENT_RECEIVER 0 -#define AER_AGENT_REQUESTER 1 -#define AER_AGENT_COMPLETER 2 -#define AER_AGENT_TRANSMITTER 3 - -#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) -#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ - 0 : PCI_ERR_UNC_COMP_ABORT) -#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) - -#define AER_GET_AGENT(t, e) \ - ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ - (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ - (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ - AER_AGENT_RECEIVER) - -#define AER_PHYSICAL_LAYER_ERROR 0 -#define AER_DATA_LINK_LAYER_ERROR 1 -#define AER_TRANSACTION_LAYER_ERROR 2 - -#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ - PCI_ERR_COR_RCVR : 0) -#define AER_DATA_LINK_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ - (PCI_ERR_COR_BAD_TLP| \ - PCI_ERR_COR_BAD_DLLP| \ - PCI_ERR_COR_REP_ROLL| \ - PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) - -#define AER_GET_LAYER_ERROR(t, e) \ - ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ - (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ - AER_TRANSACTION_LAYER_ERROR) - -/* - * AER error strings - */ -static const char *aer_error_severity_string[] = { - "Uncorrected (Non-Fatal)", - "Uncorrected (Fatal)", - "Corrected" -}; - -static const char *aer_error_layer[] = { - "Physical Layer", - "Data Link Layer", - "Transaction Layer" -}; - -static const char *aer_correctable_error_string[] = { - "Receiver Error", /* Bit Position 0 */ - NULL, - NULL, - NULL, - NULL, - NULL, - "Bad TLP", /* Bit Position 6 */ - "Bad DLLP", /* Bit Position 7 */ - "RELAY_NUM Rollover", /* Bit Position 8 */ - NULL, - NULL, - NULL, - "Replay Timer Timeout", /* Bit Position 12 */ - "Advisory Non-Fatal", /* Bit Position 13 */ - "Corrected Internal Error", /* Bit Position 14 */ - "Header Log Overflow", /* Bit Position 15 */ -}; - -static const char *aer_uncorrectable_error_string[] = { - "Undefined", /* Bit Position 0 */ - NULL, - NULL, - NULL, - "Data Link Protocol", /* Bit Position 4 */ - "Surprise Down Error", /* Bit Position 5 */ - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - "Poisoned TLP", /* Bit Position 12 */ - "Flow Control Protocol", /* Bit Position 13 */ - "Completion Timeout", /* Bit Position 14 */ - "Completer Abort", /* Bit Position 15 */ - "Unexpected Completion", /* Bit Position 16 */ - "Receiver Overflow", /* Bit Position 17 */ - "Malformed TLP", /* Bit Position 18 */ - "ECRC", /* Bit Position 19 */ - "Unsupported Request", /* Bit Position 20 */ - "ACS Violation", /* Bit Position 21 */ - "Uncorrectable Internal Error", /* Bit Position 22 */ - "MC Blocked TLP", /* Bit Position 23 */ - "AtomicOp Egress Blocked", /* Bit Position 24 */ - "TLP Prefix Blocked Error", /* Bit Position 25 */ -}; - -static const char *aer_agent_string[] = { - "Receiver ID", - "Requester ID", - "Completer ID", - "Transmitter ID" -}; - -static void __print_tlp_header(struct pci_dev *dev, - struct aer_header_log_regs *t) -{ - pci_err(dev, " TLP Header: %08x %08x %08x %08x\n", - t->dw0, t->dw1, t->dw2, t->dw3); -} - -static void __aer_print_error(struct pci_dev *dev, - struct aer_err_info *info) -{ - int i, status; - const char *errmsg = NULL; - status = (info->status & ~info->mask); - - for (i = 0; i < 32; i++) { - if (!(status & (1 << i))) - continue; - - if (info->severity == AER_CORRECTABLE) - errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ? - aer_correctable_error_string[i] : NULL; - else - errmsg = i < ARRAY_SIZE(aer_uncorrectable_error_string) ? - aer_uncorrectable_error_string[i] : NULL; - - if (errmsg) - pci_err(dev, " [%2d] %-22s%s\n", i, errmsg, - info->first_error == i ? " (First)" : ""); - else - pci_err(dev, " [%2d] Unknown Error Bit%s\n", - i, info->first_error == i ? " (First)" : ""); - } -} - -void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) -{ - int layer, agent; - int id = ((dev->bus->number << 8) | dev->devfn); - - if (!info->status) { - pci_err(dev, "PCIe Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n", - aer_error_severity_string[info->severity]); - goto out; - } - - layer = AER_GET_LAYER_ERROR(info->severity, info->status); - agent = AER_GET_AGENT(info->severity, info->status); - - pci_err(dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n", - aer_error_severity_string[info->severity], - aer_error_layer[layer], aer_agent_string[agent]); - - pci_err(dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", - dev->vendor, dev->device, - info->status, info->mask); - - __aer_print_error(dev, info); - - if (info->tlp_header_valid) - __print_tlp_header(dev, &info->tlp); - -out: - if (info->id && info->error_dev_num > 1 && info->id == id) - pci_err(dev, " Error of this Agent is reported first\n"); - - trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask), - info->severity, info->tlp_header_valid, &info->tlp); -} - -void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) -{ - u8 bus = info->id >> 8; - u8 devfn = info->id & 0xff; - - pci_info(dev, "AER: %s%s error received: %04x:%02x:%02x.%d\n", - info->multi_error_valid ? "Multiple " : "", - aer_error_severity_string[info->severity], - pci_domain_nr(dev->bus), bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); -} - -#ifdef CONFIG_ACPI_APEI_PCIEAER -int cper_severity_to_aer(int cper_severity) -{ - switch (cper_severity) { - case CPER_SEV_RECOVERABLE: - return AER_NONFATAL; - case CPER_SEV_FATAL: - return AER_FATAL; - default: - return AER_CORRECTABLE; - } -} -EXPORT_SYMBOL_GPL(cper_severity_to_aer); - -void cper_print_aer(struct pci_dev *dev, int aer_severity, - struct aer_capability_regs *aer) -{ - int layer, agent, tlp_header_valid = 0; - u32 status, mask; - struct aer_err_info info; - - if (aer_severity == AER_CORRECTABLE) { - status = aer->cor_status; - mask = aer->cor_mask; - } else { - status = aer->uncor_status; - mask = aer->uncor_mask; - tlp_header_valid = status & AER_LOG_TLP_MASKS; - } - - layer = AER_GET_LAYER_ERROR(aer_severity, status); - agent = AER_GET_AGENT(aer_severity, status); - - memset(&info, 0, sizeof(info)); - info.severity = aer_severity; - info.status = status; - info.mask = mask; - info.first_error = PCI_ERR_CAP_FEP(aer->cap_control); - - pci_err(dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask); - __aer_print_error(dev, &info); - pci_err(dev, "aer_layer=%s, aer_agent=%s\n", - aer_error_layer[layer], aer_agent_string[agent]); - - if (aer_severity != AER_CORRECTABLE) - pci_err(dev, "aer_uncor_severity: 0x%08x\n", - aer->uncor_severity); - - if (tlp_header_valid) - __print_tlp_header(dev, &aer->header_log); - - trace_aer_event(dev_name(&dev->dev), (status & ~mask), - aer_severity, tlp_header_valid, &aer->header_log); -} -#endif -- cgit v1.2.3 From 256a459370930bed087f92bb763517f8b09407eb Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:39:45 -0500 Subject: PCI/AER: Squash aerdrv_acpi.c into aerdrv.c Squash aerdrv_acpi.c into aerdrv.c. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/Makefile | 1 - drivers/pci/pcie/aer/aerdrv.c | 122 ++++++++++++++++++++++++++++++++ drivers/pci/pcie/aer/aerdrv_acpi.c | 141 ------------------------------------- 3 files changed, 122 insertions(+), 142 deletions(-) delete mode 100644 drivers/pci/pcie/aer/aerdrv_acpi.c (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile index 662fb0d2abfe..80e77c686fb8 100644 --- a/drivers/pci/pcie/aer/Makefile +++ b/drivers/pci/pcie/aer/Makefile @@ -8,6 +8,5 @@ obj-$(CONFIG_PCIEAER) += aerdriver.o obj-$(CONFIG_PCIE_ECRC) += ecrc.o aerdriver-objs := aerdrv.o -aerdriver-$(CONFIG_ACPI) += aerdrv_acpi.o obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 3dca26681eec..f77f5df83676 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "aerdrv.h" @@ -38,6 +39,127 @@ bool pci_aer_available(void) return !pcie_aer_disable && pci_msi_enabled(); } +#ifdef CONFIG_ACPI_APEI +static inline int hest_match_pci(struct acpi_hest_aer_common *p, + struct pci_dev *pci) +{ + return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) && + ACPI_HEST_BUS(p->bus) == pci->bus->number && + p->device == PCI_SLOT(pci->devfn) && + p->function == PCI_FUNC(pci->devfn); +} + +static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, + struct pci_dev *dev) +{ + u16 hest_type = hest_hdr->type; + u8 pcie_type = pci_pcie_type(dev); + + if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT && + pcie_type == PCI_EXP_TYPE_ROOT_PORT) || + (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT && + pcie_type == PCI_EXP_TYPE_ENDPOINT) || + (hest_type == ACPI_HEST_TYPE_AER_BRIDGE && + (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)) + return true; + return false; +} + +struct aer_hest_parse_info { + struct pci_dev *pci_dev; + int firmware_first; +}; + +static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr) +{ + if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT || + hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT || + hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE) + return 1; + return 0; +} + +static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) +{ + struct aer_hest_parse_info *info = data; + struct acpi_hest_aer_common *p; + int ff; + + if (!hest_source_is_pcie_aer(hest_hdr)) + return 0; + + p = (struct acpi_hest_aer_common *)(hest_hdr + 1); + ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); + + /* + * If no specific device is supplied, determine whether + * FIRMWARE_FIRST is set for *any* PCIe device. + */ + if (!info->pci_dev) { + info->firmware_first |= ff; + return 0; + } + + /* Otherwise, check the specific device */ + if (p->flags & ACPI_HEST_GLOBAL) { + if (hest_match_type(hest_hdr, info->pci_dev)) + info->firmware_first = ff; + } else + if (hest_match_pci(p, info->pci_dev)) + info->firmware_first = ff; + + return 0; +} + +static void aer_set_firmware_first(struct pci_dev *pci_dev) +{ + int rc; + struct aer_hest_parse_info info = { + .pci_dev = pci_dev, + .firmware_first = 0, + }; + + rc = apei_hest_parse(aer_hest_parse, &info); + + if (rc) + pci_dev->__aer_firmware_first = 0; + else + pci_dev->__aer_firmware_first = info.firmware_first; + pci_dev->__aer_firmware_first_valid = 1; +} + +int pcie_aer_get_firmware_first(struct pci_dev *dev) +{ + if (!pci_is_pcie(dev)) + return 0; + + if (!dev->__aer_firmware_first_valid) + aer_set_firmware_first(dev); + return dev->__aer_firmware_first; +} + +static bool aer_firmware_first; + +/** + * aer_acpi_firmware_first - Check if APEI should control AER. + */ +bool aer_acpi_firmware_first(void) +{ + static bool parsed = false; + struct aer_hest_parse_info info = { + .pci_dev = NULL, /* Check all PCIe devices */ + .firmware_first = 0, + }; + + if (!parsed) { + apei_hest_parse(aer_hest_parse, &info); + aer_firmware_first = info.firmware_first; + parsed = true; + } + return aer_firmware_first; +} +#endif + #define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c deleted file mode 100644 index 08c87de13cb8..000000000000 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Access ACPI _OSC method - * - * Copyright (C) 2006 Intel Corp. - * Tom Long Nguyen (tom.l.nguyen@intel.com) - * Zhang Yanmin (yanmin.zhang@intel.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "aerdrv.h" - -#ifdef CONFIG_ACPI_APEI -static inline int hest_match_pci(struct acpi_hest_aer_common *p, - struct pci_dev *pci) -{ - return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) && - ACPI_HEST_BUS(p->bus) == pci->bus->number && - p->device == PCI_SLOT(pci->devfn) && - p->function == PCI_FUNC(pci->devfn); -} - -static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, - struct pci_dev *dev) -{ - u16 hest_type = hest_hdr->type; - u8 pcie_type = pci_pcie_type(dev); - - if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT && - pcie_type == PCI_EXP_TYPE_ROOT_PORT) || - (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT && - pcie_type == PCI_EXP_TYPE_ENDPOINT) || - (hest_type == ACPI_HEST_TYPE_AER_BRIDGE && - (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)) - return true; - return false; -} - -struct aer_hest_parse_info { - struct pci_dev *pci_dev; - int firmware_first; -}; - -static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr) -{ - if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT || - hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT || - hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE) - return 1; - return 0; -} - -static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) -{ - struct aer_hest_parse_info *info = data; - struct acpi_hest_aer_common *p; - int ff; - - if (!hest_source_is_pcie_aer(hest_hdr)) - return 0; - - p = (struct acpi_hest_aer_common *)(hest_hdr + 1); - ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); - - /* - * If no specific device is supplied, determine whether - * FIRMWARE_FIRST is set for *any* PCIe device. - */ - if (!info->pci_dev) { - info->firmware_first |= ff; - return 0; - } - - /* Otherwise, check the specific device */ - if (p->flags & ACPI_HEST_GLOBAL) { - if (hest_match_type(hest_hdr, info->pci_dev)) - info->firmware_first = ff; - } else - if (hest_match_pci(p, info->pci_dev)) - info->firmware_first = ff; - - return 0; -} - -static void aer_set_firmware_first(struct pci_dev *pci_dev) -{ - int rc; - struct aer_hest_parse_info info = { - .pci_dev = pci_dev, - .firmware_first = 0, - }; - - rc = apei_hest_parse(aer_hest_parse, &info); - - if (rc) - pci_dev->__aer_firmware_first = 0; - else - pci_dev->__aer_firmware_first = info.firmware_first; - pci_dev->__aer_firmware_first_valid = 1; -} - -int pcie_aer_get_firmware_first(struct pci_dev *dev) -{ - if (!pci_is_pcie(dev)) - return 0; - - if (!dev->__aer_firmware_first_valid) - aer_set_firmware_first(dev); - return dev->__aer_firmware_first; -} - -static bool aer_firmware_first; - -/** - * aer_acpi_firmware_first - Check if APEI should control AER. - */ -bool aer_acpi_firmware_first(void) -{ - static bool parsed = false; - struct aer_hest_parse_info info = { - .pci_dev = NULL, /* Check all PCIe devices */ - .firmware_first = 0, - }; - - if (!parsed) { - apei_hest_parse(aer_hest_parse, &info); - aer_firmware_first = info.firmware_first; - parsed = true; - } - return aer_firmware_first; -} -#endif -- cgit v1.2.3 From 41cbc9eb1a8276a53d99317b1939cd30e917f2e8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:40:00 -0500 Subject: PCI/AER: Squash ecrc.c into aerdrv.c Squash ecrc.c into aerdrv.c. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/Makefile | 2 - drivers/pci/pcie/aer/aerdrv.c | 110 +++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/aer/ecrc.c | 117 ------------------------------------------ 3 files changed, 110 insertions(+), 119 deletions(-) delete mode 100644 drivers/pci/pcie/aer/ecrc.c (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile index 80e77c686fb8..af756b6e7e33 100644 --- a/drivers/pci/pcie/aer/Makefile +++ b/drivers/pci/pcie/aer/Makefile @@ -5,8 +5,6 @@ obj-$(CONFIG_PCIEAER) += aerdriver.o -obj-$(CONFIG_PCIE_ECRC) += ecrc.o - aerdriver-objs := aerdrv.o obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index f77f5df83676..51b66307e68c 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -7,6 +7,9 @@ * Copyright (C) 2006 Intel Corp. * Tom Long Nguyen (tom.l.nguyen@intel.com) * Zhang Yanmin (yanmin.zhang@intel.com) + * + * (C) Copyright 2009 Hewlett-Packard Development Company, L.P. + * Andrew Patterson */ #include @@ -39,6 +42,111 @@ bool pci_aer_available(void) return !pcie_aer_disable && pci_msi_enabled(); } +#ifdef CONFIG_PCIE_ECRC + +#define ECRC_POLICY_DEFAULT 0 /* ECRC set by BIOS */ +#define ECRC_POLICY_OFF 1 /* ECRC off for performance */ +#define ECRC_POLICY_ON 2 /* ECRC on for data integrity */ + +static int ecrc_policy = ECRC_POLICY_DEFAULT; + +static const char *ecrc_policy_str[] = { + [ECRC_POLICY_DEFAULT] = "bios", + [ECRC_POLICY_OFF] = "off", + [ECRC_POLICY_ON] = "on" +}; + +/** + * enable_ercr_checking - enable PCIe ECRC checking for a device + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +static int enable_ecrc_checking(struct pci_dev *dev) +{ + int pos; + u32 reg32; + + if (!pci_is_pcie(dev)) + return -ENODEV; + + pos = dev->aer_cap; + if (!pos) + return -ENODEV; + + pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); + if (reg32 & PCI_ERR_CAP_ECRC_GENC) + reg32 |= PCI_ERR_CAP_ECRC_GENE; + if (reg32 & PCI_ERR_CAP_ECRC_CHKC) + reg32 |= PCI_ERR_CAP_ECRC_CHKE; + pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); + + return 0; +} + +/** + * disable_ercr_checking - disables PCIe ECRC checking for a device + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +static int disable_ecrc_checking(struct pci_dev *dev) +{ + int pos; + u32 reg32; + + if (!pci_is_pcie(dev)) + return -ENODEV; + + pos = dev->aer_cap; + if (!pos) + return -ENODEV; + + pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); + reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); + pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); + + return 0; +} + +/** + * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy + * @dev: the PCI device + */ +void pcie_set_ecrc_checking(struct pci_dev *dev) +{ + switch (ecrc_policy) { + case ECRC_POLICY_DEFAULT: + return; + case ECRC_POLICY_OFF: + disable_ecrc_checking(dev); + break; + case ECRC_POLICY_ON: + enable_ecrc_checking(dev); + break; + default: + return; + } +} + +/** + * pcie_ecrc_get_policy - parse kernel command-line ecrc option + */ +void pcie_ecrc_get_policy(char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) + if (!strncmp(str, ecrc_policy_str[i], + strlen(ecrc_policy_str[i]))) + break; + if (i >= ARRAY_SIZE(ecrc_policy_str)) + return; + + ecrc_policy = i; +} +#endif /* CONFIG_PCIE_ECRC */ + #ifdef CONFIG_ACPI_APEI static inline int hest_match_pci(struct acpi_hest_aer_common *p, struct pci_dev *pci) @@ -137,6 +245,8 @@ int pcie_aer_get_firmware_first(struct pci_dev *dev) aer_set_firmware_first(dev); return dev->__aer_firmware_first; } +#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) static bool aer_firmware_first; diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c deleted file mode 100644 index 039efb606e31..000000000000 --- a/drivers/pci/pcie/aer/ecrc.c +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Enable/disable PCIe ECRC checking - * - * (C) Copyright 2009 Hewlett-Packard Development Company, L.P. - * Andrew Patterson - */ - -#include -#include -#include -#include -#include -#include -#include "../../pci.h" - -#define ECRC_POLICY_DEFAULT 0 /* ECRC set by BIOS */ -#define ECRC_POLICY_OFF 1 /* ECRC off for performance */ -#define ECRC_POLICY_ON 2 /* ECRC on for data integrity */ - -static int ecrc_policy = ECRC_POLICY_DEFAULT; - -static const char *ecrc_policy_str[] = { - [ECRC_POLICY_DEFAULT] = "bios", - [ECRC_POLICY_OFF] = "off", - [ECRC_POLICY_ON] = "on" -}; - -/** - * enable_ercr_checking - enable PCIe ECRC checking for a device - * @dev: the PCI device - * - * Returns 0 on success, or negative on failure. - */ -static int enable_ecrc_checking(struct pci_dev *dev) -{ - int pos; - u32 reg32; - - if (!pci_is_pcie(dev)) - return -ENODEV; - - pos = dev->aer_cap; - if (!pos) - return -ENODEV; - - pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); - if (reg32 & PCI_ERR_CAP_ECRC_GENC) - reg32 |= PCI_ERR_CAP_ECRC_GENE; - if (reg32 & PCI_ERR_CAP_ECRC_CHKC) - reg32 |= PCI_ERR_CAP_ECRC_CHKE; - pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); - - return 0; -} - -/** - * disable_ercr_checking - disables PCIe ECRC checking for a device - * @dev: the PCI device - * - * Returns 0 on success, or negative on failure. - */ -static int disable_ecrc_checking(struct pci_dev *dev) -{ - int pos; - u32 reg32; - - if (!pci_is_pcie(dev)) - return -ENODEV; - - pos = dev->aer_cap; - if (!pos) - return -ENODEV; - - pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); - reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); - pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); - - return 0; -} - -/** - * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy - * @dev: the PCI device - */ -void pcie_set_ecrc_checking(struct pci_dev *dev) -{ - switch (ecrc_policy) { - case ECRC_POLICY_DEFAULT: - return; - case ECRC_POLICY_OFF: - disable_ecrc_checking(dev); - break; - case ECRC_POLICY_ON: - enable_ecrc_checking(dev); - break; - default: - return; - } -} - -/** - * pcie_ecrc_get_policy - parse kernel command-line ecrc option - */ -void pcie_ecrc_get_policy(char *str) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) - if (!strncmp(str, ecrc_policy_str[i], - strlen(ecrc_policy_str[i]))) - break; - if (i >= ARRAY_SIZE(ecrc_policy_str)) - return; - - ecrc_policy = i; -} -- cgit v1.2.3 From 16c33b1595441fe106f8efd370fb584ed8d21bde Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:40:10 -0500 Subject: PCI/AER: Remove duplicate pcie_port_bus_type declaration pcie_port_bus_type is already declared in portdrv.h, so remove the unnecessary duplicate declaration in aerdrv.h. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/aerdrv.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 7fa902b1f7c1..f4fc71119cf2 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -76,7 +76,6 @@ struct aer_rpc { */ }; -extern struct bus_type pcie_port_bus_type; irqreturn_t aer_irq(int irq, void *context); #ifdef CONFIG_ACPI_APEI -- cgit v1.2.3 From 0544b04b792696d6e408f3a882d1a36943084fa8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:40:16 -0500 Subject: PCI/AER: Move pcie_aer_get_firmware_first() to portdrv.h Move pcie_aer_get_firmware_first() to portdrv.h, where it can be more easily shared between AER and DPC. Then DPC no longer needs to include aer/aerdrv.h. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/aerdrv.h | 10 ---------- drivers/pci/pcie/dpc.c | 1 - drivers/pci/pcie/portdrv.h | 11 +++++++++++ 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index f4fc71119cf2..b0c4aaa79d9c 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -78,14 +78,4 @@ struct aer_rpc { irqreturn_t aer_irq(int irq, void *context); -#ifdef CONFIG_ACPI_APEI -int pcie_aer_get_firmware_first(struct pci_dev *pci_dev); -#else -static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev) -{ - if (pci_dev->__aer_firmware_first_valid) - return pci_dev->__aer_firmware_first; - return 0; -} -#endif #endif /* _AERDRV_H_ */ diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index d6436681c535..921ed979109d 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -13,7 +13,6 @@ #include "portdrv.h" #include "../pci.h" -#include "aer/aerdrv.h" struct dpc_dev { struct pcie_device *dev; diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 2bb5db7b53e6..6a261dc7cd4c 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -110,6 +110,17 @@ static inline bool pcie_pme_no_msi(void) { return false; } static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {} #endif /* !CONFIG_PCIE_PME */ +#ifdef CONFIG_ACPI_APEI +int pcie_aer_get_firmware_first(struct pci_dev *pci_dev); +#else +static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev) +{ + if (pci_dev->__aer_firmware_first_valid) + return pci_dev->__aer_firmware_first; + return 0; +} +#endif + struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev, u32 service); struct device *pcie_port_find_device(struct pci_dev *dev, u32 service); -- cgit v1.2.3 From f53e7418c3402a2589216997d7cd07d3b0be497e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:40:25 -0500 Subject: PCI/AER: Move aer_irq() declaration to portdrv.h The aer_irq() declaration is the only thing needed by aer_inject.c. Move it to portdrv.h so we eventually get rid of aerdrv.h completely. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/aer_inject.c | 3 ++- drivers/pci/pcie/aer/aerdrv.h | 2 -- drivers/pci/pcie/portdrv.h | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index a49090935303..6c5fda96778a 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -21,7 +21,8 @@ #include #include #include -#include "aerdrv.h" + +#include "../portdrv.h" /* Override the existing corrected and uncorrected error masks */ static bool aer_mask_override; diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index b0c4aaa79d9c..9867950635df 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -76,6 +76,4 @@ struct aer_rpc { */ }; -irqreturn_t aer_irq(int irq, void *context); - #endif /* _AERDRV_H_ */ diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 6a261dc7cd4c..6ffc797a0dc1 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -121,6 +121,10 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev) } #endif +#ifdef CONFIG_PCIEAER +irqreturn_t aer_irq(int irq, void *context); +#endif + struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev, u32 service); struct device *pcie_port_find_device(struct pci_dev *dev, u32 service); -- cgit v1.2.3 From 23e672bc2abead19ff22d8a1d40f9d4c023524f8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:41:28 -0500 Subject: PCI/AER: Move private AER things to aerdrv.c Most of the things in aerdrv.h are only used in aerdrv.c, so move them there. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/aerdrv.c | 64 ++++++++++++++++++++++++++++++++++- drivers/pci/pcie/aer/aerdrv.h | 79 ------------------------------------------- 2 files changed, 63 insertions(+), 80 deletions(-) delete mode 100644 drivers/pci/pcie/aer/aerdrv.h (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 51b66307e68c..3fc23fc95f98 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -27,8 +27,70 @@ #include #include -#include "aerdrv.h" #include "../../pci.h" +#include "../portdrv.h" + +#define AER_ERROR_SOURCES_MAX 100 +#define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */ + +struct aer_err_info { + struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; + int error_dev_num; + + unsigned int id:16; + + unsigned int severity:2; /* 0:NONFATAL | 1:FATAL | 2:COR */ + unsigned int __pad1:5; + unsigned int multi_error_valid:1; + + unsigned int first_error:5; + unsigned int __pad2:2; + unsigned int tlp_header_valid:1; + + unsigned int status; /* COR/UNCOR Error Status */ + unsigned int mask; /* COR/UNCOR Error Mask */ + struct aer_header_log_regs tlp; /* TLP Header */ +}; + +struct aer_err_source { + unsigned int status; + unsigned int id; +}; + +struct aer_rpc { + struct pci_dev *rpd; /* Root Port device */ + struct work_struct dpc_handler; + struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX]; + struct aer_err_info e_info; + unsigned short prod_idx; /* Error Producer Index */ + unsigned short cons_idx; /* Error Consumer Index */ + int isr; + spinlock_t e_lock; /* + * Lock access to Error Status/ID Regs + * and error producer/consumer index + */ + struct mutex rpc_mutex; /* + * only one thread could do + * recovery on the same + * root port hierarchy + */ +}; + +#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ + PCI_ERR_UNC_ECRC| \ + PCI_ERR_UNC_UNSUP| \ + PCI_ERR_UNC_COMP_ABORT| \ + PCI_ERR_UNC_UNX_COMP| \ + PCI_ERR_UNC_MALF_TLP) + +#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ + PCI_EXP_RTCTL_SENFEE| \ + PCI_EXP_RTCTL_SEFEE) +#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \ + PCI_ERR_ROOT_CMD_NONFATAL_EN| \ + PCI_ERR_ROOT_CMD_FATAL_EN) +#define ERR_COR_ID(d) (d & 0xffff) +#define ERR_UNCOR_ID(d) (d >> 16) static int pcie_aer_disable; diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h deleted file mode 100644 index 9867950635df..000000000000 --- a/drivers/pci/pcie/aer/aerdrv.h +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2006 Intel Corp. - * Tom Long Nguyen (tom.l.nguyen@intel.com) - * Zhang Yanmin (yanmin.zhang@intel.com) - */ - -#ifndef _AERDRV_H_ -#define _AERDRV_H_ - -#include -#include -#include - -#include "../portdrv.h" - -#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ - PCI_EXP_RTCTL_SENFEE| \ - PCI_EXP_RTCTL_SEFEE) -#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \ - PCI_ERR_ROOT_CMD_NONFATAL_EN| \ - PCI_ERR_ROOT_CMD_FATAL_EN) -#define ERR_COR_ID(d) (d & 0xffff) -#define ERR_UNCOR_ID(d) (d >> 16) - -#define AER_ERROR_SOURCES_MAX 100 - -#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ - PCI_ERR_UNC_ECRC| \ - PCI_ERR_UNC_UNSUP| \ - PCI_ERR_UNC_COMP_ABORT| \ - PCI_ERR_UNC_UNX_COMP| \ - PCI_ERR_UNC_MALF_TLP) - -#define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */ -struct aer_err_info { - struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; - int error_dev_num; - - unsigned int id:16; - - unsigned int severity:2; /* 0:NONFATAL | 1:FATAL | 2:COR */ - unsigned int __pad1:5; - unsigned int multi_error_valid:1; - - unsigned int first_error:5; - unsigned int __pad2:2; - unsigned int tlp_header_valid:1; - - unsigned int status; /* COR/UNCOR Error Status */ - unsigned int mask; /* COR/UNCOR Error Mask */ - struct aer_header_log_regs tlp; /* TLP Header */ -}; - -struct aer_err_source { - unsigned int status; - unsigned int id; -}; - -struct aer_rpc { - struct pci_dev *rpd; /* Root Port device */ - struct work_struct dpc_handler; - struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX]; - struct aer_err_info e_info; - unsigned short prod_idx; /* Error Producer Index */ - unsigned short cons_idx; /* Error Consumer Index */ - int isr; - spinlock_t e_lock; /* - * Lock access to Error Status/ID Regs - * and error producer/consumer index - */ - struct mutex rpc_mutex; /* - * only one thread could do - * recovery on the same - * root port hierarchy - */ -}; - -#endif /* _AERDRV_H_ */ -- cgit v1.2.3 From adc1f22f5a8664b07734a23a8d9bb82a38c0d043 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:48:39 -0500 Subject: PCI/AER: Squash Kconfig.debug into Kconfig Squash Kconfig.debug into Kconfig. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/aer/Kconfig | 16 ++++++++++++++-- drivers/pci/pcie/aer/Kconfig.debug | 19 ------------------- 2 files changed, 14 insertions(+), 21 deletions(-) delete mode 100644 drivers/pci/pcie/aer/Kconfig.debug (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig index 5a64eb3d6c7a..1996df082e89 100644 --- a/drivers/pci/pcie/aer/Kconfig +++ b/drivers/pci/pcie/aer/Kconfig @@ -13,6 +13,20 @@ config PCIEAER (AER) driver support. Error reporting messages sent to Root Port will be handled by PCI Express AER driver. +config PCIEAER_INJECT + tristate "PCIe AER error injector support" + depends on PCIEAER + default n + help + This enables PCI Express Root Port Advanced Error Reporting + (AER) software error injector. + + Debugging PCIe AER code is quite difficult because it is hard + to trigger various real hardware errors. Software based + error injection can fake almost all kinds of errors with the + help of a user space helper tool aer-inject, which can be + gotten from: + http://www.kernel.org/pub/linux/utils/pci/aer-inject/ # # PCI Express ECRC @@ -25,5 +39,3 @@ config PCIE_ECRC (transaction layer end-to-end CRC checking). When in doubt, say N. - -source "drivers/pci/pcie/aer/Kconfig.debug" diff --git a/drivers/pci/pcie/aer/Kconfig.debug b/drivers/pci/pcie/aer/Kconfig.debug deleted file mode 100644 index 67e02174b65b..000000000000 --- a/drivers/pci/pcie/aer/Kconfig.debug +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# PCI Express Root Port Device AER Debug Configuration -# - -config PCIEAER_INJECT - tristate "PCIe AER error injector support" - depends on PCIEAER - default n - help - This enables PCI Express Root Port Advanced Error Reporting - (AER) software error injector. - - Debugging PCIe AER code is quite difficult because it is hard - to trigger various real hardware errors. Software based - error injection can fake almost all kinds of errors with the - help of a user space helper tool aer-inject, which can be - gotten from: - http://www.kernel.org/pub/linux/utils/pci/aer-inject/ -- cgit v1.2.3 From 4696b828ca3781deebc3f61d50978d5c8c5be405 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:48:47 -0500 Subject: PCI/AER: Hoist aerdrv.c, aer_inject.c up to drivers/pci/pcie/ Hoist aerdrv.c, aer_inject.c up to drivers/pci/pcie/ so they're next to other PCIe service drivers. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/Kconfig | 37 +- drivers/pci/pcie/Makefile | 3 +- drivers/pci/pcie/aer.c | 1377 +++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/aer/Kconfig | 41 -- drivers/pci/pcie/aer/Makefile | 10 - drivers/pci/pcie/aer/aer_inject.c | 551 --------------- drivers/pci/pcie/aer/aerdrv.c | 1377 ------------------------------------- drivers/pci/pcie/aer_inject.c | 551 +++++++++++++++ 8 files changed, 1966 insertions(+), 1981 deletions(-) create mode 100644 drivers/pci/pcie/aer.c delete mode 100644 drivers/pci/pcie/aer/Kconfig delete mode 100644 drivers/pci/pcie/aer/Makefile delete mode 100644 drivers/pci/pcie/aer/aer_inject.c delete mode 100644 drivers/pci/pcie/aer/aerdrv.c create mode 100644 drivers/pci/pcie/aer_inject.c (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index b12e28b3d8f9..4a8e26a2b012 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -23,7 +23,42 @@ config HOTPLUG_PCI_PCIE When in doubt, say N. -source "drivers/pci/pcie/aer/Kconfig" +config PCIEAER + bool "Root Port Advanced Error Reporting support" + depends on PCIEPORTBUS + select RAS + default y + help + This enables PCI Express Root Port Advanced Error Reporting + (AER) driver support. Error reporting messages sent to Root + Port will be handled by PCI Express AER driver. + +config PCIEAER_INJECT + tristate "PCIe AER error injector support" + depends on PCIEAER + default n + help + This enables PCI Express Root Port Advanced Error Reporting + (AER) software error injector. + + Debugging PCIe AER code is quite difficult because it is hard + to trigger various real hardware errors. Software based + error injection can fake almost all kinds of errors with the + help of a user space helper tool aer-inject, which can be + gotten from: + http://www.kernel.org/pub/linux/utils/pci/aer-inject/ + +# +# PCI Express ECRC +# +config PCIE_ECRC + bool "PCI Express ECRC settings control" + depends on PCIEAER + help + Used to override firmware/bios settings for PCI Express ECRC + (transaction layer end-to-end CRC checking). + + When in doubt, say N. # # PCI Express ASPM diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 03f4e0b3a140..ab514083d5d4 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -7,7 +7,8 @@ pcieportdrv-y := portdrv_core.o portdrv_pci.o err.o obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o obj-$(CONFIG_PCIEASPM) += aspm.o -obj-$(CONFIG_PCIEAER) += aer/ +obj-$(CONFIG_PCIEAER) += aer.o +obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o obj-$(CONFIG_PCIE_PME) += pme.o obj-$(CONFIG_PCIE_DPC) += dpc.o obj-$(CONFIG_PCIE_PTM) += ptm.o diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c new file mode 100644 index 000000000000..a2e88386af28 --- /dev/null +++ b/drivers/pci/pcie/aer.c @@ -0,0 +1,1377 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implement the AER root port service driver. The driver registers an IRQ + * handler. When a root port triggers an AER interrupt, the IRQ handler + * collects root port status and schedules work. + * + * Copyright (C) 2006 Intel Corp. + * Tom Long Nguyen (tom.l.nguyen@intel.com) + * Zhang Yanmin (yanmin.zhang@intel.com) + * + * (C) Copyright 2009 Hewlett-Packard Development Company, L.P. + * Andrew Patterson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" +#include "portdrv.h" + +#define AER_ERROR_SOURCES_MAX 100 +#define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */ + +struct aer_err_info { + struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; + int error_dev_num; + + unsigned int id:16; + + unsigned int severity:2; /* 0:NONFATAL | 1:FATAL | 2:COR */ + unsigned int __pad1:5; + unsigned int multi_error_valid:1; + + unsigned int first_error:5; + unsigned int __pad2:2; + unsigned int tlp_header_valid:1; + + unsigned int status; /* COR/UNCOR Error Status */ + unsigned int mask; /* COR/UNCOR Error Mask */ + struct aer_header_log_regs tlp; /* TLP Header */ +}; + +struct aer_err_source { + unsigned int status; + unsigned int id; +}; + +struct aer_rpc { + struct pci_dev *rpd; /* Root Port device */ + struct work_struct dpc_handler; + struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX]; + struct aer_err_info e_info; + unsigned short prod_idx; /* Error Producer Index */ + unsigned short cons_idx; /* Error Consumer Index */ + int isr; + spinlock_t e_lock; /* + * Lock access to Error Status/ID Regs + * and error producer/consumer index + */ + struct mutex rpc_mutex; /* + * only one thread could do + * recovery on the same + * root port hierarchy + */ +}; + +#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ + PCI_ERR_UNC_ECRC| \ + PCI_ERR_UNC_UNSUP| \ + PCI_ERR_UNC_COMP_ABORT| \ + PCI_ERR_UNC_UNX_COMP| \ + PCI_ERR_UNC_MALF_TLP) + +#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ + PCI_EXP_RTCTL_SENFEE| \ + PCI_EXP_RTCTL_SEFEE) +#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \ + PCI_ERR_ROOT_CMD_NONFATAL_EN| \ + PCI_ERR_ROOT_CMD_FATAL_EN) +#define ERR_COR_ID(d) (d & 0xffff) +#define ERR_UNCOR_ID(d) (d >> 16) + +static int pcie_aer_disable; + +void pci_no_aer(void) +{ + pcie_aer_disable = 1; +} + +bool pci_aer_available(void) +{ + return !pcie_aer_disable && pci_msi_enabled(); +} + +#ifdef CONFIG_PCIE_ECRC + +#define ECRC_POLICY_DEFAULT 0 /* ECRC set by BIOS */ +#define ECRC_POLICY_OFF 1 /* ECRC off for performance */ +#define ECRC_POLICY_ON 2 /* ECRC on for data integrity */ + +static int ecrc_policy = ECRC_POLICY_DEFAULT; + +static const char *ecrc_policy_str[] = { + [ECRC_POLICY_DEFAULT] = "bios", + [ECRC_POLICY_OFF] = "off", + [ECRC_POLICY_ON] = "on" +}; + +/** + * enable_ercr_checking - enable PCIe ECRC checking for a device + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +static int enable_ecrc_checking(struct pci_dev *dev) +{ + int pos; + u32 reg32; + + if (!pci_is_pcie(dev)) + return -ENODEV; + + pos = dev->aer_cap; + if (!pos) + return -ENODEV; + + pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); + if (reg32 & PCI_ERR_CAP_ECRC_GENC) + reg32 |= PCI_ERR_CAP_ECRC_GENE; + if (reg32 & PCI_ERR_CAP_ECRC_CHKC) + reg32 |= PCI_ERR_CAP_ECRC_CHKE; + pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); + + return 0; +} + +/** + * disable_ercr_checking - disables PCIe ECRC checking for a device + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +static int disable_ecrc_checking(struct pci_dev *dev) +{ + int pos; + u32 reg32; + + if (!pci_is_pcie(dev)) + return -ENODEV; + + pos = dev->aer_cap; + if (!pos) + return -ENODEV; + + pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); + reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); + pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); + + return 0; +} + +/** + * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy + * @dev: the PCI device + */ +void pcie_set_ecrc_checking(struct pci_dev *dev) +{ + switch (ecrc_policy) { + case ECRC_POLICY_DEFAULT: + return; + case ECRC_POLICY_OFF: + disable_ecrc_checking(dev); + break; + case ECRC_POLICY_ON: + enable_ecrc_checking(dev); + break; + default: + return; + } +} + +/** + * pcie_ecrc_get_policy - parse kernel command-line ecrc option + */ +void pcie_ecrc_get_policy(char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) + if (!strncmp(str, ecrc_policy_str[i], + strlen(ecrc_policy_str[i]))) + break; + if (i >= ARRAY_SIZE(ecrc_policy_str)) + return; + + ecrc_policy = i; +} +#endif /* CONFIG_PCIE_ECRC */ + +#ifdef CONFIG_ACPI_APEI +static inline int hest_match_pci(struct acpi_hest_aer_common *p, + struct pci_dev *pci) +{ + return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) && + ACPI_HEST_BUS(p->bus) == pci->bus->number && + p->device == PCI_SLOT(pci->devfn) && + p->function == PCI_FUNC(pci->devfn); +} + +static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, + struct pci_dev *dev) +{ + u16 hest_type = hest_hdr->type; + u8 pcie_type = pci_pcie_type(dev); + + if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT && + pcie_type == PCI_EXP_TYPE_ROOT_PORT) || + (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT && + pcie_type == PCI_EXP_TYPE_ENDPOINT) || + (hest_type == ACPI_HEST_TYPE_AER_BRIDGE && + (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)) + return true; + return false; +} + +struct aer_hest_parse_info { + struct pci_dev *pci_dev; + int firmware_first; +}; + +static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr) +{ + if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT || + hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT || + hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE) + return 1; + return 0; +} + +static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) +{ + struct aer_hest_parse_info *info = data; + struct acpi_hest_aer_common *p; + int ff; + + if (!hest_source_is_pcie_aer(hest_hdr)) + return 0; + + p = (struct acpi_hest_aer_common *)(hest_hdr + 1); + ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); + + /* + * If no specific device is supplied, determine whether + * FIRMWARE_FIRST is set for *any* PCIe device. + */ + if (!info->pci_dev) { + info->firmware_first |= ff; + return 0; + } + + /* Otherwise, check the specific device */ + if (p->flags & ACPI_HEST_GLOBAL) { + if (hest_match_type(hest_hdr, info->pci_dev)) + info->firmware_first = ff; + } else + if (hest_match_pci(p, info->pci_dev)) + info->firmware_first = ff; + + return 0; +} + +static void aer_set_firmware_first(struct pci_dev *pci_dev) +{ + int rc; + struct aer_hest_parse_info info = { + .pci_dev = pci_dev, + .firmware_first = 0, + }; + + rc = apei_hest_parse(aer_hest_parse, &info); + + if (rc) + pci_dev->__aer_firmware_first = 0; + else + pci_dev->__aer_firmware_first = info.firmware_first; + pci_dev->__aer_firmware_first_valid = 1; +} + +int pcie_aer_get_firmware_first(struct pci_dev *dev) +{ + if (!pci_is_pcie(dev)) + return 0; + + if (!dev->__aer_firmware_first_valid) + aer_set_firmware_first(dev); + return dev->__aer_firmware_first; +} +#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) + +static bool aer_firmware_first; + +/** + * aer_acpi_firmware_first - Check if APEI should control AER. + */ +bool aer_acpi_firmware_first(void) +{ + static bool parsed = false; + struct aer_hest_parse_info info = { + .pci_dev = NULL, /* Check all PCIe devices */ + .firmware_first = 0, + }; + + if (!parsed) { + apei_hest_parse(aer_hest_parse, &info); + aer_firmware_first = info.firmware_first; + parsed = true; + } + return aer_firmware_first; +} +#endif + +#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) + +int pci_enable_pcie_error_reporting(struct pci_dev *dev) +{ + if (pcie_aer_get_firmware_first(dev)) + return -EIO; + + if (!dev->aer_cap) + return -EIO; + + return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); +} +EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); + +int pci_disable_pcie_error_reporting(struct pci_dev *dev) +{ + if (pcie_aer_get_firmware_first(dev)) + return -EIO; + + return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL, + PCI_EXP_AER_FLAGS); +} +EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); + +int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) +{ + int pos; + u32 status; + + pos = dev->aer_cap; + if (!pos) + return -EIO; + + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + if (status) + pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); + + return 0; +} +EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); + +int pci_cleanup_aer_error_status_regs(struct pci_dev *dev) +{ + int pos; + u32 status; + int port_type; + + if (!pci_is_pcie(dev)) + return -ENODEV; + + pos = dev->aer_cap; + if (!pos) + return -EIO; + + port_type = pci_pcie_type(dev); + if (port_type == PCI_EXP_TYPE_ROOT_PORT) { + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); + pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); + } + + pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); + pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status); + + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); + + return 0; +} + +int pci_aer_init(struct pci_dev *dev) +{ + dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + return pci_cleanup_aer_error_status_regs(dev); +} + +#define AER_AGENT_RECEIVER 0 +#define AER_AGENT_REQUESTER 1 +#define AER_AGENT_COMPLETER 2 +#define AER_AGENT_TRANSMITTER 3 + +#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ + 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) +#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ + 0 : PCI_ERR_UNC_COMP_ABORT) +#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) + +#define AER_GET_AGENT(t, e) \ + ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ + (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ + (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ + AER_AGENT_RECEIVER) + +#define AER_PHYSICAL_LAYER_ERROR 0 +#define AER_DATA_LINK_LAYER_ERROR 1 +#define AER_TRANSACTION_LAYER_ERROR 2 + +#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + PCI_ERR_COR_RCVR : 0) +#define AER_DATA_LINK_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_BAD_TLP| \ + PCI_ERR_COR_BAD_DLLP| \ + PCI_ERR_COR_REP_ROLL| \ + PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) + +#define AER_GET_LAYER_ERROR(t, e) \ + ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ + (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ + AER_TRANSACTION_LAYER_ERROR) + +/* + * AER error strings + */ +static const char *aer_error_severity_string[] = { + "Uncorrected (Non-Fatal)", + "Uncorrected (Fatal)", + "Corrected" +}; + +static const char *aer_error_layer[] = { + "Physical Layer", + "Data Link Layer", + "Transaction Layer" +}; + +static const char *aer_correctable_error_string[] = { + "Receiver Error", /* Bit Position 0 */ + NULL, + NULL, + NULL, + NULL, + NULL, + "Bad TLP", /* Bit Position 6 */ + "Bad DLLP", /* Bit Position 7 */ + "RELAY_NUM Rollover", /* Bit Position 8 */ + NULL, + NULL, + NULL, + "Replay Timer Timeout", /* Bit Position 12 */ + "Advisory Non-Fatal", /* Bit Position 13 */ + "Corrected Internal Error", /* Bit Position 14 */ + "Header Log Overflow", /* Bit Position 15 */ +}; + +static const char *aer_uncorrectable_error_string[] = { + "Undefined", /* Bit Position 0 */ + NULL, + NULL, + NULL, + "Data Link Protocol", /* Bit Position 4 */ + "Surprise Down Error", /* Bit Position 5 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Poisoned TLP", /* Bit Position 12 */ + "Flow Control Protocol", /* Bit Position 13 */ + "Completion Timeout", /* Bit Position 14 */ + "Completer Abort", /* Bit Position 15 */ + "Unexpected Completion", /* Bit Position 16 */ + "Receiver Overflow", /* Bit Position 17 */ + "Malformed TLP", /* Bit Position 18 */ + "ECRC", /* Bit Position 19 */ + "Unsupported Request", /* Bit Position 20 */ + "ACS Violation", /* Bit Position 21 */ + "Uncorrectable Internal Error", /* Bit Position 22 */ + "MC Blocked TLP", /* Bit Position 23 */ + "AtomicOp Egress Blocked", /* Bit Position 24 */ + "TLP Prefix Blocked Error", /* Bit Position 25 */ +}; + +static const char *aer_agent_string[] = { + "Receiver ID", + "Requester ID", + "Completer ID", + "Transmitter ID" +}; + +static void __print_tlp_header(struct pci_dev *dev, + struct aer_header_log_regs *t) +{ + pci_err(dev, " TLP Header: %08x %08x %08x %08x\n", + t->dw0, t->dw1, t->dw2, t->dw3); +} + +static void __aer_print_error(struct pci_dev *dev, + struct aer_err_info *info) +{ + int i, status; + const char *errmsg = NULL; + status = (info->status & ~info->mask); + + for (i = 0; i < 32; i++) { + if (!(status & (1 << i))) + continue; + + if (info->severity == AER_CORRECTABLE) + errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ? + aer_correctable_error_string[i] : NULL; + else + errmsg = i < ARRAY_SIZE(aer_uncorrectable_error_string) ? + aer_uncorrectable_error_string[i] : NULL; + + if (errmsg) + pci_err(dev, " [%2d] %-22s%s\n", i, errmsg, + info->first_error == i ? " (First)" : ""); + else + pci_err(dev, " [%2d] Unknown Error Bit%s\n", + i, info->first_error == i ? " (First)" : ""); + } +} + +static void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) +{ + int layer, agent; + int id = ((dev->bus->number << 8) | dev->devfn); + + if (!info->status) { + pci_err(dev, "PCIe Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n", + aer_error_severity_string[info->severity]); + goto out; + } + + layer = AER_GET_LAYER_ERROR(info->severity, info->status); + agent = AER_GET_AGENT(info->severity, info->status); + + pci_err(dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n", + aer_error_severity_string[info->severity], + aer_error_layer[layer], aer_agent_string[agent]); + + pci_err(dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", + dev->vendor, dev->device, + info->status, info->mask); + + __aer_print_error(dev, info); + + if (info->tlp_header_valid) + __print_tlp_header(dev, &info->tlp); + +out: + if (info->id && info->error_dev_num > 1 && info->id == id) + pci_err(dev, " Error of this Agent is reported first\n"); + + trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask), + info->severity, info->tlp_header_valid, &info->tlp); +} + +static void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) +{ + u8 bus = info->id >> 8; + u8 devfn = info->id & 0xff; + + pci_info(dev, "AER: %s%s error received: %04x:%02x:%02x.%d\n", + info->multi_error_valid ? "Multiple " : "", + aer_error_severity_string[info->severity], + pci_domain_nr(dev->bus), bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); +} + +#ifdef CONFIG_ACPI_APEI_PCIEAER +int cper_severity_to_aer(int cper_severity) +{ + switch (cper_severity) { + case CPER_SEV_RECOVERABLE: + return AER_NONFATAL; + case CPER_SEV_FATAL: + return AER_FATAL; + default: + return AER_CORRECTABLE; + } +} +EXPORT_SYMBOL_GPL(cper_severity_to_aer); + +void cper_print_aer(struct pci_dev *dev, int aer_severity, + struct aer_capability_regs *aer) +{ + int layer, agent, tlp_header_valid = 0; + u32 status, mask; + struct aer_err_info info; + + if (aer_severity == AER_CORRECTABLE) { + status = aer->cor_status; + mask = aer->cor_mask; + } else { + status = aer->uncor_status; + mask = aer->uncor_mask; + tlp_header_valid = status & AER_LOG_TLP_MASKS; + } + + layer = AER_GET_LAYER_ERROR(aer_severity, status); + agent = AER_GET_AGENT(aer_severity, status); + + memset(&info, 0, sizeof(info)); + info.severity = aer_severity; + info.status = status; + info.mask = mask; + info.first_error = PCI_ERR_CAP_FEP(aer->cap_control); + + pci_err(dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask); + __aer_print_error(dev, &info); + pci_err(dev, "aer_layer=%s, aer_agent=%s\n", + aer_error_layer[layer], aer_agent_string[agent]); + + if (aer_severity != AER_CORRECTABLE) + pci_err(dev, "aer_uncor_severity: 0x%08x\n", + aer->uncor_severity); + + if (tlp_header_valid) + __print_tlp_header(dev, &aer->header_log); + + trace_aer_event(dev_name(&dev->dev), (status & ~mask), + aer_severity, tlp_header_valid, &aer->header_log); +} +#endif + +/** + * add_error_device - list device to be handled + * @e_info: pointer to error info + * @dev: pointer to pci_dev to be added + */ +static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) +{ + if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { + e_info->dev[e_info->error_dev_num] = dev; + e_info->error_dev_num++; + return 0; + } + return -ENOSPC; +} + +/** + * is_error_source - check whether the device is source of reported error + * @dev: pointer to pci_dev to be checked + * @e_info: pointer to reported error info + */ +static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) +{ + int pos; + u32 status, mask; + u16 reg16; + + /* + * When bus id is equal to 0, it might be a bad id + * reported by root port. + */ + if ((PCI_BUS_NUM(e_info->id) != 0) && + !(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) { + /* Device ID match? */ + if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) + return true; + + /* Continue id comparing if there is no multiple error */ + if (!e_info->multi_error_valid) + return false; + } + + /* + * When either + * 1) bus id is equal to 0. Some ports might lose the bus + * id of error source id; + * 2) bus flag PCI_BUS_FLAGS_NO_AERSID is set + * 3) There are multiple errors and prior ID comparing fails; + * We check AER status registers to find possible reporter. + */ + if (atomic_read(&dev->enable_cnt) == 0) + return false; + + /* Check if AER is enabled */ + pcie_capability_read_word(dev, PCI_EXP_DEVCTL, ®16); + if (!(reg16 & PCI_EXP_AER_FLAGS)) + return false; + + pos = dev->aer_cap; + if (!pos) + return false; + + /* Check if error is recorded */ + if (e_info->severity == AER_CORRECTABLE) { + pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask); + } else { + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); + } + if (status & ~mask) + return true; + + return false; +} + +static int find_device_iter(struct pci_dev *dev, void *data) +{ + struct aer_err_info *e_info = (struct aer_err_info *)data; + + if (is_error_source(dev, e_info)) { + /* List this device */ + if (add_error_device(e_info, dev)) { + /* We cannot handle more... Stop iteration */ + /* TODO: Should print error message here? */ + return 1; + } + + /* If there is only a single error, stop iteration */ + if (!e_info->multi_error_valid) + return 1; + } + return 0; +} + +/** + * find_source_device - search through device hierarchy for source device + * @parent: pointer to Root Port pci_dev data structure + * @e_info: including detailed error information such like id + * + * Return true if found. + * + * Invoked by DPC when error is detected at the Root Port. + * Caller of this function must set id, severity, and multi_error_valid of + * struct aer_err_info pointed by @e_info properly. This function must fill + * e_info->error_dev_num and e_info->dev[], based on the given information. + */ +static bool find_source_device(struct pci_dev *parent, + struct aer_err_info *e_info) +{ + struct pci_dev *dev = parent; + int result; + + /* Must reset in this function */ + e_info->error_dev_num = 0; + + /* Is Root Port an agent that sends error message? */ + result = find_device_iter(dev, e_info); + if (result) + return true; + + pci_walk_bus(parent->subordinate, find_device_iter, e_info); + + if (!e_info->error_dev_num) { + pci_printk(KERN_DEBUG, parent, "can't find device of ID%04x\n", + e_info->id); + return false; + } + return true; +} + +/** + * handle_error_source - handle logging error into an event log + * @dev: pointer to pci_dev data structure of error source device + * @info: comprehensive error information + * + * Invoked when an error being detected by Root Port. + */ +static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info) +{ + int pos; + + if (info->severity == AER_CORRECTABLE) { + /* + * Correctable error does not need software intervention. + * No need to go through error recovery process. + */ + pos = dev->aer_cap; + if (pos) + pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, + info->status); + } else if (info->severity == AER_NONFATAL) + pcie_do_nonfatal_recovery(dev); + else if (info->severity == AER_FATAL) + pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER); +} + +#ifdef CONFIG_ACPI_APEI_PCIEAER + +#define AER_RECOVER_RING_ORDER 4 +#define AER_RECOVER_RING_SIZE (1 << AER_RECOVER_RING_ORDER) + +struct aer_recover_entry { + u8 bus; + u8 devfn; + u16 domain; + int severity; + struct aer_capability_regs *regs; +}; + +static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry, + AER_RECOVER_RING_SIZE); + +static void aer_recover_work_func(struct work_struct *work) +{ + struct aer_recover_entry entry; + struct pci_dev *pdev; + + while (kfifo_get(&aer_recover_ring, &entry)) { + pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus, + entry.devfn); + if (!pdev) { + pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n", + entry.domain, entry.bus, + PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn)); + continue; + } + cper_print_aer(pdev, entry.severity, entry.regs); + if (entry.severity == AER_NONFATAL) + pcie_do_nonfatal_recovery(pdev); + else if (entry.severity == AER_FATAL) + pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_AER); + pci_dev_put(pdev); + } +} + +/* + * Mutual exclusion for writers of aer_recover_ring, reader side don't + * need lock, because there is only one reader and lock is not needed + * between reader and writer. + */ +static DEFINE_SPINLOCK(aer_recover_ring_lock); +static DECLARE_WORK(aer_recover_work, aer_recover_work_func); + +void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, + int severity, struct aer_capability_regs *aer_regs) +{ + unsigned long flags; + struct aer_recover_entry entry = { + .bus = bus, + .devfn = devfn, + .domain = domain, + .severity = severity, + .regs = aer_regs, + }; + + spin_lock_irqsave(&aer_recover_ring_lock, flags); + if (kfifo_put(&aer_recover_ring, entry)) + schedule_work(&aer_recover_work); + else + pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n", + domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + spin_unlock_irqrestore(&aer_recover_ring_lock, flags); +} +EXPORT_SYMBOL_GPL(aer_recover_queue); +#endif + +/** + * get_device_error_info - read error status from dev and store it to info + * @dev: pointer to the device expected to have a error record + * @info: pointer to structure to store the error record + * + * Return 1 on success, 0 on error. + * + * Note that @info is reused among all error devices. Clear fields properly. + */ +static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) +{ + int pos, temp; + + /* Must reset in this function */ + info->status = 0; + info->tlp_header_valid = 0; + + pos = dev->aer_cap; + + /* The device might not support AER */ + if (!pos) + return 0; + + if (info->severity == AER_CORRECTABLE) { + pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, + &info->status); + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, + &info->mask); + if (!(info->status & ~info->mask)) + return 0; + } else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + info->severity == AER_NONFATAL) { + + /* Link is still healthy for IO reads */ + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, + &info->status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, + &info->mask); + if (!(info->status & ~info->mask)) + return 0; + + /* Get First Error Pointer */ + pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp); + info->first_error = PCI_ERR_CAP_FEP(temp); + + if (info->status & AER_LOG_TLP_MASKS) { + info->tlp_header_valid = 1; + pci_read_config_dword(dev, + pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); + pci_read_config_dword(dev, + pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); + pci_read_config_dword(dev, + pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); + pci_read_config_dword(dev, + pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); + } + } + + return 1; +} + +static inline void aer_process_err_devices(struct aer_err_info *e_info) +{ + int i; + + /* Report all before handle them, not to lost records by reset etc. */ + for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { + if (get_device_error_info(e_info->dev[i], e_info)) + aer_print_error(e_info->dev[i], e_info); + } + for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { + if (get_device_error_info(e_info->dev[i], e_info)) + handle_error_source(e_info->dev[i], e_info); + } +} + +/** + * aer_isr_one_error - consume an error detected by root port + * @rpc: pointer to the root port which holds an error + * @e_src: pointer to an error source + */ +static void aer_isr_one_error(struct aer_rpc *rpc, + struct aer_err_source *e_src) +{ + struct pci_dev *pdev = rpc->rpd; + struct aer_err_info *e_info = &rpc->e_info; + + /* + * There is a possibility that both correctable error and + * uncorrectable error being logged. Report correctable error first. + */ + if (e_src->status & PCI_ERR_ROOT_COR_RCV) { + e_info->id = ERR_COR_ID(e_src->id); + e_info->severity = AER_CORRECTABLE; + + if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV) + e_info->multi_error_valid = 1; + else + e_info->multi_error_valid = 0; + aer_print_port_info(pdev, e_info); + + if (find_source_device(pdev, e_info)) + aer_process_err_devices(e_info); + } + + if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) { + e_info->id = ERR_UNCOR_ID(e_src->id); + + if (e_src->status & PCI_ERR_ROOT_FATAL_RCV) + e_info->severity = AER_FATAL; + else + e_info->severity = AER_NONFATAL; + + if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV) + e_info->multi_error_valid = 1; + else + e_info->multi_error_valid = 0; + + aer_print_port_info(pdev, e_info); + + if (find_source_device(pdev, e_info)) + aer_process_err_devices(e_info); + } +} + +/** + * get_e_source - retrieve an error source + * @rpc: pointer to the root port which holds an error + * @e_src: pointer to store retrieved error source + * + * Return 1 if an error source is retrieved, otherwise 0. + * + * Invoked by DPC handler to consume an error. + */ +static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src) +{ + unsigned long flags; + + /* Lock access to Root error producer/consumer index */ + spin_lock_irqsave(&rpc->e_lock, flags); + if (rpc->prod_idx == rpc->cons_idx) { + spin_unlock_irqrestore(&rpc->e_lock, flags); + return 0; + } + + *e_src = rpc->e_sources[rpc->cons_idx]; + rpc->cons_idx++; + if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) + rpc->cons_idx = 0; + spin_unlock_irqrestore(&rpc->e_lock, flags); + + return 1; +} + +/** + * aer_isr - consume errors detected by root port + * @work: definition of this work item + * + * Invoked, as DPC, when root port records new detected error + */ +static void aer_isr(struct work_struct *work) +{ + struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); + struct aer_err_source uninitialized_var(e_src); + + mutex_lock(&rpc->rpc_mutex); + while (get_e_source(rpc, &e_src)) + aer_isr_one_error(rpc, &e_src); + mutex_unlock(&rpc->rpc_mutex); +} + +/** + * aer_irq - Root Port's ISR + * @irq: IRQ assigned to Root Port + * @context: pointer to Root Port data structure + * + * Invoked when Root Port detects AER messages. + */ +irqreturn_t aer_irq(int irq, void *context) +{ + unsigned int status, id; + struct pcie_device *pdev = (struct pcie_device *)context; + struct aer_rpc *rpc = get_service_data(pdev); + int next_prod_idx; + unsigned long flags; + int pos; + + pos = pdev->port->aer_cap; + /* + * Must lock access to Root Error Status Reg, Root Error ID Reg, + * and Root error producer/consumer index + */ + spin_lock_irqsave(&rpc->e_lock, flags); + + /* Read error status */ + pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); + if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) { + spin_unlock_irqrestore(&rpc->e_lock, flags); + return IRQ_NONE; + } + + /* Read error source and clear error status */ + pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id); + pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); + + /* Store error source for later DPC handler */ + next_prod_idx = rpc->prod_idx + 1; + if (next_prod_idx == AER_ERROR_SOURCES_MAX) + next_prod_idx = 0; + if (next_prod_idx == rpc->cons_idx) { + /* + * Error Storm Condition - possibly the same error occurred. + * Drop the error. + */ + spin_unlock_irqrestore(&rpc->e_lock, flags); + return IRQ_HANDLED; + } + rpc->e_sources[rpc->prod_idx].status = status; + rpc->e_sources[rpc->prod_idx].id = id; + rpc->prod_idx = next_prod_idx; + spin_unlock_irqrestore(&rpc->e_lock, flags); + + /* Invoke DPC handler */ + schedule_work(&rpc->dpc_handler); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(aer_irq); + +static int set_device_error_reporting(struct pci_dev *dev, void *data) +{ + bool enable = *((bool *)data); + int type = pci_pcie_type(dev); + + if ((type == PCI_EXP_TYPE_ROOT_PORT) || + (type == PCI_EXP_TYPE_UPSTREAM) || + (type == PCI_EXP_TYPE_DOWNSTREAM)) { + if (enable) + pci_enable_pcie_error_reporting(dev); + else + pci_disable_pcie_error_reporting(dev); + } + + if (enable) + pcie_set_ecrc_checking(dev); + + return 0; +} + +/** + * set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports. + * @dev: pointer to root port's pci_dev data structure + * @enable: true = enable error reporting, false = disable error reporting. + */ +static void set_downstream_devices_error_reporting(struct pci_dev *dev, + bool enable) +{ + set_device_error_reporting(dev, &enable); + + if (!dev->subordinate) + return; + pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable); +} + +/** + * aer_enable_rootport - enable Root Port's interrupts when receiving messages + * @rpc: pointer to a Root Port data structure + * + * Invoked when PCIe bus loads AER service driver. + */ +static void aer_enable_rootport(struct aer_rpc *rpc) +{ + struct pci_dev *pdev = rpc->rpd; + int aer_pos; + u16 reg16; + u32 reg32; + + /* Clear PCIe Capability's Device Status */ + pcie_capability_read_word(pdev, PCI_EXP_DEVSTA, ®16); + pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, reg16); + + /* Disable system error generation in response to error messages */ + pcie_capability_clear_word(pdev, PCI_EXP_RTCTL, + SYSTEM_ERROR_INTR_ON_MESG_MASK); + + aer_pos = pdev->aer_cap; + /* Clear error status */ + pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); + pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); + pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); + pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); + pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); + pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); + + /* + * Enable error reporting for the root port device and downstream port + * devices. + */ + set_downstream_devices_error_reporting(pdev, true); + + /* Enable Root Port's interrupt in response to error messages */ + pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32); +} + +/** + * aer_disable_rootport - disable Root Port's interrupts when receiving messages + * @rpc: pointer to a Root Port data structure + * + * Invoked when PCIe bus unloads AER service driver. + */ +static void aer_disable_rootport(struct aer_rpc *rpc) +{ + struct pci_dev *pdev = rpc->rpd; + u32 reg32; + int pos; + + /* + * Disable error reporting for the root port device and downstream port + * devices. + */ + set_downstream_devices_error_reporting(pdev, false); + + pos = pdev->aer_cap; + /* Disable Root's interrupt in response to error messages */ + pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32); + + /* Clear Root's error status reg */ + pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); + pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); +} + +/** + * aer_alloc_rpc - allocate Root Port data structure + * @dev: pointer to the pcie_dev data structure + * + * Invoked when Root Port's AER service is loaded. + */ +static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) +{ + struct aer_rpc *rpc; + + rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL); + if (!rpc) + return NULL; + + /* Initialize Root lock access, e_lock, to Root Error Status Reg */ + spin_lock_init(&rpc->e_lock); + + rpc->rpd = dev->port; + INIT_WORK(&rpc->dpc_handler, aer_isr); + mutex_init(&rpc->rpc_mutex); + + /* Use PCIe bus function to store rpc into PCIe device */ + set_service_data(dev, rpc); + + return rpc; +} + +/** + * aer_remove - clean up resources + * @dev: pointer to the pcie_dev data structure + * + * Invoked when PCI Express bus unloads or AER probe fails. + */ +static void aer_remove(struct pcie_device *dev) +{ + struct aer_rpc *rpc = get_service_data(dev); + + if (rpc) { + /* If register interrupt service, it must be free. */ + if (rpc->isr) + free_irq(dev->irq, dev); + + flush_work(&rpc->dpc_handler); + aer_disable_rootport(rpc); + kfree(rpc); + set_service_data(dev, NULL); + } +} + +/** + * aer_probe - initialize resources + * @dev: pointer to the pcie_dev data structure + * + * Invoked when PCI Express bus loads AER service driver. + */ +static int aer_probe(struct pcie_device *dev) +{ + int status; + struct aer_rpc *rpc; + struct device *device = &dev->port->dev; + + /* Alloc rpc data structure */ + rpc = aer_alloc_rpc(dev); + if (!rpc) { + dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n"); + aer_remove(dev); + return -ENOMEM; + } + + /* Request IRQ ISR */ + status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev); + if (status) { + dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n", + dev->irq); + aer_remove(dev); + return status; + } + + rpc->isr = 1; + + aer_enable_rootport(rpc); + dev_info(device, "AER enabled with IRQ %d\n", dev->irq); + return 0; +} + +/** + * aer_root_reset - reset link on Root Port + * @dev: pointer to Root Port's pci_dev data structure + * + * Invoked by Port Bus driver when performing link reset at Root Port. + */ +static pci_ers_result_t aer_root_reset(struct pci_dev *dev) +{ + u32 reg32; + int pos; + + pos = dev->aer_cap; + + /* Disable Root's interrupt in response to error messages */ + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); + + pci_reset_bridge_secondary_bus(dev); + pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n"); + + /* Clear Root Error Status */ + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); + pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32); + + /* Enable Root Port's interrupt in response to error messages */ + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); + + return PCI_ERS_RESULT_RECOVERED; +} + +/** + * aer_error_resume - clean up corresponding error status bits + * @dev: pointer to Root Port's pci_dev data structure + * + * Invoked by Port Bus driver during nonfatal recovery. + */ +static void aer_error_resume(struct pci_dev *dev) +{ + int pos; + u32 status, mask; + u16 reg16; + + /* Clean up Root device status */ + pcie_capability_read_word(dev, PCI_EXP_DEVSTA, ®16); + pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16); + + /* Clean AER Root Error Status */ + pos = dev->aer_cap; + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); + status &= ~mask; /* Clear corresponding nonfatal bits */ + pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); +} + +static struct pcie_port_service_driver aerdriver = { + .name = "aer", + .port_type = PCI_EXP_TYPE_ROOT_PORT, + .service = PCIE_PORT_SERVICE_AER, + + .probe = aer_probe, + .remove = aer_remove, + .error_resume = aer_error_resume, + .reset_link = aer_root_reset, +}; + +/** + * aer_service_init - register AER root service driver + * + * Invoked when AER root service driver is loaded. + */ +static int __init aer_service_init(void) +{ + if (!pci_aer_available() || aer_acpi_firmware_first()) + return -ENXIO; + return pcie_port_service_register(&aerdriver); +} +device_initcall(aer_service_init); diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig deleted file mode 100644 index 1996df082e89..000000000000 --- a/drivers/pci/pcie/aer/Kconfig +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# PCI Express Root Port Device AER Configuration -# - -config PCIEAER - bool "Root Port Advanced Error Reporting support" - depends on PCIEPORTBUS - select RAS - default y - help - This enables PCI Express Root Port Advanced Error Reporting - (AER) driver support. Error reporting messages sent to Root - Port will be handled by PCI Express AER driver. - -config PCIEAER_INJECT - tristate "PCIe AER error injector support" - depends on PCIEAER - default n - help - This enables PCI Express Root Port Advanced Error Reporting - (AER) software error injector. - - Debugging PCIe AER code is quite difficult because it is hard - to trigger various real hardware errors. Software based - error injection can fake almost all kinds of errors with the - help of a user space helper tool aer-inject, which can be - gotten from: - http://www.kernel.org/pub/linux/utils/pci/aer-inject/ - -# -# PCI Express ECRC -# -config PCIE_ECRC - bool "PCI Express ECRC settings control" - depends on PCIEAER - help - Used to override firmware/bios settings for PCI Express ECRC - (transaction layer end-to-end CRC checking). - - When in doubt, say N. diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile deleted file mode 100644 index af756b6e7e33..000000000000 --- a/drivers/pci/pcie/aer/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for PCI-Express Root Port Advanced Error Reporting Driver -# - -obj-$(CONFIG_PCIEAER) += aerdriver.o - -aerdriver-objs := aerdrv.o - -obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c deleted file mode 100644 index 6c5fda96778a..000000000000 --- a/drivers/pci/pcie/aer/aer_inject.c +++ /dev/null @@ -1,551 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCIe AER software error injection support. - * - * Debuging PCIe AER code is quite difficult because it is hard to - * trigger various real hardware errors. Software based error - * injection can fake almost all kinds of errors with the help of a - * user space helper tool aer-inject, which can be gotten from: - * http://www.kernel.org/pub/linux/utils/pci/aer-inject/ - * - * Copyright 2009 Intel Corporation. - * Huang Ying - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../portdrv.h" - -/* Override the existing corrected and uncorrected error masks */ -static bool aer_mask_override; -module_param(aer_mask_override, bool, 0); - -struct aer_error_inj { - u8 bus; - u8 dev; - u8 fn; - u32 uncor_status; - u32 cor_status; - u32 header_log0; - u32 header_log1; - u32 header_log2; - u32 header_log3; - u32 domain; -}; - -struct aer_error { - struct list_head list; - u32 domain; - unsigned int bus; - unsigned int devfn; - int pos_cap_err; - - u32 uncor_status; - u32 cor_status; - u32 header_log0; - u32 header_log1; - u32 header_log2; - u32 header_log3; - u32 root_status; - u32 source_id; -}; - -struct pci_bus_ops { - struct list_head list; - struct pci_bus *bus; - struct pci_ops *ops; -}; - -static LIST_HEAD(einjected); - -static LIST_HEAD(pci_bus_ops_list); - -/* Protect einjected and pci_bus_ops_list */ -static DEFINE_SPINLOCK(inject_lock); - -static void aer_error_init(struct aer_error *err, u32 domain, - unsigned int bus, unsigned int devfn, - int pos_cap_err) -{ - INIT_LIST_HEAD(&err->list); - err->domain = domain; - err->bus = bus; - err->devfn = devfn; - err->pos_cap_err = pos_cap_err; -} - -/* inject_lock must be held before calling */ -static struct aer_error *__find_aer_error(u32 domain, unsigned int bus, - unsigned int devfn) -{ - struct aer_error *err; - - list_for_each_entry(err, &einjected, list) { - if (domain == err->domain && - bus == err->bus && - devfn == err->devfn) - return err; - } - return NULL; -} - -/* inject_lock must be held before calling */ -static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev) -{ - int domain = pci_domain_nr(dev->bus); - if (domain < 0) - return NULL; - return __find_aer_error(domain, dev->bus->number, dev->devfn); -} - -/* inject_lock must be held before calling */ -static struct pci_ops *__find_pci_bus_ops(struct pci_bus *bus) -{ - struct pci_bus_ops *bus_ops; - - list_for_each_entry(bus_ops, &pci_bus_ops_list, list) { - if (bus_ops->bus == bus) - return bus_ops->ops; - } - return NULL; -} - -static struct pci_bus_ops *pci_bus_ops_pop(void) -{ - unsigned long flags; - struct pci_bus_ops *bus_ops; - - spin_lock_irqsave(&inject_lock, flags); - bus_ops = list_first_entry_or_null(&pci_bus_ops_list, - struct pci_bus_ops, list); - if (bus_ops) - list_del(&bus_ops->list); - spin_unlock_irqrestore(&inject_lock, flags); - return bus_ops; -} - -static u32 *find_pci_config_dword(struct aer_error *err, int where, - int *prw1cs) -{ - int rw1cs = 0; - u32 *target = NULL; - - if (err->pos_cap_err == -1) - return NULL; - - switch (where - err->pos_cap_err) { - case PCI_ERR_UNCOR_STATUS: - target = &err->uncor_status; - rw1cs = 1; - break; - case PCI_ERR_COR_STATUS: - target = &err->cor_status; - rw1cs = 1; - break; - case PCI_ERR_HEADER_LOG: - target = &err->header_log0; - break; - case PCI_ERR_HEADER_LOG+4: - target = &err->header_log1; - break; - case PCI_ERR_HEADER_LOG+8: - target = &err->header_log2; - break; - case PCI_ERR_HEADER_LOG+12: - target = &err->header_log3; - break; - case PCI_ERR_ROOT_STATUS: - target = &err->root_status; - rw1cs = 1; - break; - case PCI_ERR_ROOT_ERR_SRC: - target = &err->source_id; - break; - } - if (prw1cs) - *prw1cs = rw1cs; - return target; -} - -static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - u32 *sim; - struct aer_error *err; - unsigned long flags; - struct pci_ops *ops; - struct pci_ops *my_ops; - int domain; - int rv; - - spin_lock_irqsave(&inject_lock, flags); - if (size != sizeof(u32)) - goto out; - domain = pci_domain_nr(bus); - if (domain < 0) - goto out; - err = __find_aer_error(domain, bus->number, devfn); - if (!err) - goto out; - - sim = find_pci_config_dword(err, where, NULL); - if (sim) { - *val = *sim; - spin_unlock_irqrestore(&inject_lock, flags); - return 0; - } -out: - ops = __find_pci_bus_ops(bus); - /* - * pci_lock must already be held, so we can directly - * manipulate bus->ops. Many config access functions, - * including pci_generic_config_read() require the original - * bus->ops be installed to function, so temporarily put them - * back. - */ - my_ops = bus->ops; - bus->ops = ops; - rv = ops->read(bus, devfn, where, size, val); - bus->ops = my_ops; - spin_unlock_irqrestore(&inject_lock, flags); - return rv; -} - -static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - u32 *sim; - struct aer_error *err; - unsigned long flags; - int rw1cs; - struct pci_ops *ops; - struct pci_ops *my_ops; - int domain; - int rv; - - spin_lock_irqsave(&inject_lock, flags); - if (size != sizeof(u32)) - goto out; - domain = pci_domain_nr(bus); - if (domain < 0) - goto out; - err = __find_aer_error(domain, bus->number, devfn); - if (!err) - goto out; - - sim = find_pci_config_dword(err, where, &rw1cs); - if (sim) { - if (rw1cs) - *sim ^= val; - else - *sim = val; - spin_unlock_irqrestore(&inject_lock, flags); - return 0; - } -out: - ops = __find_pci_bus_ops(bus); - /* - * pci_lock must already be held, so we can directly - * manipulate bus->ops. Many config access functions, - * including pci_generic_config_write() require the original - * bus->ops be installed to function, so temporarily put them - * back. - */ - my_ops = bus->ops; - bus->ops = ops; - rv = ops->write(bus, devfn, where, size, val); - bus->ops = my_ops; - spin_unlock_irqrestore(&inject_lock, flags); - return rv; -} - -static struct pci_ops aer_inj_pci_ops = { - .read = aer_inj_read_config, - .write = aer_inj_write_config, -}; - -static void pci_bus_ops_init(struct pci_bus_ops *bus_ops, - struct pci_bus *bus, - struct pci_ops *ops) -{ - INIT_LIST_HEAD(&bus_ops->list); - bus_ops->bus = bus; - bus_ops->ops = ops; -} - -static int pci_bus_set_aer_ops(struct pci_bus *bus) -{ - struct pci_ops *ops; - struct pci_bus_ops *bus_ops; - unsigned long flags; - - bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL); - if (!bus_ops) - return -ENOMEM; - ops = pci_bus_set_ops(bus, &aer_inj_pci_ops); - spin_lock_irqsave(&inject_lock, flags); - if (ops == &aer_inj_pci_ops) - goto out; - pci_bus_ops_init(bus_ops, bus, ops); - list_add(&bus_ops->list, &pci_bus_ops_list); - bus_ops = NULL; -out: - spin_unlock_irqrestore(&inject_lock, flags); - kfree(bus_ops); - return 0; -} - -static int find_aer_device_iter(struct device *device, void *data) -{ - struct pcie_device **result = data; - struct pcie_device *pcie_dev; - - if (device->bus == &pcie_port_bus_type) { - pcie_dev = to_pcie_device(device); - if (pcie_dev->service & PCIE_PORT_SERVICE_AER) { - *result = pcie_dev; - return 1; - } - } - return 0; -} - -static int find_aer_device(struct pci_dev *dev, struct pcie_device **result) -{ - return device_for_each_child(&dev->dev, result, find_aer_device_iter); -} - -static int aer_inject(struct aer_error_inj *einj) -{ - struct aer_error *err, *rperr; - struct aer_error *err_alloc = NULL, *rperr_alloc = NULL; - struct pci_dev *dev, *rpdev; - struct pcie_device *edev; - unsigned long flags; - unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn); - int pos_cap_err, rp_pos_cap_err; - u32 sever, cor_mask, uncor_mask, cor_mask_orig = 0, uncor_mask_orig = 0; - int ret = 0; - - dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn); - if (!dev) - return -ENODEV; - rpdev = pcie_find_root_port(dev); - if (!rpdev) { - pci_err(dev, "aer_inject: Root port not found\n"); - ret = -ENODEV; - goto out_put; - } - - pos_cap_err = dev->aer_cap; - if (!pos_cap_err) { - pci_err(dev, "aer_inject: Device doesn't support AER\n"); - ret = -EPROTONOSUPPORT; - goto out_put; - } - pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever); - pci_read_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, &cor_mask); - pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, - &uncor_mask); - - rp_pos_cap_err = rpdev->aer_cap; - if (!rp_pos_cap_err) { - pci_err(rpdev, "aer_inject: Root port doesn't support AER\n"); - ret = -EPROTONOSUPPORT; - goto out_put; - } - - err_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL); - if (!err_alloc) { - ret = -ENOMEM; - goto out_put; - } - rperr_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL); - if (!rperr_alloc) { - ret = -ENOMEM; - goto out_put; - } - - if (aer_mask_override) { - cor_mask_orig = cor_mask; - cor_mask &= !(einj->cor_status); - pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, - cor_mask); - - uncor_mask_orig = uncor_mask; - uncor_mask &= !(einj->uncor_status); - pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, - uncor_mask); - } - - spin_lock_irqsave(&inject_lock, flags); - - err = __find_aer_error_by_dev(dev); - if (!err) { - err = err_alloc; - err_alloc = NULL; - aer_error_init(err, einj->domain, einj->bus, devfn, - pos_cap_err); - list_add(&err->list, &einjected); - } - err->uncor_status |= einj->uncor_status; - err->cor_status |= einj->cor_status; - err->header_log0 = einj->header_log0; - err->header_log1 = einj->header_log1; - err->header_log2 = einj->header_log2; - err->header_log3 = einj->header_log3; - - if (!aer_mask_override && einj->cor_status && - !(einj->cor_status & ~cor_mask)) { - ret = -EINVAL; - pci_warn(dev, "aer_inject: The correctable error(s) is masked by device\n"); - spin_unlock_irqrestore(&inject_lock, flags); - goto out_put; - } - if (!aer_mask_override && einj->uncor_status && - !(einj->uncor_status & ~uncor_mask)) { - ret = -EINVAL; - pci_warn(dev, "aer_inject: The uncorrectable error(s) is masked by device\n"); - spin_unlock_irqrestore(&inject_lock, flags); - goto out_put; - } - - rperr = __find_aer_error_by_dev(rpdev); - if (!rperr) { - rperr = rperr_alloc; - rperr_alloc = NULL; - aer_error_init(rperr, pci_domain_nr(rpdev->bus), - rpdev->bus->number, rpdev->devfn, - rp_pos_cap_err); - list_add(&rperr->list, &einjected); - } - if (einj->cor_status) { - if (rperr->root_status & PCI_ERR_ROOT_COR_RCV) - rperr->root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; - else - rperr->root_status |= PCI_ERR_ROOT_COR_RCV; - rperr->source_id &= 0xffff0000; - rperr->source_id |= (einj->bus << 8) | devfn; - } - if (einj->uncor_status) { - if (rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV) - rperr->root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; - if (sever & einj->uncor_status) { - rperr->root_status |= PCI_ERR_ROOT_FATAL_RCV; - if (!(rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV)) - rperr->root_status |= PCI_ERR_ROOT_FIRST_FATAL; - } else - rperr->root_status |= PCI_ERR_ROOT_NONFATAL_RCV; - rperr->root_status |= PCI_ERR_ROOT_UNCOR_RCV; - rperr->source_id &= 0x0000ffff; - rperr->source_id |= ((einj->bus << 8) | devfn) << 16; - } - spin_unlock_irqrestore(&inject_lock, flags); - - if (aer_mask_override) { - pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, - cor_mask_orig); - pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, - uncor_mask_orig); - } - - ret = pci_bus_set_aer_ops(dev->bus); - if (ret) - goto out_put; - ret = pci_bus_set_aer_ops(rpdev->bus); - if (ret) - goto out_put; - - if (find_aer_device(rpdev, &edev)) { - if (!get_service_data(edev)) { - dev_warn(&edev->device, - "aer_inject: AER service is not initialized\n"); - ret = -EPROTONOSUPPORT; - goto out_put; - } - dev_info(&edev->device, - "aer_inject: Injecting errors %08x/%08x into device %s\n", - einj->cor_status, einj->uncor_status, pci_name(dev)); - aer_irq(-1, edev); - } else { - pci_err(rpdev, "aer_inject: AER device not found\n"); - ret = -ENODEV; - } -out_put: - kfree(err_alloc); - kfree(rperr_alloc); - pci_dev_put(dev); - return ret; -} - -static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf, - size_t usize, loff_t *off) -{ - struct aer_error_inj einj; - int ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (usize < offsetof(struct aer_error_inj, domain) || - usize > sizeof(einj)) - return -EINVAL; - - memset(&einj, 0, sizeof(einj)); - if (copy_from_user(&einj, ubuf, usize)) - return -EFAULT; - - ret = aer_inject(&einj); - return ret ? ret : usize; -} - -static const struct file_operations aer_inject_fops = { - .write = aer_inject_write, - .owner = THIS_MODULE, - .llseek = noop_llseek, -}; - -static struct miscdevice aer_inject_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "aer_inject", - .fops = &aer_inject_fops, -}; - -static int __init aer_inject_init(void) -{ - return misc_register(&aer_inject_device); -} - -static void __exit aer_inject_exit(void) -{ - struct aer_error *err, *err_next; - unsigned long flags; - struct pci_bus_ops *bus_ops; - - misc_deregister(&aer_inject_device); - - while ((bus_ops = pci_bus_ops_pop())) { - pci_bus_set_ops(bus_ops->bus, bus_ops->ops); - kfree(bus_ops); - } - - spin_lock_irqsave(&inject_lock, flags); - list_for_each_entry_safe(err, err_next, &einjected, list) { - list_del(&err->list); - kfree(err); - } - spin_unlock_irqrestore(&inject_lock, flags); -} - -module_init(aer_inject_init); -module_exit(aer_inject_exit); - -MODULE_DESCRIPTION("PCIe AER software error injector"); -MODULE_LICENSE("GPL"); diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c deleted file mode 100644 index 3fc23fc95f98..000000000000 --- a/drivers/pci/pcie/aer/aerdrv.c +++ /dev/null @@ -1,1377 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Implement the AER root port service driver. The driver registers an IRQ - * handler. When a root port triggers an AER interrupt, the IRQ handler - * collects root port status and schedules work. - * - * Copyright (C) 2006 Intel Corp. - * Tom Long Nguyen (tom.l.nguyen@intel.com) - * Zhang Yanmin (yanmin.zhang@intel.com) - * - * (C) Copyright 2009 Hewlett-Packard Development Company, L.P. - * Andrew Patterson - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../pci.h" -#include "../portdrv.h" - -#define AER_ERROR_SOURCES_MAX 100 -#define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */ - -struct aer_err_info { - struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; - int error_dev_num; - - unsigned int id:16; - - unsigned int severity:2; /* 0:NONFATAL | 1:FATAL | 2:COR */ - unsigned int __pad1:5; - unsigned int multi_error_valid:1; - - unsigned int first_error:5; - unsigned int __pad2:2; - unsigned int tlp_header_valid:1; - - unsigned int status; /* COR/UNCOR Error Status */ - unsigned int mask; /* COR/UNCOR Error Mask */ - struct aer_header_log_regs tlp; /* TLP Header */ -}; - -struct aer_err_source { - unsigned int status; - unsigned int id; -}; - -struct aer_rpc { - struct pci_dev *rpd; /* Root Port device */ - struct work_struct dpc_handler; - struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX]; - struct aer_err_info e_info; - unsigned short prod_idx; /* Error Producer Index */ - unsigned short cons_idx; /* Error Consumer Index */ - int isr; - spinlock_t e_lock; /* - * Lock access to Error Status/ID Regs - * and error producer/consumer index - */ - struct mutex rpc_mutex; /* - * only one thread could do - * recovery on the same - * root port hierarchy - */ -}; - -#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ - PCI_ERR_UNC_ECRC| \ - PCI_ERR_UNC_UNSUP| \ - PCI_ERR_UNC_COMP_ABORT| \ - PCI_ERR_UNC_UNX_COMP| \ - PCI_ERR_UNC_MALF_TLP) - -#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ - PCI_EXP_RTCTL_SENFEE| \ - PCI_EXP_RTCTL_SEFEE) -#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \ - PCI_ERR_ROOT_CMD_NONFATAL_EN| \ - PCI_ERR_ROOT_CMD_FATAL_EN) -#define ERR_COR_ID(d) (d & 0xffff) -#define ERR_UNCOR_ID(d) (d >> 16) - -static int pcie_aer_disable; - -void pci_no_aer(void) -{ - pcie_aer_disable = 1; -} - -bool pci_aer_available(void) -{ - return !pcie_aer_disable && pci_msi_enabled(); -} - -#ifdef CONFIG_PCIE_ECRC - -#define ECRC_POLICY_DEFAULT 0 /* ECRC set by BIOS */ -#define ECRC_POLICY_OFF 1 /* ECRC off for performance */ -#define ECRC_POLICY_ON 2 /* ECRC on for data integrity */ - -static int ecrc_policy = ECRC_POLICY_DEFAULT; - -static const char *ecrc_policy_str[] = { - [ECRC_POLICY_DEFAULT] = "bios", - [ECRC_POLICY_OFF] = "off", - [ECRC_POLICY_ON] = "on" -}; - -/** - * enable_ercr_checking - enable PCIe ECRC checking for a device - * @dev: the PCI device - * - * Returns 0 on success, or negative on failure. - */ -static int enable_ecrc_checking(struct pci_dev *dev) -{ - int pos; - u32 reg32; - - if (!pci_is_pcie(dev)) - return -ENODEV; - - pos = dev->aer_cap; - if (!pos) - return -ENODEV; - - pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); - if (reg32 & PCI_ERR_CAP_ECRC_GENC) - reg32 |= PCI_ERR_CAP_ECRC_GENE; - if (reg32 & PCI_ERR_CAP_ECRC_CHKC) - reg32 |= PCI_ERR_CAP_ECRC_CHKE; - pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); - - return 0; -} - -/** - * disable_ercr_checking - disables PCIe ECRC checking for a device - * @dev: the PCI device - * - * Returns 0 on success, or negative on failure. - */ -static int disable_ecrc_checking(struct pci_dev *dev) -{ - int pos; - u32 reg32; - - if (!pci_is_pcie(dev)) - return -ENODEV; - - pos = dev->aer_cap; - if (!pos) - return -ENODEV; - - pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); - reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); - pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); - - return 0; -} - -/** - * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy - * @dev: the PCI device - */ -void pcie_set_ecrc_checking(struct pci_dev *dev) -{ - switch (ecrc_policy) { - case ECRC_POLICY_DEFAULT: - return; - case ECRC_POLICY_OFF: - disable_ecrc_checking(dev); - break; - case ECRC_POLICY_ON: - enable_ecrc_checking(dev); - break; - default: - return; - } -} - -/** - * pcie_ecrc_get_policy - parse kernel command-line ecrc option - */ -void pcie_ecrc_get_policy(char *str) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) - if (!strncmp(str, ecrc_policy_str[i], - strlen(ecrc_policy_str[i]))) - break; - if (i >= ARRAY_SIZE(ecrc_policy_str)) - return; - - ecrc_policy = i; -} -#endif /* CONFIG_PCIE_ECRC */ - -#ifdef CONFIG_ACPI_APEI -static inline int hest_match_pci(struct acpi_hest_aer_common *p, - struct pci_dev *pci) -{ - return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) && - ACPI_HEST_BUS(p->bus) == pci->bus->number && - p->device == PCI_SLOT(pci->devfn) && - p->function == PCI_FUNC(pci->devfn); -} - -static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, - struct pci_dev *dev) -{ - u16 hest_type = hest_hdr->type; - u8 pcie_type = pci_pcie_type(dev); - - if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT && - pcie_type == PCI_EXP_TYPE_ROOT_PORT) || - (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT && - pcie_type == PCI_EXP_TYPE_ENDPOINT) || - (hest_type == ACPI_HEST_TYPE_AER_BRIDGE && - (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)) - return true; - return false; -} - -struct aer_hest_parse_info { - struct pci_dev *pci_dev; - int firmware_first; -}; - -static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr) -{ - if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT || - hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT || - hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE) - return 1; - return 0; -} - -static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) -{ - struct aer_hest_parse_info *info = data; - struct acpi_hest_aer_common *p; - int ff; - - if (!hest_source_is_pcie_aer(hest_hdr)) - return 0; - - p = (struct acpi_hest_aer_common *)(hest_hdr + 1); - ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); - - /* - * If no specific device is supplied, determine whether - * FIRMWARE_FIRST is set for *any* PCIe device. - */ - if (!info->pci_dev) { - info->firmware_first |= ff; - return 0; - } - - /* Otherwise, check the specific device */ - if (p->flags & ACPI_HEST_GLOBAL) { - if (hest_match_type(hest_hdr, info->pci_dev)) - info->firmware_first = ff; - } else - if (hest_match_pci(p, info->pci_dev)) - info->firmware_first = ff; - - return 0; -} - -static void aer_set_firmware_first(struct pci_dev *pci_dev) -{ - int rc; - struct aer_hest_parse_info info = { - .pci_dev = pci_dev, - .firmware_first = 0, - }; - - rc = apei_hest_parse(aer_hest_parse, &info); - - if (rc) - pci_dev->__aer_firmware_first = 0; - else - pci_dev->__aer_firmware_first = info.firmware_first; - pci_dev->__aer_firmware_first_valid = 1; -} - -int pcie_aer_get_firmware_first(struct pci_dev *dev) -{ - if (!pci_is_pcie(dev)) - return 0; - - if (!dev->__aer_firmware_first_valid) - aer_set_firmware_first(dev); - return dev->__aer_firmware_first; -} -#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) - -static bool aer_firmware_first; - -/** - * aer_acpi_firmware_first - Check if APEI should control AER. - */ -bool aer_acpi_firmware_first(void) -{ - static bool parsed = false; - struct aer_hest_parse_info info = { - .pci_dev = NULL, /* Check all PCIe devices */ - .firmware_first = 0, - }; - - if (!parsed) { - apei_hest_parse(aer_hest_parse, &info); - aer_firmware_first = info.firmware_first; - parsed = true; - } - return aer_firmware_first; -} -#endif - -#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) - -int pci_enable_pcie_error_reporting(struct pci_dev *dev) -{ - if (pcie_aer_get_firmware_first(dev)) - return -EIO; - - if (!dev->aer_cap) - return -EIO; - - return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); -} -EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); - -int pci_disable_pcie_error_reporting(struct pci_dev *dev) -{ - if (pcie_aer_get_firmware_first(dev)) - return -EIO; - - return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL, - PCI_EXP_AER_FLAGS); -} -EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); - -int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) -{ - int pos; - u32 status; - - pos = dev->aer_cap; - if (!pos) - return -EIO; - - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); - if (status) - pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); - - return 0; -} -EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); - -int pci_cleanup_aer_error_status_regs(struct pci_dev *dev) -{ - int pos; - u32 status; - int port_type; - - if (!pci_is_pcie(dev)) - return -ENODEV; - - pos = dev->aer_cap; - if (!pos) - return -EIO; - - port_type = pci_pcie_type(dev); - if (port_type == PCI_EXP_TYPE_ROOT_PORT) { - pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); - pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); - } - - pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); - pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status); - - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); - pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); - - return 0; -} - -int pci_aer_init(struct pci_dev *dev) -{ - dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); - return pci_cleanup_aer_error_status_regs(dev); -} - -#define AER_AGENT_RECEIVER 0 -#define AER_AGENT_REQUESTER 1 -#define AER_AGENT_COMPLETER 2 -#define AER_AGENT_TRANSMITTER 3 - -#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) -#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ - 0 : PCI_ERR_UNC_COMP_ABORT) -#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) - -#define AER_GET_AGENT(t, e) \ - ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ - (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ - (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ - AER_AGENT_RECEIVER) - -#define AER_PHYSICAL_LAYER_ERROR 0 -#define AER_DATA_LINK_LAYER_ERROR 1 -#define AER_TRANSACTION_LAYER_ERROR 2 - -#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ - PCI_ERR_COR_RCVR : 0) -#define AER_DATA_LINK_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ - (PCI_ERR_COR_BAD_TLP| \ - PCI_ERR_COR_BAD_DLLP| \ - PCI_ERR_COR_REP_ROLL| \ - PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) - -#define AER_GET_LAYER_ERROR(t, e) \ - ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ - (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ - AER_TRANSACTION_LAYER_ERROR) - -/* - * AER error strings - */ -static const char *aer_error_severity_string[] = { - "Uncorrected (Non-Fatal)", - "Uncorrected (Fatal)", - "Corrected" -}; - -static const char *aer_error_layer[] = { - "Physical Layer", - "Data Link Layer", - "Transaction Layer" -}; - -static const char *aer_correctable_error_string[] = { - "Receiver Error", /* Bit Position 0 */ - NULL, - NULL, - NULL, - NULL, - NULL, - "Bad TLP", /* Bit Position 6 */ - "Bad DLLP", /* Bit Position 7 */ - "RELAY_NUM Rollover", /* Bit Position 8 */ - NULL, - NULL, - NULL, - "Replay Timer Timeout", /* Bit Position 12 */ - "Advisory Non-Fatal", /* Bit Position 13 */ - "Corrected Internal Error", /* Bit Position 14 */ - "Header Log Overflow", /* Bit Position 15 */ -}; - -static const char *aer_uncorrectable_error_string[] = { - "Undefined", /* Bit Position 0 */ - NULL, - NULL, - NULL, - "Data Link Protocol", /* Bit Position 4 */ - "Surprise Down Error", /* Bit Position 5 */ - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - "Poisoned TLP", /* Bit Position 12 */ - "Flow Control Protocol", /* Bit Position 13 */ - "Completion Timeout", /* Bit Position 14 */ - "Completer Abort", /* Bit Position 15 */ - "Unexpected Completion", /* Bit Position 16 */ - "Receiver Overflow", /* Bit Position 17 */ - "Malformed TLP", /* Bit Position 18 */ - "ECRC", /* Bit Position 19 */ - "Unsupported Request", /* Bit Position 20 */ - "ACS Violation", /* Bit Position 21 */ - "Uncorrectable Internal Error", /* Bit Position 22 */ - "MC Blocked TLP", /* Bit Position 23 */ - "AtomicOp Egress Blocked", /* Bit Position 24 */ - "TLP Prefix Blocked Error", /* Bit Position 25 */ -}; - -static const char *aer_agent_string[] = { - "Receiver ID", - "Requester ID", - "Completer ID", - "Transmitter ID" -}; - -static void __print_tlp_header(struct pci_dev *dev, - struct aer_header_log_regs *t) -{ - pci_err(dev, " TLP Header: %08x %08x %08x %08x\n", - t->dw0, t->dw1, t->dw2, t->dw3); -} - -static void __aer_print_error(struct pci_dev *dev, - struct aer_err_info *info) -{ - int i, status; - const char *errmsg = NULL; - status = (info->status & ~info->mask); - - for (i = 0; i < 32; i++) { - if (!(status & (1 << i))) - continue; - - if (info->severity == AER_CORRECTABLE) - errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ? - aer_correctable_error_string[i] : NULL; - else - errmsg = i < ARRAY_SIZE(aer_uncorrectable_error_string) ? - aer_uncorrectable_error_string[i] : NULL; - - if (errmsg) - pci_err(dev, " [%2d] %-22s%s\n", i, errmsg, - info->first_error == i ? " (First)" : ""); - else - pci_err(dev, " [%2d] Unknown Error Bit%s\n", - i, info->first_error == i ? " (First)" : ""); - } -} - -static void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) -{ - int layer, agent; - int id = ((dev->bus->number << 8) | dev->devfn); - - if (!info->status) { - pci_err(dev, "PCIe Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n", - aer_error_severity_string[info->severity]); - goto out; - } - - layer = AER_GET_LAYER_ERROR(info->severity, info->status); - agent = AER_GET_AGENT(info->severity, info->status); - - pci_err(dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n", - aer_error_severity_string[info->severity], - aer_error_layer[layer], aer_agent_string[agent]); - - pci_err(dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", - dev->vendor, dev->device, - info->status, info->mask); - - __aer_print_error(dev, info); - - if (info->tlp_header_valid) - __print_tlp_header(dev, &info->tlp); - -out: - if (info->id && info->error_dev_num > 1 && info->id == id) - pci_err(dev, " Error of this Agent is reported first\n"); - - trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask), - info->severity, info->tlp_header_valid, &info->tlp); -} - -static void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) -{ - u8 bus = info->id >> 8; - u8 devfn = info->id & 0xff; - - pci_info(dev, "AER: %s%s error received: %04x:%02x:%02x.%d\n", - info->multi_error_valid ? "Multiple " : "", - aer_error_severity_string[info->severity], - pci_domain_nr(dev->bus), bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); -} - -#ifdef CONFIG_ACPI_APEI_PCIEAER -int cper_severity_to_aer(int cper_severity) -{ - switch (cper_severity) { - case CPER_SEV_RECOVERABLE: - return AER_NONFATAL; - case CPER_SEV_FATAL: - return AER_FATAL; - default: - return AER_CORRECTABLE; - } -} -EXPORT_SYMBOL_GPL(cper_severity_to_aer); - -void cper_print_aer(struct pci_dev *dev, int aer_severity, - struct aer_capability_regs *aer) -{ - int layer, agent, tlp_header_valid = 0; - u32 status, mask; - struct aer_err_info info; - - if (aer_severity == AER_CORRECTABLE) { - status = aer->cor_status; - mask = aer->cor_mask; - } else { - status = aer->uncor_status; - mask = aer->uncor_mask; - tlp_header_valid = status & AER_LOG_TLP_MASKS; - } - - layer = AER_GET_LAYER_ERROR(aer_severity, status); - agent = AER_GET_AGENT(aer_severity, status); - - memset(&info, 0, sizeof(info)); - info.severity = aer_severity; - info.status = status; - info.mask = mask; - info.first_error = PCI_ERR_CAP_FEP(aer->cap_control); - - pci_err(dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask); - __aer_print_error(dev, &info); - pci_err(dev, "aer_layer=%s, aer_agent=%s\n", - aer_error_layer[layer], aer_agent_string[agent]); - - if (aer_severity != AER_CORRECTABLE) - pci_err(dev, "aer_uncor_severity: 0x%08x\n", - aer->uncor_severity); - - if (tlp_header_valid) - __print_tlp_header(dev, &aer->header_log); - - trace_aer_event(dev_name(&dev->dev), (status & ~mask), - aer_severity, tlp_header_valid, &aer->header_log); -} -#endif - -/** - * add_error_device - list device to be handled - * @e_info: pointer to error info - * @dev: pointer to pci_dev to be added - */ -static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) -{ - if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { - e_info->dev[e_info->error_dev_num] = dev; - e_info->error_dev_num++; - return 0; - } - return -ENOSPC; -} - -/** - * is_error_source - check whether the device is source of reported error - * @dev: pointer to pci_dev to be checked - * @e_info: pointer to reported error info - */ -static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) -{ - int pos; - u32 status, mask; - u16 reg16; - - /* - * When bus id is equal to 0, it might be a bad id - * reported by root port. - */ - if ((PCI_BUS_NUM(e_info->id) != 0) && - !(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) { - /* Device ID match? */ - if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) - return true; - - /* Continue id comparing if there is no multiple error */ - if (!e_info->multi_error_valid) - return false; - } - - /* - * When either - * 1) bus id is equal to 0. Some ports might lose the bus - * id of error source id; - * 2) bus flag PCI_BUS_FLAGS_NO_AERSID is set - * 3) There are multiple errors and prior ID comparing fails; - * We check AER status registers to find possible reporter. - */ - if (atomic_read(&dev->enable_cnt) == 0) - return false; - - /* Check if AER is enabled */ - pcie_capability_read_word(dev, PCI_EXP_DEVCTL, ®16); - if (!(reg16 & PCI_EXP_AER_FLAGS)) - return false; - - pos = dev->aer_cap; - if (!pos) - return false; - - /* Check if error is recorded */ - if (e_info->severity == AER_CORRECTABLE) { - pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); - pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask); - } else { - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); - } - if (status & ~mask) - return true; - - return false; -} - -static int find_device_iter(struct pci_dev *dev, void *data) -{ - struct aer_err_info *e_info = (struct aer_err_info *)data; - - if (is_error_source(dev, e_info)) { - /* List this device */ - if (add_error_device(e_info, dev)) { - /* We cannot handle more... Stop iteration */ - /* TODO: Should print error message here? */ - return 1; - } - - /* If there is only a single error, stop iteration */ - if (!e_info->multi_error_valid) - return 1; - } - return 0; -} - -/** - * find_source_device - search through device hierarchy for source device - * @parent: pointer to Root Port pci_dev data structure - * @e_info: including detailed error information such like id - * - * Return true if found. - * - * Invoked by DPC when error is detected at the Root Port. - * Caller of this function must set id, severity, and multi_error_valid of - * struct aer_err_info pointed by @e_info properly. This function must fill - * e_info->error_dev_num and e_info->dev[], based on the given information. - */ -static bool find_source_device(struct pci_dev *parent, - struct aer_err_info *e_info) -{ - struct pci_dev *dev = parent; - int result; - - /* Must reset in this function */ - e_info->error_dev_num = 0; - - /* Is Root Port an agent that sends error message? */ - result = find_device_iter(dev, e_info); - if (result) - return true; - - pci_walk_bus(parent->subordinate, find_device_iter, e_info); - - if (!e_info->error_dev_num) { - pci_printk(KERN_DEBUG, parent, "can't find device of ID%04x\n", - e_info->id); - return false; - } - return true; -} - -/** - * handle_error_source - handle logging error into an event log - * @dev: pointer to pci_dev data structure of error source device - * @info: comprehensive error information - * - * Invoked when an error being detected by Root Port. - */ -static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info) -{ - int pos; - - if (info->severity == AER_CORRECTABLE) { - /* - * Correctable error does not need software intervention. - * No need to go through error recovery process. - */ - pos = dev->aer_cap; - if (pos) - pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, - info->status); - } else if (info->severity == AER_NONFATAL) - pcie_do_nonfatal_recovery(dev); - else if (info->severity == AER_FATAL) - pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER); -} - -#ifdef CONFIG_ACPI_APEI_PCIEAER - -#define AER_RECOVER_RING_ORDER 4 -#define AER_RECOVER_RING_SIZE (1 << AER_RECOVER_RING_ORDER) - -struct aer_recover_entry { - u8 bus; - u8 devfn; - u16 domain; - int severity; - struct aer_capability_regs *regs; -}; - -static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry, - AER_RECOVER_RING_SIZE); - -static void aer_recover_work_func(struct work_struct *work) -{ - struct aer_recover_entry entry; - struct pci_dev *pdev; - - while (kfifo_get(&aer_recover_ring, &entry)) { - pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus, - entry.devfn); - if (!pdev) { - pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n", - entry.domain, entry.bus, - PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn)); - continue; - } - cper_print_aer(pdev, entry.severity, entry.regs); - if (entry.severity == AER_NONFATAL) - pcie_do_nonfatal_recovery(pdev); - else if (entry.severity == AER_FATAL) - pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_AER); - pci_dev_put(pdev); - } -} - -/* - * Mutual exclusion for writers of aer_recover_ring, reader side don't - * need lock, because there is only one reader and lock is not needed - * between reader and writer. - */ -static DEFINE_SPINLOCK(aer_recover_ring_lock); -static DECLARE_WORK(aer_recover_work, aer_recover_work_func); - -void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, - int severity, struct aer_capability_regs *aer_regs) -{ - unsigned long flags; - struct aer_recover_entry entry = { - .bus = bus, - .devfn = devfn, - .domain = domain, - .severity = severity, - .regs = aer_regs, - }; - - spin_lock_irqsave(&aer_recover_ring_lock, flags); - if (kfifo_put(&aer_recover_ring, entry)) - schedule_work(&aer_recover_work); - else - pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n", - domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); - spin_unlock_irqrestore(&aer_recover_ring_lock, flags); -} -EXPORT_SYMBOL_GPL(aer_recover_queue); -#endif - -/** - * get_device_error_info - read error status from dev and store it to info - * @dev: pointer to the device expected to have a error record - * @info: pointer to structure to store the error record - * - * Return 1 on success, 0 on error. - * - * Note that @info is reused among all error devices. Clear fields properly. - */ -static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) -{ - int pos, temp; - - /* Must reset in this function */ - info->status = 0; - info->tlp_header_valid = 0; - - pos = dev->aer_cap; - - /* The device might not support AER */ - if (!pos) - return 0; - - if (info->severity == AER_CORRECTABLE) { - pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, - &info->status); - pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, - &info->mask); - if (!(info->status & ~info->mask)) - return 0; - } else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || - info->severity == AER_NONFATAL) { - - /* Link is still healthy for IO reads */ - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, - &info->status); - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, - &info->mask); - if (!(info->status & ~info->mask)) - return 0; - - /* Get First Error Pointer */ - pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp); - info->first_error = PCI_ERR_CAP_FEP(temp); - - if (info->status & AER_LOG_TLP_MASKS) { - info->tlp_header_valid = 1; - pci_read_config_dword(dev, - pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); - pci_read_config_dword(dev, - pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); - pci_read_config_dword(dev, - pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); - pci_read_config_dword(dev, - pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); - } - } - - return 1; -} - -static inline void aer_process_err_devices(struct aer_err_info *e_info) -{ - int i; - - /* Report all before handle them, not to lost records by reset etc. */ - for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { - if (get_device_error_info(e_info->dev[i], e_info)) - aer_print_error(e_info->dev[i], e_info); - } - for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { - if (get_device_error_info(e_info->dev[i], e_info)) - handle_error_source(e_info->dev[i], e_info); - } -} - -/** - * aer_isr_one_error - consume an error detected by root port - * @rpc: pointer to the root port which holds an error - * @e_src: pointer to an error source - */ -static void aer_isr_one_error(struct aer_rpc *rpc, - struct aer_err_source *e_src) -{ - struct pci_dev *pdev = rpc->rpd; - struct aer_err_info *e_info = &rpc->e_info; - - /* - * There is a possibility that both correctable error and - * uncorrectable error being logged. Report correctable error first. - */ - if (e_src->status & PCI_ERR_ROOT_COR_RCV) { - e_info->id = ERR_COR_ID(e_src->id); - e_info->severity = AER_CORRECTABLE; - - if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV) - e_info->multi_error_valid = 1; - else - e_info->multi_error_valid = 0; - aer_print_port_info(pdev, e_info); - - if (find_source_device(pdev, e_info)) - aer_process_err_devices(e_info); - } - - if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) { - e_info->id = ERR_UNCOR_ID(e_src->id); - - if (e_src->status & PCI_ERR_ROOT_FATAL_RCV) - e_info->severity = AER_FATAL; - else - e_info->severity = AER_NONFATAL; - - if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV) - e_info->multi_error_valid = 1; - else - e_info->multi_error_valid = 0; - - aer_print_port_info(pdev, e_info); - - if (find_source_device(pdev, e_info)) - aer_process_err_devices(e_info); - } -} - -/** - * get_e_source - retrieve an error source - * @rpc: pointer to the root port which holds an error - * @e_src: pointer to store retrieved error source - * - * Return 1 if an error source is retrieved, otherwise 0. - * - * Invoked by DPC handler to consume an error. - */ -static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src) -{ - unsigned long flags; - - /* Lock access to Root error producer/consumer index */ - spin_lock_irqsave(&rpc->e_lock, flags); - if (rpc->prod_idx == rpc->cons_idx) { - spin_unlock_irqrestore(&rpc->e_lock, flags); - return 0; - } - - *e_src = rpc->e_sources[rpc->cons_idx]; - rpc->cons_idx++; - if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) - rpc->cons_idx = 0; - spin_unlock_irqrestore(&rpc->e_lock, flags); - - return 1; -} - -/** - * aer_isr - consume errors detected by root port - * @work: definition of this work item - * - * Invoked, as DPC, when root port records new detected error - */ -static void aer_isr(struct work_struct *work) -{ - struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); - struct aer_err_source uninitialized_var(e_src); - - mutex_lock(&rpc->rpc_mutex); - while (get_e_source(rpc, &e_src)) - aer_isr_one_error(rpc, &e_src); - mutex_unlock(&rpc->rpc_mutex); -} - -/** - * aer_irq - Root Port's ISR - * @irq: IRQ assigned to Root Port - * @context: pointer to Root Port data structure - * - * Invoked when Root Port detects AER messages. - */ -irqreturn_t aer_irq(int irq, void *context) -{ - unsigned int status, id; - struct pcie_device *pdev = (struct pcie_device *)context; - struct aer_rpc *rpc = get_service_data(pdev); - int next_prod_idx; - unsigned long flags; - int pos; - - pos = pdev->port->aer_cap; - /* - * Must lock access to Root Error Status Reg, Root Error ID Reg, - * and Root error producer/consumer index - */ - spin_lock_irqsave(&rpc->e_lock, flags); - - /* Read error status */ - pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); - if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) { - spin_unlock_irqrestore(&rpc->e_lock, flags); - return IRQ_NONE; - } - - /* Read error source and clear error status */ - pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id); - pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); - - /* Store error source for later DPC handler */ - next_prod_idx = rpc->prod_idx + 1; - if (next_prod_idx == AER_ERROR_SOURCES_MAX) - next_prod_idx = 0; - if (next_prod_idx == rpc->cons_idx) { - /* - * Error Storm Condition - possibly the same error occurred. - * Drop the error. - */ - spin_unlock_irqrestore(&rpc->e_lock, flags); - return IRQ_HANDLED; - } - rpc->e_sources[rpc->prod_idx].status = status; - rpc->e_sources[rpc->prod_idx].id = id; - rpc->prod_idx = next_prod_idx; - spin_unlock_irqrestore(&rpc->e_lock, flags); - - /* Invoke DPC handler */ - schedule_work(&rpc->dpc_handler); - - return IRQ_HANDLED; -} -EXPORT_SYMBOL_GPL(aer_irq); - -static int set_device_error_reporting(struct pci_dev *dev, void *data) -{ - bool enable = *((bool *)data); - int type = pci_pcie_type(dev); - - if ((type == PCI_EXP_TYPE_ROOT_PORT) || - (type == PCI_EXP_TYPE_UPSTREAM) || - (type == PCI_EXP_TYPE_DOWNSTREAM)) { - if (enable) - pci_enable_pcie_error_reporting(dev); - else - pci_disable_pcie_error_reporting(dev); - } - - if (enable) - pcie_set_ecrc_checking(dev); - - return 0; -} - -/** - * set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports. - * @dev: pointer to root port's pci_dev data structure - * @enable: true = enable error reporting, false = disable error reporting. - */ -static void set_downstream_devices_error_reporting(struct pci_dev *dev, - bool enable) -{ - set_device_error_reporting(dev, &enable); - - if (!dev->subordinate) - return; - pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable); -} - -/** - * aer_enable_rootport - enable Root Port's interrupts when receiving messages - * @rpc: pointer to a Root Port data structure - * - * Invoked when PCIe bus loads AER service driver. - */ -static void aer_enable_rootport(struct aer_rpc *rpc) -{ - struct pci_dev *pdev = rpc->rpd; - int aer_pos; - u16 reg16; - u32 reg32; - - /* Clear PCIe Capability's Device Status */ - pcie_capability_read_word(pdev, PCI_EXP_DEVSTA, ®16); - pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, reg16); - - /* Disable system error generation in response to error messages */ - pcie_capability_clear_word(pdev, PCI_EXP_RTCTL, - SYSTEM_ERROR_INTR_ON_MESG_MASK); - - aer_pos = pdev->aer_cap; - /* Clear error status */ - pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); - pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); - pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); - pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); - pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); - pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); - - /* - * Enable error reporting for the root port device and downstream port - * devices. - */ - set_downstream_devices_error_reporting(pdev, true); - - /* Enable Root Port's interrupt in response to error messages */ - pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ®32); - reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; - pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32); -} - -/** - * aer_disable_rootport - disable Root Port's interrupts when receiving messages - * @rpc: pointer to a Root Port data structure - * - * Invoked when PCIe bus unloads AER service driver. - */ -static void aer_disable_rootport(struct aer_rpc *rpc) -{ - struct pci_dev *pdev = rpc->rpd; - u32 reg32; - int pos; - - /* - * Disable error reporting for the root port device and downstream port - * devices. - */ - set_downstream_devices_error_reporting(pdev, false); - - pos = pdev->aer_cap; - /* Disable Root's interrupt in response to error messages */ - pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32); - reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; - pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32); - - /* Clear Root's error status reg */ - pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); - pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); -} - -/** - * aer_alloc_rpc - allocate Root Port data structure - * @dev: pointer to the pcie_dev data structure - * - * Invoked when Root Port's AER service is loaded. - */ -static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) -{ - struct aer_rpc *rpc; - - rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL); - if (!rpc) - return NULL; - - /* Initialize Root lock access, e_lock, to Root Error Status Reg */ - spin_lock_init(&rpc->e_lock); - - rpc->rpd = dev->port; - INIT_WORK(&rpc->dpc_handler, aer_isr); - mutex_init(&rpc->rpc_mutex); - - /* Use PCIe bus function to store rpc into PCIe device */ - set_service_data(dev, rpc); - - return rpc; -} - -/** - * aer_remove - clean up resources - * @dev: pointer to the pcie_dev data structure - * - * Invoked when PCI Express bus unloads or AER probe fails. - */ -static void aer_remove(struct pcie_device *dev) -{ - struct aer_rpc *rpc = get_service_data(dev); - - if (rpc) { - /* If register interrupt service, it must be free. */ - if (rpc->isr) - free_irq(dev->irq, dev); - - flush_work(&rpc->dpc_handler); - aer_disable_rootport(rpc); - kfree(rpc); - set_service_data(dev, NULL); - } -} - -/** - * aer_probe - initialize resources - * @dev: pointer to the pcie_dev data structure - * - * Invoked when PCI Express bus loads AER service driver. - */ -static int aer_probe(struct pcie_device *dev) -{ - int status; - struct aer_rpc *rpc; - struct device *device = &dev->port->dev; - - /* Alloc rpc data structure */ - rpc = aer_alloc_rpc(dev); - if (!rpc) { - dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n"); - aer_remove(dev); - return -ENOMEM; - } - - /* Request IRQ ISR */ - status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev); - if (status) { - dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n", - dev->irq); - aer_remove(dev); - return status; - } - - rpc->isr = 1; - - aer_enable_rootport(rpc); - dev_info(device, "AER enabled with IRQ %d\n", dev->irq); - return 0; -} - -/** - * aer_root_reset - reset link on Root Port - * @dev: pointer to Root Port's pci_dev data structure - * - * Invoked by Port Bus driver when performing link reset at Root Port. - */ -static pci_ers_result_t aer_root_reset(struct pci_dev *dev) -{ - u32 reg32; - int pos; - - pos = dev->aer_cap; - - /* Disable Root's interrupt in response to error messages */ - pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); - reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; - pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); - - pci_reset_bridge_secondary_bus(dev); - pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n"); - - /* Clear Root Error Status */ - pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); - pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32); - - /* Enable Root Port's interrupt in response to error messages */ - pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); - reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; - pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); - - return PCI_ERS_RESULT_RECOVERED; -} - -/** - * aer_error_resume - clean up corresponding error status bits - * @dev: pointer to Root Port's pci_dev data structure - * - * Invoked by Port Bus driver during nonfatal recovery. - */ -static void aer_error_resume(struct pci_dev *dev) -{ - int pos; - u32 status, mask; - u16 reg16; - - /* Clean up Root device status */ - pcie_capability_read_word(dev, PCI_EXP_DEVSTA, ®16); - pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16); - - /* Clean AER Root Error Status */ - pos = dev->aer_cap; - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); - status &= ~mask; /* Clear corresponding nonfatal bits */ - pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); -} - -static struct pcie_port_service_driver aerdriver = { - .name = "aer", - .port_type = PCI_EXP_TYPE_ROOT_PORT, - .service = PCIE_PORT_SERVICE_AER, - - .probe = aer_probe, - .remove = aer_remove, - .error_resume = aer_error_resume, - .reset_link = aer_root_reset, -}; - -/** - * aer_service_init - register AER root service driver - * - * Invoked when AER root service driver is loaded. - */ -static int __init aer_service_init(void) -{ - if (!pci_aer_available() || aer_acpi_firmware_first()) - return -ENXIO; - return pcie_port_service_register(&aerdriver); -} -device_initcall(aer_service_init); diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c new file mode 100644 index 000000000000..0eb24346cad3 --- /dev/null +++ b/drivers/pci/pcie/aer_inject.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe AER software error injection support. + * + * Debuging PCIe AER code is quite difficult because it is hard to + * trigger various real hardware errors. Software based error + * injection can fake almost all kinds of errors with the help of a + * user space helper tool aer-inject, which can be gotten from: + * http://www.kernel.org/pub/linux/utils/pci/aer-inject/ + * + * Copyright 2009 Intel Corporation. + * Huang Ying + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portdrv.h" + +/* Override the existing corrected and uncorrected error masks */ +static bool aer_mask_override; +module_param(aer_mask_override, bool, 0); + +struct aer_error_inj { + u8 bus; + u8 dev; + u8 fn; + u32 uncor_status; + u32 cor_status; + u32 header_log0; + u32 header_log1; + u32 header_log2; + u32 header_log3; + u32 domain; +}; + +struct aer_error { + struct list_head list; + u32 domain; + unsigned int bus; + unsigned int devfn; + int pos_cap_err; + + u32 uncor_status; + u32 cor_status; + u32 header_log0; + u32 header_log1; + u32 header_log2; + u32 header_log3; + u32 root_status; + u32 source_id; +}; + +struct pci_bus_ops { + struct list_head list; + struct pci_bus *bus; + struct pci_ops *ops; +}; + +static LIST_HEAD(einjected); + +static LIST_HEAD(pci_bus_ops_list); + +/* Protect einjected and pci_bus_ops_list */ +static DEFINE_SPINLOCK(inject_lock); + +static void aer_error_init(struct aer_error *err, u32 domain, + unsigned int bus, unsigned int devfn, + int pos_cap_err) +{ + INIT_LIST_HEAD(&err->list); + err->domain = domain; + err->bus = bus; + err->devfn = devfn; + err->pos_cap_err = pos_cap_err; +} + +/* inject_lock must be held before calling */ +static struct aer_error *__find_aer_error(u32 domain, unsigned int bus, + unsigned int devfn) +{ + struct aer_error *err; + + list_for_each_entry(err, &einjected, list) { + if (domain == err->domain && + bus == err->bus && + devfn == err->devfn) + return err; + } + return NULL; +} + +/* inject_lock must be held before calling */ +static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev) +{ + int domain = pci_domain_nr(dev->bus); + if (domain < 0) + return NULL; + return __find_aer_error(domain, dev->bus->number, dev->devfn); +} + +/* inject_lock must be held before calling */ +static struct pci_ops *__find_pci_bus_ops(struct pci_bus *bus) +{ + struct pci_bus_ops *bus_ops; + + list_for_each_entry(bus_ops, &pci_bus_ops_list, list) { + if (bus_ops->bus == bus) + return bus_ops->ops; + } + return NULL; +} + +static struct pci_bus_ops *pci_bus_ops_pop(void) +{ + unsigned long flags; + struct pci_bus_ops *bus_ops; + + spin_lock_irqsave(&inject_lock, flags); + bus_ops = list_first_entry_or_null(&pci_bus_ops_list, + struct pci_bus_ops, list); + if (bus_ops) + list_del(&bus_ops->list); + spin_unlock_irqrestore(&inject_lock, flags); + return bus_ops; +} + +static u32 *find_pci_config_dword(struct aer_error *err, int where, + int *prw1cs) +{ + int rw1cs = 0; + u32 *target = NULL; + + if (err->pos_cap_err == -1) + return NULL; + + switch (where - err->pos_cap_err) { + case PCI_ERR_UNCOR_STATUS: + target = &err->uncor_status; + rw1cs = 1; + break; + case PCI_ERR_COR_STATUS: + target = &err->cor_status; + rw1cs = 1; + break; + case PCI_ERR_HEADER_LOG: + target = &err->header_log0; + break; + case PCI_ERR_HEADER_LOG+4: + target = &err->header_log1; + break; + case PCI_ERR_HEADER_LOG+8: + target = &err->header_log2; + break; + case PCI_ERR_HEADER_LOG+12: + target = &err->header_log3; + break; + case PCI_ERR_ROOT_STATUS: + target = &err->root_status; + rw1cs = 1; + break; + case PCI_ERR_ROOT_ERR_SRC: + target = &err->source_id; + break; + } + if (prw1cs) + *prw1cs = rw1cs; + return target; +} + +static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + u32 *sim; + struct aer_error *err; + unsigned long flags; + struct pci_ops *ops; + struct pci_ops *my_ops; + int domain; + int rv; + + spin_lock_irqsave(&inject_lock, flags); + if (size != sizeof(u32)) + goto out; + domain = pci_domain_nr(bus); + if (domain < 0) + goto out; + err = __find_aer_error(domain, bus->number, devfn); + if (!err) + goto out; + + sim = find_pci_config_dword(err, where, NULL); + if (sim) { + *val = *sim; + spin_unlock_irqrestore(&inject_lock, flags); + return 0; + } +out: + ops = __find_pci_bus_ops(bus); + /* + * pci_lock must already be held, so we can directly + * manipulate bus->ops. Many config access functions, + * including pci_generic_config_read() require the original + * bus->ops be installed to function, so temporarily put them + * back. + */ + my_ops = bus->ops; + bus->ops = ops; + rv = ops->read(bus, devfn, where, size, val); + bus->ops = my_ops; + spin_unlock_irqrestore(&inject_lock, flags); + return rv; +} + +static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 *sim; + struct aer_error *err; + unsigned long flags; + int rw1cs; + struct pci_ops *ops; + struct pci_ops *my_ops; + int domain; + int rv; + + spin_lock_irqsave(&inject_lock, flags); + if (size != sizeof(u32)) + goto out; + domain = pci_domain_nr(bus); + if (domain < 0) + goto out; + err = __find_aer_error(domain, bus->number, devfn); + if (!err) + goto out; + + sim = find_pci_config_dword(err, where, &rw1cs); + if (sim) { + if (rw1cs) + *sim ^= val; + else + *sim = val; + spin_unlock_irqrestore(&inject_lock, flags); + return 0; + } +out: + ops = __find_pci_bus_ops(bus); + /* + * pci_lock must already be held, so we can directly + * manipulate bus->ops. Many config access functions, + * including pci_generic_config_write() require the original + * bus->ops be installed to function, so temporarily put them + * back. + */ + my_ops = bus->ops; + bus->ops = ops; + rv = ops->write(bus, devfn, where, size, val); + bus->ops = my_ops; + spin_unlock_irqrestore(&inject_lock, flags); + return rv; +} + +static struct pci_ops aer_inj_pci_ops = { + .read = aer_inj_read_config, + .write = aer_inj_write_config, +}; + +static void pci_bus_ops_init(struct pci_bus_ops *bus_ops, + struct pci_bus *bus, + struct pci_ops *ops) +{ + INIT_LIST_HEAD(&bus_ops->list); + bus_ops->bus = bus; + bus_ops->ops = ops; +} + +static int pci_bus_set_aer_ops(struct pci_bus *bus) +{ + struct pci_ops *ops; + struct pci_bus_ops *bus_ops; + unsigned long flags; + + bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + ops = pci_bus_set_ops(bus, &aer_inj_pci_ops); + spin_lock_irqsave(&inject_lock, flags); + if (ops == &aer_inj_pci_ops) + goto out; + pci_bus_ops_init(bus_ops, bus, ops); + list_add(&bus_ops->list, &pci_bus_ops_list); + bus_ops = NULL; +out: + spin_unlock_irqrestore(&inject_lock, flags); + kfree(bus_ops); + return 0; +} + +static int find_aer_device_iter(struct device *device, void *data) +{ + struct pcie_device **result = data; + struct pcie_device *pcie_dev; + + if (device->bus == &pcie_port_bus_type) { + pcie_dev = to_pcie_device(device); + if (pcie_dev->service & PCIE_PORT_SERVICE_AER) { + *result = pcie_dev; + return 1; + } + } + return 0; +} + +static int find_aer_device(struct pci_dev *dev, struct pcie_device **result) +{ + return device_for_each_child(&dev->dev, result, find_aer_device_iter); +} + +static int aer_inject(struct aer_error_inj *einj) +{ + struct aer_error *err, *rperr; + struct aer_error *err_alloc = NULL, *rperr_alloc = NULL; + struct pci_dev *dev, *rpdev; + struct pcie_device *edev; + unsigned long flags; + unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn); + int pos_cap_err, rp_pos_cap_err; + u32 sever, cor_mask, uncor_mask, cor_mask_orig = 0, uncor_mask_orig = 0; + int ret = 0; + + dev = pci_get_domain_bus_and_slot(einj->domain, einj->bus, devfn); + if (!dev) + return -ENODEV; + rpdev = pcie_find_root_port(dev); + if (!rpdev) { + pci_err(dev, "aer_inject: Root port not found\n"); + ret = -ENODEV; + goto out_put; + } + + pos_cap_err = dev->aer_cap; + if (!pos_cap_err) { + pci_err(dev, "aer_inject: Device doesn't support AER\n"); + ret = -EPROTONOSUPPORT; + goto out_put; + } + pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever); + pci_read_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, &cor_mask); + pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, + &uncor_mask); + + rp_pos_cap_err = rpdev->aer_cap; + if (!rp_pos_cap_err) { + pci_err(rpdev, "aer_inject: Root port doesn't support AER\n"); + ret = -EPROTONOSUPPORT; + goto out_put; + } + + err_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL); + if (!err_alloc) { + ret = -ENOMEM; + goto out_put; + } + rperr_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL); + if (!rperr_alloc) { + ret = -ENOMEM; + goto out_put; + } + + if (aer_mask_override) { + cor_mask_orig = cor_mask; + cor_mask &= !(einj->cor_status); + pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, + cor_mask); + + uncor_mask_orig = uncor_mask; + uncor_mask &= !(einj->uncor_status); + pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, + uncor_mask); + } + + spin_lock_irqsave(&inject_lock, flags); + + err = __find_aer_error_by_dev(dev); + if (!err) { + err = err_alloc; + err_alloc = NULL; + aer_error_init(err, einj->domain, einj->bus, devfn, + pos_cap_err); + list_add(&err->list, &einjected); + } + err->uncor_status |= einj->uncor_status; + err->cor_status |= einj->cor_status; + err->header_log0 = einj->header_log0; + err->header_log1 = einj->header_log1; + err->header_log2 = einj->header_log2; + err->header_log3 = einj->header_log3; + + if (!aer_mask_override && einj->cor_status && + !(einj->cor_status & ~cor_mask)) { + ret = -EINVAL; + pci_warn(dev, "aer_inject: The correctable error(s) is masked by device\n"); + spin_unlock_irqrestore(&inject_lock, flags); + goto out_put; + } + if (!aer_mask_override && einj->uncor_status && + !(einj->uncor_status & ~uncor_mask)) { + ret = -EINVAL; + pci_warn(dev, "aer_inject: The uncorrectable error(s) is masked by device\n"); + spin_unlock_irqrestore(&inject_lock, flags); + goto out_put; + } + + rperr = __find_aer_error_by_dev(rpdev); + if (!rperr) { + rperr = rperr_alloc; + rperr_alloc = NULL; + aer_error_init(rperr, pci_domain_nr(rpdev->bus), + rpdev->bus->number, rpdev->devfn, + rp_pos_cap_err); + list_add(&rperr->list, &einjected); + } + if (einj->cor_status) { + if (rperr->root_status & PCI_ERR_ROOT_COR_RCV) + rperr->root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; + else + rperr->root_status |= PCI_ERR_ROOT_COR_RCV; + rperr->source_id &= 0xffff0000; + rperr->source_id |= (einj->bus << 8) | devfn; + } + if (einj->uncor_status) { + if (rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV) + rperr->root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; + if (sever & einj->uncor_status) { + rperr->root_status |= PCI_ERR_ROOT_FATAL_RCV; + if (!(rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV)) + rperr->root_status |= PCI_ERR_ROOT_FIRST_FATAL; + } else + rperr->root_status |= PCI_ERR_ROOT_NONFATAL_RCV; + rperr->root_status |= PCI_ERR_ROOT_UNCOR_RCV; + rperr->source_id &= 0x0000ffff; + rperr->source_id |= ((einj->bus << 8) | devfn) << 16; + } + spin_unlock_irqrestore(&inject_lock, flags); + + if (aer_mask_override) { + pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, + cor_mask_orig); + pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, + uncor_mask_orig); + } + + ret = pci_bus_set_aer_ops(dev->bus); + if (ret) + goto out_put; + ret = pci_bus_set_aer_ops(rpdev->bus); + if (ret) + goto out_put; + + if (find_aer_device(rpdev, &edev)) { + if (!get_service_data(edev)) { + dev_warn(&edev->device, + "aer_inject: AER service is not initialized\n"); + ret = -EPROTONOSUPPORT; + goto out_put; + } + dev_info(&edev->device, + "aer_inject: Injecting errors %08x/%08x into device %s\n", + einj->cor_status, einj->uncor_status, pci_name(dev)); + aer_irq(-1, edev); + } else { + pci_err(rpdev, "aer_inject: AER device not found\n"); + ret = -ENODEV; + } +out_put: + kfree(err_alloc); + kfree(rperr_alloc); + pci_dev_put(dev); + return ret; +} + +static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf, + size_t usize, loff_t *off) +{ + struct aer_error_inj einj; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (usize < offsetof(struct aer_error_inj, domain) || + usize > sizeof(einj)) + return -EINVAL; + + memset(&einj, 0, sizeof(einj)); + if (copy_from_user(&einj, ubuf, usize)) + return -EFAULT; + + ret = aer_inject(&einj); + return ret ? ret : usize; +} + +static const struct file_operations aer_inject_fops = { + .write = aer_inject_write, + .owner = THIS_MODULE, + .llseek = noop_llseek, +}; + +static struct miscdevice aer_inject_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "aer_inject", + .fops = &aer_inject_fops, +}; + +static int __init aer_inject_init(void) +{ + return misc_register(&aer_inject_device); +} + +static void __exit aer_inject_exit(void) +{ + struct aer_error *err, *err_next; + unsigned long flags; + struct pci_bus_ops *bus_ops; + + misc_deregister(&aer_inject_device); + + while ((bus_ops = pci_bus_ops_pop())) { + pci_bus_set_ops(bus_ops->bus, bus_ops->ops); + kfree(bus_ops); + } + + spin_lock_irqsave(&inject_lock, flags); + list_for_each_entry_safe(err, err_next, &einjected, list) { + list_del(&err->list); + kfree(err); + } + spin_unlock_irqrestore(&inject_lock, flags); +} + +module_init(aer_inject_init); +module_exit(aer_inject_exit); + +MODULE_DESCRIPTION("PCIe AER software error injector"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 0b15f1e38f639a322213ebdc8b752300138edd0b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Jun 2018 08:48:55 -0500 Subject: PCI/AER: Use "PCI Express" consistently in Kconfig text Use "PCI Express" consistently in Kconfig text. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Keith Busch --- drivers/pci/pcie/Kconfig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 4a8e26a2b012..0a1e9d379bc5 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -24,7 +24,7 @@ config HOTPLUG_PCI_PCIE When in doubt, say N. config PCIEAER - bool "Root Port Advanced Error Reporting support" + bool "PCI Express Advanced Error Reporting support" depends on PCIEPORTBUS select RAS default y @@ -34,15 +34,15 @@ config PCIEAER Port will be handled by PCI Express AER driver. config PCIEAER_INJECT - tristate "PCIe AER error injector support" + tristate "PCI Express error injection support" depends on PCIEAER default n help This enables PCI Express Root Port Advanced Error Reporting (AER) software error injector. - Debugging PCIe AER code is quite difficult because it is hard - to trigger various real hardware errors. Software based + Debugging AER code is quite difficult because it is hard + to trigger various real hardware errors. Software-based error injection can fake almost all kinds of errors with the help of a user space helper tool aer-inject, which can be gotten from: @@ -127,7 +127,7 @@ config PCIE_PME depends on PCIEPORTBUS && PM config PCIE_DPC - bool "PCIe Downstream Port Containment support" + bool "PCI Express Downstream Port Containment support" depends on PCIEPORTBUS && PCIEAER default n help @@ -138,7 +138,7 @@ config PCIE_DPC it is safe to answer N. config PCIE_PTM - bool "PCIe Precision Time Measurement support" + bool "PCI Express Precision Time Measurement support" default n depends on PCIEPORTBUS help -- cgit v1.2.3 From 6396bb221514d2876fd6dc0aa2a1f240d99b37bb Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 12 Jun 2018 14:03:40 -0700 Subject: treewide: kzalloc() -> kcalloc() The kzalloc() function has a 2-factor argument form, kcalloc(). This patch replaces cases of: kzalloc(a * b, gfp) with: kcalloc(a * b, gfp) as well as handling cases of: kzalloc(a * b * c, gfp) with: kzalloc(array3_size(a, b, c), gfp) as it's slightly less ugly than: kzalloc_array(array_size(a, b), c, gfp) This does, however, attempt to ignore constant size factors like: kzalloc(4 * 1024, gfp) though any constants defined via macros get caught up in the conversion. Any factors with a sizeof() of "unsigned char", "char", and "u8" were dropped, since they're redundant. The Coccinelle script used for this was: // Fix redundant parens around sizeof(). @@ type TYPE; expression THING, E; @@ ( kzalloc( - (sizeof(TYPE)) * E + sizeof(TYPE) * E , ...) | kzalloc( - (sizeof(THING)) * E + sizeof(THING) * E , ...) ) // Drop single-byte sizes and redundant parens. @@ expression COUNT; typedef u8; typedef __u8; @@ ( kzalloc( - sizeof(u8) * (COUNT) + COUNT , ...) | kzalloc( - sizeof(__u8) * (COUNT) + COUNT , ...) | kzalloc( - sizeof(char) * (COUNT) + COUNT , ...) | kzalloc( - sizeof(unsigned char) * (COUNT) + COUNT , ...) | kzalloc( - sizeof(u8) * COUNT + COUNT , ...) | kzalloc( - sizeof(__u8) * COUNT + COUNT , ...) | kzalloc( - sizeof(char) * COUNT + COUNT , ...) | kzalloc( - sizeof(unsigned char) * COUNT + COUNT , ...) ) // 2-factor product with sizeof(type/expression) and identifier or constant. @@ type TYPE; expression THING; identifier COUNT_ID; constant COUNT_CONST; @@ ( - kzalloc + kcalloc ( - sizeof(TYPE) * (COUNT_ID) + COUNT_ID, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * COUNT_ID + COUNT_ID, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * (COUNT_CONST) + COUNT_CONST, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * COUNT_CONST + COUNT_CONST, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * (COUNT_ID) + COUNT_ID, sizeof(THING) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * COUNT_ID + COUNT_ID, sizeof(THING) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * (COUNT_CONST) + COUNT_CONST, sizeof(THING) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * COUNT_CONST + COUNT_CONST, sizeof(THING) , ...) ) // 2-factor product, only identifiers. @@ identifier SIZE, COUNT; @@ - kzalloc + kcalloc ( - SIZE * COUNT + COUNT, SIZE , ...) // 3-factor product with 1 sizeof(type) or sizeof(expression), with // redundant parens removed. @@ expression THING; identifier STRIDE, COUNT; type TYPE; @@ ( kzalloc( - sizeof(TYPE) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kzalloc( - sizeof(TYPE) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kzalloc( - sizeof(TYPE) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kzalloc( - sizeof(TYPE) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kzalloc( - sizeof(THING) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kzalloc( - sizeof(THING) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kzalloc( - sizeof(THING) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kzalloc( - sizeof(THING) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) ) // 3-factor product with 2 sizeof(variable), with redundant parens removed. @@ expression THING1, THING2; identifier COUNT; type TYPE1, TYPE2; @@ ( kzalloc( - sizeof(TYPE1) * sizeof(TYPE2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | kzalloc( - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | kzalloc( - sizeof(THING1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | kzalloc( - sizeof(THING1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | kzalloc( - sizeof(TYPE1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) | kzalloc( - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) ) // 3-factor product, only identifiers, with redundant parens removed. @@ identifier STRIDE, SIZE, COUNT; @@ ( kzalloc( - (COUNT) * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - COUNT * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - COUNT * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - (COUNT) * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - COUNT * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - (COUNT) * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - (COUNT) * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kzalloc( - COUNT * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) ) // Any remaining multi-factor products, first at least 3-factor products, // when they're not all constants... @@ expression E1, E2, E3; constant C1, C2, C3; @@ ( kzalloc(C1 * C2 * C3, ...) | kzalloc( - (E1) * E2 * E3 + array3_size(E1, E2, E3) , ...) | kzalloc( - (E1) * (E2) * E3 + array3_size(E1, E2, E3) , ...) | kzalloc( - (E1) * (E2) * (E3) + array3_size(E1, E2, E3) , ...) | kzalloc( - E1 * E2 * E3 + array3_size(E1, E2, E3) , ...) ) // And then all remaining 2 factors products when they're not all constants, // keeping sizeof() as the second factor argument. @@ expression THING, E1, E2; type TYPE; constant C1, C2, C3; @@ ( kzalloc(sizeof(THING) * C2, ...) | kzalloc(sizeof(TYPE) * C2, ...) | kzalloc(C1 * C2 * C3, ...) | kzalloc(C1 * C2, ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * (E2) + E2, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(TYPE) * E2 + E2, sizeof(TYPE) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * (E2) + E2, sizeof(THING) , ...) | - kzalloc + kcalloc ( - sizeof(THING) * E2 + E2, sizeof(THING) , ...) | - kzalloc + kcalloc ( - (E1) * E2 + E1, E2 , ...) | - kzalloc + kcalloc ( - (E1) * (E2) + E1, E2 , ...) | - kzalloc + kcalloc ( - E1 * E2 + E1, E2 , ...) ) Signed-off-by: Kees Cook --- arch/arm/mach-footbridge/dc21285.c | 2 +- arch/arm/mach-ixp4xx/common-pci.c | 2 +- arch/arm/mach-omap1/mcbsp.c | 2 +- arch/arm/mach-omap2/hsmmc.c | 2 +- arch/arm/mach-omap2/omap_device.c | 4 +- arch/arm/mach-omap2/prm_common.c | 9 ++-- arch/arm/mach-vexpress/spc.c | 2 +- arch/arm/mm/dma-mapping.c | 4 +- arch/arm64/kernel/armv8_deprecated.c | 4 +- arch/arm64/mm/context.c | 2 +- arch/ia64/kernel/topology.c | 6 +-- arch/ia64/sn/kernel/io_common.c | 2 +- arch/ia64/sn/pci/pcibr/pcibr_provider.c | 2 +- arch/mips/alchemy/common/clock.c | 2 +- arch/mips/alchemy/common/dbdma.c | 2 +- arch/mips/alchemy/common/platform.c | 4 +- arch/mips/alchemy/devboards/platform.c | 4 +- arch/mips/bmips/dma.c | 2 +- arch/mips/txx9/rbtx4939/setup.c | 2 +- arch/powerpc/kernel/vdso.c | 4 +- arch/powerpc/mm/numa.c | 2 +- arch/powerpc/net/bpf_jit_comp.c | 2 +- arch/powerpc/net/bpf_jit_comp64.c | 2 +- arch/powerpc/oprofile/cell/spu_profiler.c | 4 +- arch/powerpc/platforms/4xx/pci.c | 2 +- arch/powerpc/platforms/powernv/opal-sysparam.c | 8 ++-- arch/powerpc/sysdev/mpic.c | 4 +- arch/powerpc/sysdev/xive/native.c | 2 +- arch/s390/appldata/appldata_base.c | 2 +- arch/s390/kernel/vdso.c | 4 +- arch/sh/drivers/dma/dmabrg.c | 2 +- arch/sh/drivers/pci/pcie-sh7786.c | 2 +- arch/sparc/kernel/sys_sparc_64.c | 3 +- arch/x86/events/amd/iommu.c | 2 +- arch/x86/events/intel/uncore.c | 2 +- arch/x86/kernel/cpu/mcheck/mce.c | 2 +- arch/x86/kernel/cpu/mcheck/mce_amd.c | 2 +- arch/x86/kernel/cpu/mtrr/if.c | 2 +- arch/x86/kernel/hpet.c | 2 +- arch/x86/pci/xen.c | 2 +- arch/x86/platform/uv/uv_time.c | 2 +- block/bio.c | 3 +- block/blk-tag.c | 4 +- drivers/acpi/acpi_platform.c | 2 +- drivers/acpi/sysfs.c | 6 +-- drivers/android/binder_alloc.c | 4 +- drivers/ata/libata-core.c | 2 +- drivers/ata/libata-pmp.c | 2 +- drivers/atm/fore200e.c | 3 +- drivers/atm/iphase.c | 2 +- drivers/block/drbd/drbd_main.c | 3 +- drivers/block/null_blk.c | 9 ++-- drivers/block/ps3vram.c | 5 ++- drivers/block/rsxx/core.c | 3 +- drivers/block/rsxx/dma.c | 2 +- drivers/block/xen-blkback/xenbus.c | 3 +- drivers/block/xen-blkfront.c | 23 ++++++---- drivers/char/agp/amd-k7-agp.c | 3 +- drivers/char/agp/ati-agp.c | 3 +- drivers/char/agp/sworks-agp.c | 2 +- drivers/char/ipmi/ipmi_ssif.c | 3 +- drivers/clk/renesas/clk-r8a7740.c | 2 +- drivers/clk/renesas/clk-r8a7779.c | 2 +- drivers/clk/renesas/clk-rcar-gen2.c | 2 +- drivers/clk/renesas/clk-rz.c | 2 +- drivers/clk/st/clkgen-fsyn.c | 2 +- drivers/clk/st/clkgen-pll.c | 2 +- drivers/clk/sunxi/clk-usb.c | 2 +- drivers/clk/tegra/clk.c | 7 +-- drivers/clk/ti/apll.c | 2 +- drivers/clk/ti/divider.c | 4 +- drivers/clk/ti/dpll.c | 2 +- drivers/clocksource/sh_cmt.c | 2 +- drivers/clocksource/sh_mtu2.c | 2 +- drivers/clocksource/sh_tmu.c | 2 +- drivers/cpufreq/acpi-cpufreq.c | 4 +- drivers/cpufreq/arm_big_little.c | 2 +- drivers/cpufreq/cppc_cpufreq.c | 3 +- drivers/cpufreq/ia64-acpi-cpufreq.c | 4 +- drivers/cpufreq/longhaul.c | 4 +- drivers/cpufreq/pxa3xx-cpufreq.c | 2 +- drivers/cpufreq/s3c24xx-cpufreq.c | 2 +- drivers/cpufreq/sfi-cpufreq.c | 4 +- drivers/cpufreq/spear-cpufreq.c | 2 +- drivers/crypto/amcc/crypto4xx_core.c | 8 ++-- drivers/crypto/inside-secure/safexcel_hash.c | 2 +- drivers/crypto/marvell/hash.c | 2 +- drivers/crypto/n2_core.c | 4 +- drivers/crypto/qat/qat_common/qat_uclo.c | 5 ++- drivers/dma/ioat/init.c | 4 +- drivers/dma/mv_xor.c | 2 +- drivers/dma/pl330.c | 4 +- drivers/dma/sh/shdma-base.c | 5 ++- drivers/dma/xilinx/zynqmp_dma.c | 2 +- drivers/edac/amd64_edac.c | 2 +- drivers/edac/i7core_edac.c | 2 +- drivers/extcon/extcon.c | 24 ++++++----- drivers/firmware/dell_rbu.c | 2 +- drivers/firmware/efi/capsule.c | 2 +- drivers/firmware/efi/runtime-map.c | 2 +- drivers/fmc/fmc-sdb.c | 4 +- drivers/gpio/gpio-ml-ioh.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 6 +-- drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_test.c | 2 +- drivers/gpu/drm/amd/amdgpu/atom.c | 2 +- drivers/gpu/drm/amd/amdgpu/ci_dpm.c | 9 ++-- drivers/gpu/drm/amd/amdgpu/kv_dpm.c | 5 ++- drivers/gpu/drm/amd/amdgpu/si_dpm.c | 9 ++-- .../drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 2 +- drivers/gpu/drm/amd/display/dc/basics/logger.c | 2 +- drivers/gpu/drm/amd/display/dc/basics/vector.c | 4 +- .../gpu/drm/amd/display/dc/dce/dce_clock_source.c | 6 ++- drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c | 3 +- .../drm/amd/display/modules/color/color_gamma.c | 10 +++-- .../drm/amd/display/modules/freesync/freesync.c | 3 +- drivers/gpu/drm/amd/display/modules/stats/stats.c | 12 +++--- drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c | 2 +- drivers/gpu/drm/i915/gvt/vgpu.c | 2 +- drivers/gpu/drm/i915/intel_hdcp.c | 2 +- drivers/gpu/drm/i915/selftests/intel_uncore.c | 2 +- drivers/gpu/drm/nouveau/nvif/fifo.c | 4 +- drivers/gpu/drm/nouveau/nvif/object.c | 2 +- drivers/gpu/drm/nouveau/nvkm/core/event.c | 3 +- drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c | 2 +- drivers/gpu/drm/omapdrm/omap_gem.c | 2 +- drivers/gpu/drm/radeon/atom.c | 2 +- drivers/gpu/drm/radeon/btc_dpm.c | 4 +- drivers/gpu/drm/radeon/ci_dpm.c | 9 ++-- drivers/gpu/drm/radeon/kv_dpm.c | 5 ++- drivers/gpu/drm/radeon/ni_dpm.c | 9 ++-- drivers/gpu/drm/radeon/r600_dpm.c | 2 +- drivers/gpu/drm/radeon/radeon_atombios.c | 39 ++++++++++------- drivers/gpu/drm/radeon/radeon_combios.c | 9 ++-- drivers/gpu/drm/radeon/radeon_test.c | 2 +- drivers/gpu/drm/radeon/rs780_dpm.c | 5 ++- drivers/gpu/drm/radeon/rv6xx_dpm.c | 5 ++- drivers/gpu/drm/radeon/rv770_dpm.c | 5 ++- drivers/gpu/drm/radeon/si_dpm.c | 9 ++-- drivers/gpu/drm/radeon/sumo_dpm.c | 5 ++- drivers/gpu/drm/radeon/trinity_dpm.c | 5 ++- drivers/gpu/drm/selftests/test-drm_mm.c | 4 +- drivers/hid/hid-debug.c | 4 +- drivers/hv/hv.c | 2 +- drivers/hv/ring_buffer.c | 2 +- drivers/hwmon/acpi_power_meter.c | 7 +-- drivers/hwmon/coretemp.c | 2 +- drivers/hwmon/i5k_amb.c | 5 ++- drivers/hwmon/ibmpex.c | 2 +- drivers/i2c/busses/i2c-amd756-s4882.c | 4 +- drivers/i2c/busses/i2c-nforce2-s4985.c | 4 +- drivers/i2c/busses/i2c-nforce2.c | 2 +- drivers/i2c/i2c-stub.c | 5 ++- drivers/ide/hpt366.c | 2 +- drivers/ide/it821x.c | 2 +- drivers/iio/imu/adis_buffer.c | 2 +- drivers/iio/inkern.c | 2 +- drivers/infiniband/core/cache.c | 5 ++- drivers/infiniband/core/device.c | 4 +- drivers/infiniband/core/iwpm_util.c | 10 +++-- drivers/infiniband/hw/cxgb3/cxio_hal.c | 4 +- drivers/infiniband/hw/cxgb4/device.c | 7 +-- drivers/infiniband/hw/cxgb4/qp.c | 8 ++-- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 2 +- drivers/infiniband/hw/mlx4/mad.c | 3 +- drivers/infiniband/hw/mthca/mthca_mr.c | 2 +- drivers/infiniband/hw/mthca/mthca_profile.c | 2 +- drivers/infiniband/hw/nes/nes_mgt.c | 3 +- drivers/infiniband/hw/nes/nes_verbs.c | 5 ++- drivers/infiniband/hw/ocrdma/ocrdma_hw.c | 2 +- drivers/infiniband/hw/ocrdma/ocrdma_main.c | 11 ++--- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 12 +++--- drivers/infiniband/hw/qedr/main.c | 4 +- drivers/infiniband/hw/qedr/verbs.c | 4 +- drivers/infiniband/hw/qib/qib_iba7322.c | 5 ++- drivers/infiniband/hw/qib/qib_init.c | 4 +- drivers/infiniband/hw/usnic/usnic_ib_qp_grp.c | 2 +- drivers/infiniband/hw/usnic/usnic_vnic.c | 2 +- drivers/infiniband/ulp/ipoib/ipoib_main.c | 7 +-- drivers/infiniband/ulp/isert/ib_isert.c | 5 ++- drivers/input/keyboard/omap4-keypad.c | 3 +- drivers/iommu/dmar.c | 2 +- drivers/iommu/intel-iommu.c | 4 +- drivers/iommu/omap-iommu.c | 2 +- drivers/ipack/carriers/tpci200.c | 4 +- drivers/irqchip/irq-alpine-msi.c | 3 +- drivers/irqchip/irq-gic-v2m.c | 2 +- drivers/irqchip/irq-gic-v3-its.c | 15 ++++--- drivers/irqchip/irq-gic-v3.c | 5 ++- drivers/irqchip/irq-partition-percpu.c | 2 +- drivers/irqchip/irq-s3c24xx.c | 2 +- drivers/isdn/capi/capi.c | 2 +- drivers/isdn/gigaset/capi.c | 2 +- drivers/isdn/gigaset/i4l.c | 2 +- drivers/isdn/hardware/avm/b1.c | 2 +- drivers/isdn/hisax/fsm.c | 4 +- drivers/isdn/i4l/isdn_common.c | 4 +- drivers/isdn/mISDN/fsm.c | 6 ++- drivers/lightnvm/pblk-init.c | 2 +- drivers/mailbox/pcc.c | 3 +- drivers/md/bcache/super.c | 7 +-- drivers/md/dm-crypt.c | 5 ++- drivers/md/md-bitmap.c | 2 +- drivers/md/md-cluster.c | 6 +-- drivers/md/md-multipath.c | 3 +- drivers/md/raid0.c | 10 +++-- drivers/md/raid1.c | 9 ++-- drivers/md/raid10.c | 13 +++--- drivers/md/raid5.c | 15 ++++--- drivers/media/dvb-frontends/dib7000p.c | 4 +- drivers/media/dvb-frontends/dib8000.c | 4 +- drivers/media/dvb-frontends/dib9000.c | 4 +- drivers/media/usb/au0828/au0828-video.c | 6 +-- drivers/media/usb/cx231xx/cx231xx-core.c | 8 ++-- drivers/media/usb/cx231xx/cx231xx-vbi.c | 4 +- drivers/media/usb/go7007/go7007-fw.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-hdw.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-std.c | 2 +- drivers/media/usb/stk1160/stk1160-video.c | 6 +-- drivers/media/usb/stkwebcam/stk-webcam.c | 5 ++- drivers/media/usb/usbtv/usbtv-video.c | 2 +- drivers/mfd/cros_ec_dev.c | 7 +-- drivers/mfd/mfd-core.c | 2 +- drivers/mfd/timberdale.c | 4 +- drivers/misc/altera-stapl/altera.c | 6 +-- drivers/misc/cxl/guest.c | 2 +- drivers/misc/cxl/of.c | 2 +- drivers/misc/genwqe/card_ddcb.c | 9 ++-- drivers/misc/sgi-xp/xpc_main.c | 8 ++-- drivers/misc/sgi-xp/xpc_partition.c | 2 +- drivers/misc/sgi-xp/xpnet.c | 5 ++- drivers/misc/sram.c | 2 +- drivers/mtd/ar7part.c | 2 +- drivers/mtd/bcm47xxpart.c | 2 +- drivers/mtd/chips/cfi_cmdset_0001.c | 5 ++- drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- drivers/mtd/devices/docg3.c | 2 +- drivers/mtd/maps/physmap_of_core.c | 4 +- drivers/mtd/nand/onenand/onenand_base.c | 6 ++- drivers/mtd/ofpart.c | 4 +- drivers/mtd/parsers/parser_trx.c | 2 +- drivers/mtd/parsers/sharpslpart.c | 5 ++- drivers/mtd/sm_ftl.c | 4 +- drivers/mtd/tests/pagetest.c | 2 +- drivers/mtd/ubi/wl.c | 2 +- drivers/net/bonding/bond_main.c | 2 +- drivers/net/can/grcan.c | 4 +- drivers/net/can/slcan.c | 2 +- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 4 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c | 13 +++--- drivers/net/ethernet/broadcom/cnic.c | 10 ++--- drivers/net/ethernet/broadcom/tg3.c | 5 ++- drivers/net/ethernet/brocade/bna/bnad.c | 4 +- drivers/net/ethernet/calxeda/xgmac.c | 4 +- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 4 +- drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c | 4 +- drivers/net/ethernet/cortina/gemini.c | 4 +- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 3 +- drivers/net/ethernet/intel/e1000e/netdev.c | 2 +- drivers/net/ethernet/intel/igb/igb_main.c | 7 +-- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 +- drivers/net/ethernet/jme.c | 10 +++-- drivers/net/ethernet/mellanox/mlx4/alloc.c | 4 +- drivers/net/ethernet/mellanox/mlx4/cmd.c | 15 ++++--- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 20 +++++---- drivers/net/ethernet/mellanox/mlx4/main.c | 5 ++- .../net/ethernet/mellanox/mlx4/resource_tracker.c | 16 +++---- .../net/ethernet/mellanox/mlx5/core/fpga/ipsec.c | 2 +- .../net/ethernet/mellanox/mlx5/core/lib/clock.c | 5 ++- .../net/ethernet/mellanox/mlxsw/spectrum_qdisc.c | 3 +- drivers/net/ethernet/micrel/ksz884x.c | 2 +- drivers/net/ethernet/neterion/vxge/vxge-config.c | 8 ++-- drivers/net/ethernet/neterion/vxge/vxge-main.c | 4 +- drivers/net/ethernet/pasemi/pasemi_mac.c | 10 +++-- drivers/net/ethernet/qlogic/qed/qed_debug.c | 5 ++- drivers/net/ethernet/qlogic/qed/qed_dev.c | 16 +++---- drivers/net/ethernet/qlogic/qed/qed_init_ops.c | 4 +- drivers/net/ethernet/qlogic/qed/qed_l2.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 10 +++-- .../ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 8 ++-- drivers/net/ethernet/socionext/netsec.c | 2 +- drivers/net/ethernet/toshiba/ps3_gelic_wireless.c | 5 ++- drivers/net/phy/dp83640.c | 5 ++- drivers/net/slip/slip.c | 2 +- drivers/net/team/team.c | 2 +- drivers/net/usb/smsc95xx.c | 2 +- drivers/net/virtio_net.c | 8 ++-- drivers/net/wan/fsl_ucc_hdlc.c | 6 ++- drivers/net/wireless/ath/ath10k/htt_rx.c | 2 +- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 2 +- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/ath/carl9170/main.c | 7 +-- drivers/net/wireless/broadcom/b43/phy_n.c | 2 +- drivers/net/wireless/broadcom/b43legacy/main.c | 4 +- .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 5 ++- .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 2 +- .../wireless/broadcom/brcm80211/brcmsmac/main.c | 7 +-- drivers/net/wireless/intel/iwlegacy/common.c | 13 +++--- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 2 +- drivers/net/wireless/intersil/p54/eeprom.c | 12 +++--- drivers/net/wireless/intersil/prism54/oid_mgt.c | 2 +- .../net/wireless/marvell/mwifiex/11n_rxreorder.c | 4 +- drivers/net/wireless/marvell/mwifiex/sdio.c | 9 ++-- drivers/net/wireless/quantenna/qtnfmac/commands.c | 2 +- drivers/net/wireless/ralink/rt2x00/rt2x00debug.c | 2 +- drivers/net/wireless/realtek/rtlwifi/efuse.c | 4 +- drivers/net/wireless/realtek/rtlwifi/usb.c | 2 +- drivers/net/wireless/st/cw1200/queue.c | 10 ++--- drivers/net/wireless/st/cw1200/scan.c | 6 +-- drivers/nvmem/rockchip-efuse.c | 6 ++- drivers/nvmem/sunxi_sid.c | 2 +- drivers/of/platform.c | 2 +- drivers/of/unittest.c | 2 +- drivers/opp/ti-opp-supply.c | 4 +- drivers/pci/msi.c | 4 +- drivers/pci/pci-sysfs.c | 2 +- drivers/pcmcia/pd6729.c | 2 +- drivers/pinctrl/bcm/pinctrl-bcm2835.c | 4 +- drivers/pinctrl/freescale/pinctrl-mxs.c | 2 +- drivers/pinctrl/pinctrl-lantiq.c | 3 +- drivers/pinctrl/sirf/pinctrl-sirf.c | 2 +- drivers/pinctrl/spear/pinctrl-spear.c | 2 +- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 2 +- drivers/pinctrl/vt8500/pinctrl-wmt.c | 2 +- drivers/platform/x86/alienware-wmi.c | 6 +-- drivers/platform/x86/intel_ips.c | 12 +++--- drivers/platform/x86/panasonic-laptop.c | 2 +- drivers/platform/x86/thinkpad_acpi.c | 2 +- drivers/power/supply/wm97xx_battery.c | 2 +- drivers/power/supply/z2_battery.c | 2 +- drivers/powercap/powercap_sys.c | 9 ++-- drivers/rapidio/rio-scan.c | 6 +-- drivers/regulator/s2mps11.c | 2 +- drivers/s390/block/dcssblk.c | 6 +-- drivers/s390/char/keyboard.c | 2 +- drivers/s390/char/vmur.c | 2 +- drivers/s390/char/zcore.c | 2 +- drivers/s390/cio/qdio_setup.c | 2 +- drivers/s390/cio/qdio_thinint.c | 5 ++- drivers/s390/crypto/pkey_api.c | 2 +- drivers/s390/net/ctcm_main.c | 2 +- drivers/s390/net/qeth_core_main.c | 27 ++++++------ drivers/scsi/BusLogic.c | 2 +- drivers/scsi/aacraid/linit.c | 4 +- drivers/scsi/aic7xxx/aic7xxx_core.c | 4 +- drivers/scsi/aic94xx/aic94xx_hwi.c | 5 ++- drivers/scsi/aic94xx/aic94xx_init.c | 2 +- drivers/scsi/be2iscsi/be_main.c | 40 +++++++++-------- drivers/scsi/bfa/bfad_attr.c | 2 +- drivers/scsi/bfa/bfad_bsg.c | 5 ++- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 2 +- drivers/scsi/bnx2fc/bnx2fc_io.c | 8 ++-- drivers/scsi/csiostor/csio_wr.c | 4 +- drivers/scsi/esas2r/esas2r_init.c | 11 ++--- drivers/scsi/hpsa.c | 22 +++++----- drivers/scsi/ipr.c | 10 +++-- drivers/scsi/libsas/sas_expander.c | 2 +- drivers/scsi/lpfc/lpfc_init.c | 7 +-- drivers/scsi/lpfc/lpfc_sli.c | 50 ++++++++++------------ drivers/scsi/lpfc/lpfc_vport.c | 2 +- drivers/scsi/megaraid/megaraid_sas_base.c | 8 ++-- drivers/scsi/megaraid/megaraid_sas_fusion.c | 2 +- drivers/scsi/osst.c | 2 +- drivers/scsi/pm8001/pm8001_ctl.c | 2 +- drivers/scsi/pmcraid.c | 5 ++- drivers/scsi/qedi/qedi_main.c | 2 +- drivers/scsi/qla2xxx/qla_init.c | 10 +++-- drivers/scsi/qla2xxx/qla_isr.c | 5 ++- drivers/scsi/qla2xxx/qla_os.c | 14 +++--- drivers/scsi/qla2xxx/qla_target.c | 10 +++-- drivers/scsi/scsi_debug.c | 2 +- drivers/scsi/ses.c | 2 +- drivers/scsi/sg.c | 2 +- drivers/scsi/smartpqi/smartpqi_init.c | 5 ++- drivers/scsi/st.c | 2 +- drivers/sh/clk/cpg.c | 2 +- drivers/sh/intc/core.c | 10 ++--- drivers/sh/maple/maple.c | 2 +- drivers/slimbus/qcom-ctrl.c | 2 +- drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c | 2 +- drivers/staging/rtlwifi/efuse.c | 4 +- drivers/staging/unisys/visorhba/visorhba_main.c | 2 +- drivers/target/target_core_transport.c | 2 +- drivers/target/target_core_user.c | 5 ++- drivers/thermal/int340x_thermal/acpi_thermal_rel.c | 4 +- .../thermal/int340x_thermal/int340x_thermal_zone.c | 7 +-- drivers/thermal/of-thermal.c | 4 +- drivers/thermal/x86_pkg_temp_thermal.c | 3 +- drivers/tty/ehv_bytechan.c | 2 +- drivers/tty/goldfish.c | 5 ++- drivers/tty/hvc/hvc_iucv.c | 2 +- drivers/tty/serial/pch_uart.c | 2 +- drivers/tty/serial/serial_core.c | 2 +- drivers/tty/serial/sunsab.c | 5 ++- drivers/uio/uio_pruss.c | 2 +- drivers/usb/core/hub.c | 2 +- drivers/usb/dwc2/hcd.c | 11 ++--- drivers/usb/gadget/udc/bdc/bdc_ep.c | 6 +-- drivers/usb/gadget/udc/fsl_udc_core.c | 2 +- drivers/usb/host/ehci-sched.c | 5 ++- drivers/usb/host/imx21-hcd.c | 4 +- drivers/usb/mon/mon_bin.c | 3 +- drivers/usb/renesas_usbhs/mod_gadget.c | 2 +- drivers/usb/renesas_usbhs/pipe.c | 3 +- drivers/usb/wusbcore/wa-rpipe.c | 3 +- drivers/vhost/scsi.c | 15 ++++--- drivers/video/console/sticore.c | 2 +- drivers/video/fbdev/broadsheetfb.c | 2 +- drivers/video/fbdev/core/fbmon.c | 7 +-- drivers/video/fbdev/mmp/fb/mmpfb.c | 4 +- drivers/video/fbdev/omap2/omapfb/dss/manager.c | 4 +- drivers/video/fbdev/omap2/omapfb/dss/overlay.c | 4 +- drivers/video/fbdev/uvesafb.c | 7 +-- drivers/video/of_display_timing.c | 5 ++- drivers/virt/fsl_hypervisor.c | 2 +- drivers/virtio/virtio_pci_common.c | 2 +- drivers/xen/arm-device.c | 6 +-- fs/btrfs/check-integrity.c | 4 +- fs/cifs/cifssmb.c | 2 +- fs/cifs/file.c | 2 +- fs/ext4/extents.c | 8 ++-- fs/nfs/flexfilelayout/flexfilelayout.c | 2 +- fs/nfs/flexfilelayout/flexfilelayoutdev.c | 3 +- fs/nfsd/export.c | 5 ++- fs/ocfs2/journal.c | 2 +- fs/ocfs2/sysfile.c | 9 ++-- fs/overlayfs/namei.c | 2 +- fs/proc/proc_sysctl.c | 2 +- fs/reiserfs/inode.c | 3 +- fs/udf/super.c | 7 +-- kernel/bpf/verifier.c | 2 +- kernel/debug/kdb/kdb_main.c | 2 +- kernel/events/uprobes.c | 3 +- kernel/locking/locktorture.c | 6 ++- kernel/sched/fair.c | 4 +- kernel/sched/rt.c | 4 +- kernel/sysctl.c | 3 +- kernel/trace/ftrace.c | 2 +- kernel/trace/trace.c | 3 +- kernel/workqueue.c | 2 +- lib/lru_cache.c | 2 +- lib/mpi/mpiutil.c | 2 +- mm/slab.c | 3 +- mm/slub.c | 7 +-- net/bridge/br_multicast.c | 2 +- net/can/bcm.c | 3 +- net/core/ethtool.c | 4 +- net/ieee802154/nl-phy.c | 2 +- net/ipv4/fib_frontend.c | 2 +- net/ipv4/route.c | 2 +- net/ipv6/icmp.c | 2 +- net/mac80211/chan.c | 2 +- net/mac80211/rc80211_minstrel.c | 2 +- net/mac80211/rc80211_minstrel_ht.c | 2 +- net/mac80211/scan.c | 2 +- net/mac80211/util.c | 5 ++- net/netfilter/nf_tables_api.c | 2 +- net/netfilter/nfnetlink_cthelper.c | 5 ++- net/netrom/af_netrom.c | 2 +- net/openvswitch/vport.c | 2 +- net/rds/ib.c | 3 +- net/rose/af_rose.c | 3 +- net/sctp/auth.c | 5 ++- net/smc/smc_wr.c | 6 +-- net/sunrpc/auth_gss/gss_rpc_upcall.c | 2 +- net/sunrpc/cache.c | 2 +- net/wireless/nl80211.c | 4 +- security/apparmor/policy_unpack.c | 2 +- security/selinux/ss/services.c | 2 +- sound/firewire/fireface/ff-protocol-ff400.c | 2 +- sound/pci/ctxfi/ctatc.c | 18 ++++---- sound/pci/ctxfi/ctdaio.c | 3 +- sound/pci/ctxfi/ctmixer.c | 5 ++- sound/pci/ctxfi/ctsrc.c | 2 +- sound/pci/hda/patch_ca0132.c | 4 +- sound/soc/codecs/wm_adsp.c | 2 +- sound/soc/intel/common/sst-ipc.c | 4 +- sound/soc/soc-core.c | 4 +- sound/soc/soc-dapm.c | 2 +- sound/soc/soc-topology.c | 2 +- sound/usb/6fire/pcm.c | 10 +++-- sound/usb/line6/capture.c | 4 +- sound/usb/line6/playback.c | 4 +- virt/kvm/arm/vgic/vgic-v4.c | 2 +- 484 files changed, 1177 insertions(+), 977 deletions(-) (limited to 'drivers/pci') diff --git a/arch/arm/mach-footbridge/dc21285.c b/arch/arm/mach-footbridge/dc21285.c index e7b350f18f5f..16d71bac0061 100644 --- a/arch/arm/mach-footbridge/dc21285.c +++ b/arch/arm/mach-footbridge/dc21285.c @@ -252,7 +252,7 @@ int __init dc21285_setup(int nr, struct pci_sys_data *sys) if (nr || !footbridge_cfn_mode()) return 0; - res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); + res = kcalloc(2, sizeof(struct resource), GFP_KERNEL); if (!res) { printk("out of memory for root bus resources"); return 0; diff --git a/arch/arm/mach-ixp4xx/common-pci.c b/arch/arm/mach-ixp4xx/common-pci.c index bcf3df59f71b..6835b17113e5 100644 --- a/arch/arm/mach-ixp4xx/common-pci.c +++ b/arch/arm/mach-ixp4xx/common-pci.c @@ -421,7 +421,7 @@ int ixp4xx_setup(int nr, struct pci_sys_data *sys) if (nr >= 1) return 0; - res = kzalloc(sizeof(*res) * 2, GFP_KERNEL); + res = kcalloc(2, sizeof(*res), GFP_KERNEL); if (res == NULL) { /* * If we're out of memory this early, something is wrong, diff --git a/arch/arm/mach-omap1/mcbsp.c b/arch/arm/mach-omap1/mcbsp.c index 8ed67f8d1762..27e22e702f96 100644 --- a/arch/arm/mach-omap1/mcbsp.c +++ b/arch/arm/mach-omap1/mcbsp.c @@ -389,7 +389,7 @@ static void omap_mcbsp_register_board_cfg(struct resource *res, int res_count, { int i; - omap_mcbsp_devices = kzalloc(size * sizeof(struct platform_device *), + omap_mcbsp_devices = kcalloc(size, sizeof(struct platform_device *), GFP_KERNEL); if (!omap_mcbsp_devices) { printk(KERN_ERR "Could not register McBSP devices\n"); diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index b064066d431c..9344035d537f 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -35,7 +35,7 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, { char *hc_name; - hc_name = kzalloc(sizeof(char) * (HSMMC_NAME_LEN + 1), GFP_KERNEL); + hc_name = kzalloc(HSMMC_NAME_LEN + 1, GFP_KERNEL); if (!hc_name) { kfree(hc_name); return -ENOMEM; diff --git a/arch/arm/mach-omap2/omap_device.c b/arch/arm/mach-omap2/omap_device.c index 3b829a50d1db..06b6bca3a179 100644 --- a/arch/arm/mach-omap2/omap_device.c +++ b/arch/arm/mach-omap2/omap_device.c @@ -155,7 +155,7 @@ static int omap_device_build_from_dt(struct platform_device *pdev) if (!omap_hwmod_parse_module_range(NULL, node, &res)) return -ENODEV; - hwmods = kzalloc(sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); + hwmods = kcalloc(oh_cnt, sizeof(struct omap_hwmod *), GFP_KERNEL); if (!hwmods) { ret = -ENOMEM; goto odbfd_exit; @@ -405,7 +405,7 @@ omap_device_copy_resources(struct omap_hwmod *oh, goto error; } - res = kzalloc(sizeof(*res) * 2, GFP_KERNEL); + res = kcalloc(2, sizeof(*res), GFP_KERNEL); if (!res) return -ENOMEM; diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 021b5a8b9c0a..058a37e6d11c 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c @@ -285,10 +285,11 @@ int omap_prcm_register_chain_handler(struct omap_prcm_irq_setup *irq_setup) prcm_irq_setup = irq_setup; - prcm_irq_chips = kzalloc(sizeof(void *) * nr_regs, GFP_KERNEL); - prcm_irq_setup->saved_mask = kzalloc(sizeof(u32) * nr_regs, GFP_KERNEL); - prcm_irq_setup->priority_mask = kzalloc(sizeof(u32) * nr_regs, - GFP_KERNEL); + prcm_irq_chips = kcalloc(nr_regs, sizeof(void *), GFP_KERNEL); + prcm_irq_setup->saved_mask = kcalloc(nr_regs, sizeof(u32), + GFP_KERNEL); + prcm_irq_setup->priority_mask = kcalloc(nr_regs, sizeof(u32), + GFP_KERNEL); if (!prcm_irq_chips || !prcm_irq_setup->saved_mask || !prcm_irq_setup->priority_mask) diff --git a/arch/arm/mach-vexpress/spc.c b/arch/arm/mach-vexpress/spc.c index 21c064267af5..0f5381d13494 100644 --- a/arch/arm/mach-vexpress/spc.c +++ b/arch/arm/mach-vexpress/spc.c @@ -403,7 +403,7 @@ static int ve_spc_populate_opps(uint32_t cluster) uint32_t data = 0, off, ret, idx; struct ve_spc_opp *opps; - opps = kzalloc(sizeof(*opps) * MAX_OPPS, GFP_KERNEL); + opps = kcalloc(MAX_OPPS, sizeof(*opps), GFP_KERNEL); if (!opps) return -ENOMEM; diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index af27f1c22d93..be0fa7e39c26 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2162,8 +2162,8 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size) goto err; mapping->bitmap_size = bitmap_size; - mapping->bitmaps = kzalloc(extensions * sizeof(unsigned long *), - GFP_KERNEL); + mapping->bitmaps = kcalloc(extensions, sizeof(unsigned long *), + GFP_KERNEL); if (!mapping->bitmaps) goto err2; diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index 97d45d5151d4..d4707abb2f16 100644 --- a/arch/arm64/kernel/armv8_deprecated.c +++ b/arch/arm64/kernel/armv8_deprecated.c @@ -234,8 +234,8 @@ static void __init register_insn_emulation_sysctl(void) struct insn_emulation *insn; struct ctl_table *insns_sysctl, *sysctl; - insns_sysctl = kzalloc(sizeof(*sysctl) * (nr_insn_emulated + 1), - GFP_KERNEL); + insns_sysctl = kcalloc(nr_insn_emulated + 1, sizeof(*sysctl), + GFP_KERNEL); raw_spin_lock_irqsave(&insn_emulation_lock, flags); list_for_each_entry(insn, &insn_emulation, node) { diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 301417ae2ba8..c127f94da8e2 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -263,7 +263,7 @@ static int asids_init(void) */ WARN_ON(NUM_USER_ASIDS - 1 <= num_possible_cpus()); atomic64_set(&asid_generation, ASID_FIRST_VERSION); - asid_map = kzalloc(BITS_TO_LONGS(NUM_USER_ASIDS) * sizeof(*asid_map), + asid_map = kcalloc(BITS_TO_LONGS(NUM_USER_ASIDS), sizeof(*asid_map), GFP_KERNEL); if (!asid_map) panic("Failed to allocate bitmap for %lu ASIDs\n", diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c index d76529cbff20..9b820f7a6a98 100644 --- a/arch/ia64/kernel/topology.c +++ b/arch/ia64/kernel/topology.c @@ -85,7 +85,7 @@ static int __init topology_init(void) } #endif - sysfs_cpus = kzalloc(sizeof(struct ia64_cpu) * NR_CPUS, GFP_KERNEL); + sysfs_cpus = kcalloc(NR_CPUS, sizeof(struct ia64_cpu), GFP_KERNEL); if (!sysfs_cpus) panic("kzalloc in topology_init failed - NR_CPUS too big?"); @@ -319,8 +319,8 @@ static int cpu_cache_sysfs_init(unsigned int cpu) return -1; } - this_cache=kzalloc(sizeof(struct cache_info)*unique_caches, - GFP_KERNEL); + this_cache=kcalloc(unique_caches, sizeof(struct cache_info), + GFP_KERNEL); if (this_cache == NULL) return -ENOMEM; diff --git a/arch/ia64/sn/kernel/io_common.c b/arch/ia64/sn/kernel/io_common.c index 8479e9a7ce16..102aabad6d20 100644 --- a/arch/ia64/sn/kernel/io_common.c +++ b/arch/ia64/sn/kernel/io_common.c @@ -132,7 +132,7 @@ static s64 sn_device_fixup_war(u64 nasid, u64 widget, int device, printk_once(KERN_WARNING "PROM version < 4.50 -- implementing old PROM flush WAR\n"); - war_list = kzalloc(DEV_PER_WIDGET * sizeof(*war_list), GFP_KERNEL); + war_list = kcalloc(DEV_PER_WIDGET, sizeof(*war_list), GFP_KERNEL); BUG_ON(!war_list); SAL_CALL_NOLOCK(isrv, SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, diff --git a/arch/ia64/sn/pci/pcibr/pcibr_provider.c b/arch/ia64/sn/pci/pcibr/pcibr_provider.c index 8dbbef4a4f47..7195df1da121 100644 --- a/arch/ia64/sn/pci/pcibr/pcibr_provider.c +++ b/arch/ia64/sn/pci/pcibr/pcibr_provider.c @@ -184,7 +184,7 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont /* Setup the PMU ATE map */ soft->pbi_int_ate_resource.lowest_free_index = 0; soft->pbi_int_ate_resource.ate = - kzalloc(soft->pbi_int_ate_size * sizeof(u64), GFP_KERNEL); + kcalloc(soft->pbi_int_ate_size, sizeof(u64), GFP_KERNEL); if (!soft->pbi_int_ate_resource.ate) { kfree(soft); diff --git a/arch/mips/alchemy/common/clock.c b/arch/mips/alchemy/common/clock.c index 6b6f6851df92..d129475fd40d 100644 --- a/arch/mips/alchemy/common/clock.c +++ b/arch/mips/alchemy/common/clock.c @@ -985,7 +985,7 @@ static int __init alchemy_clk_setup_imux(int ctype) return -ENODEV; } - a = kzalloc((sizeof(*a)) * 6, GFP_KERNEL); + a = kcalloc(6, sizeof(*a), GFP_KERNEL); if (!a) return -ENOMEM; diff --git a/arch/mips/alchemy/common/dbdma.c b/arch/mips/alchemy/common/dbdma.c index 24b04758cce5..4ca2c28878e0 100644 --- a/arch/mips/alchemy/common/dbdma.c +++ b/arch/mips/alchemy/common/dbdma.c @@ -1050,7 +1050,7 @@ static int __init dbdma_setup(unsigned int irq, dbdev_tab_t *idtable) { int ret; - dbdev_tab = kzalloc(sizeof(dbdev_tab_t) * DBDEV_TAB_SIZE, GFP_KERNEL); + dbdev_tab = kcalloc(DBDEV_TAB_SIZE, sizeof(dbdev_tab_t), GFP_KERNEL); if (!dbdev_tab) return -ENOMEM; diff --git a/arch/mips/alchemy/common/platform.c b/arch/mips/alchemy/common/platform.c index d77a64f4c78b..1454d9f6ab2d 100644 --- a/arch/mips/alchemy/common/platform.c +++ b/arch/mips/alchemy/common/platform.c @@ -115,7 +115,7 @@ static void __init alchemy_setup_uarts(int ctype) uartclk = clk_get_rate(clk); clk_put(clk); - ports = kzalloc(s * (c + 1), GFP_KERNEL); + ports = kcalloc(s, (c + 1), GFP_KERNEL); if (!ports) { printk(KERN_INFO "Alchemy: no memory for UART data\n"); return; @@ -198,7 +198,7 @@ static unsigned long alchemy_ehci_data[][2] __initdata = { static int __init _new_usbres(struct resource **r, struct platform_device **d) { - *r = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); + *r = kcalloc(2, sizeof(struct resource), GFP_KERNEL); if (!*r) return -ENOMEM; *d = kzalloc(sizeof(struct platform_device), GFP_KERNEL); diff --git a/arch/mips/alchemy/devboards/platform.c b/arch/mips/alchemy/devboards/platform.c index 4640edab207c..203854ddd1bb 100644 --- a/arch/mips/alchemy/devboards/platform.c +++ b/arch/mips/alchemy/devboards/platform.c @@ -103,7 +103,7 @@ int __init db1x_register_pcmcia_socket(phys_addr_t pcmcia_attr_start, if (stschg_irq) cnt++; - sr = kzalloc(sizeof(struct resource) * cnt, GFP_KERNEL); + sr = kcalloc(cnt, sizeof(struct resource), GFP_KERNEL); if (!sr) return -ENOMEM; @@ -178,7 +178,7 @@ int __init db1x_register_norflash(unsigned long size, int width, return -EINVAL; ret = -ENOMEM; - parts = kzalloc(sizeof(struct mtd_partition) * 5, GFP_KERNEL); + parts = kcalloc(5, sizeof(struct mtd_partition), GFP_KERNEL); if (!parts) goto out; diff --git a/arch/mips/bmips/dma.c b/arch/mips/bmips/dma.c index 04790f4e1805..6dec30842b2f 100644 --- a/arch/mips/bmips/dma.c +++ b/arch/mips/bmips/dma.c @@ -94,7 +94,7 @@ static int __init bmips_init_dma_ranges(void) goto out_bad; /* add a dummy (zero) entry at the end as a sentinel */ - bmips_dma_ranges = kzalloc(sizeof(struct bmips_dma_range) * (len + 1), + bmips_dma_ranges = kcalloc(len + 1, sizeof(struct bmips_dma_range), GFP_KERNEL); if (!bmips_dma_ranges) goto out_bad; diff --git a/arch/mips/txx9/rbtx4939/setup.c b/arch/mips/txx9/rbtx4939/setup.c index fd26fadc8617..ef29a9c2ffd6 100644 --- a/arch/mips/txx9/rbtx4939/setup.c +++ b/arch/mips/txx9/rbtx4939/setup.c @@ -219,7 +219,7 @@ static int __init rbtx4939_led_probe(struct platform_device *pdev) "nand-disk", }; - leds_data = kzalloc(sizeof(*leds_data) * RBTX4939_MAX_7SEGLEDS, + leds_data = kcalloc(RBTX4939_MAX_7SEGLEDS, sizeof(*leds_data), GFP_KERNEL); if (!leds_data) return -ENOMEM; diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index b44ec104a5a1..d2205b97628c 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -791,7 +791,7 @@ static int __init vdso_init(void) #ifdef CONFIG_VDSO32 /* Make sure pages are in the correct state */ - vdso32_pagelist = kzalloc(sizeof(struct page *) * (vdso32_pages + 2), + vdso32_pagelist = kcalloc(vdso32_pages + 2, sizeof(struct page *), GFP_KERNEL); BUG_ON(vdso32_pagelist == NULL); for (i = 0; i < vdso32_pages; i++) { @@ -805,7 +805,7 @@ static int __init vdso_init(void) #endif #ifdef CONFIG_PPC64 - vdso64_pagelist = kzalloc(sizeof(struct page *) * (vdso64_pages + 2), + vdso64_pagelist = kcalloc(vdso64_pages + 2, sizeof(struct page *), GFP_KERNEL); BUG_ON(vdso64_pagelist == NULL); for (i = 0; i < vdso64_pages; i++) { diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 57a5029b4521..0c7e05d89244 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1316,7 +1316,7 @@ int numa_update_cpu_topology(bool cpus_locked) if (!weight) return 0; - updates = kzalloc(weight * (sizeof(*updates)), GFP_KERNEL); + updates = kcalloc(weight, sizeof(*updates), GFP_KERNEL); if (!updates) return 0; diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index a9636d8cba15..5b061fc81df3 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -566,7 +566,7 @@ void bpf_jit_compile(struct bpf_prog *fp) if (!bpf_jit_enable) return; - addrs = kzalloc((flen+1) * sizeof(*addrs), GFP_KERNEL); + addrs = kcalloc(flen + 1, sizeof(*addrs), GFP_KERNEL); if (addrs == NULL) return; diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index f1c95779843b..380cbf9a40d9 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -949,7 +949,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) goto skip_init_ctx; } - addrs = kzalloc((flen+1) * sizeof(*addrs), GFP_KERNEL); + addrs = kcalloc(flen + 1, sizeof(*addrs), GFP_KERNEL); if (addrs == NULL) { fp = org_fp; goto out_addrs; diff --git a/arch/powerpc/oprofile/cell/spu_profiler.c b/arch/powerpc/oprofile/cell/spu_profiler.c index 5182f2936af2..4e099e556645 100644 --- a/arch/powerpc/oprofile/cell/spu_profiler.c +++ b/arch/powerpc/oprofile/cell/spu_profiler.c @@ -210,8 +210,8 @@ int start_spu_profiling_cycles(unsigned int cycles_reset) timer.function = profile_spus; /* Allocate arrays for collecting SPU PC samples */ - samples = kzalloc(SPUS_PER_NODE * - TRACE_ARRAY_SIZE * sizeof(u32), GFP_KERNEL); + samples = kcalloc(SPUS_PER_NODE * TRACE_ARRAY_SIZE, sizeof(u32), + GFP_KERNEL); if (!samples) return -ENOMEM; diff --git a/arch/powerpc/platforms/4xx/pci.c b/arch/powerpc/platforms/4xx/pci.c index 73e6b36bcd51..5aca523551ae 100644 --- a/arch/powerpc/platforms/4xx/pci.c +++ b/arch/powerpc/platforms/4xx/pci.c @@ -1449,7 +1449,7 @@ static int __init ppc4xx_pciex_check_core_init(struct device_node *np) count = ppc4xx_pciex_hwops->core_init(np); if (count > 0) { ppc4xx_pciex_ports = - kzalloc(count * sizeof(struct ppc4xx_pciex_port), + kcalloc(count, sizeof(struct ppc4xx_pciex_port), GFP_KERNEL); if (ppc4xx_pciex_ports) { ppc4xx_pciex_port_count = count; diff --git a/arch/powerpc/platforms/powernv/opal-sysparam.c b/arch/powerpc/platforms/powernv/opal-sysparam.c index 6fd4092798d5..9aa87df114fd 100644 --- a/arch/powerpc/platforms/powernv/opal-sysparam.c +++ b/arch/powerpc/platforms/powernv/opal-sysparam.c @@ -198,21 +198,21 @@ void __init opal_sys_param_init(void) goto out_param_buf; } - id = kzalloc(sizeof(*id) * count, GFP_KERNEL); + id = kcalloc(count, sizeof(*id), GFP_KERNEL); if (!id) { pr_err("SYSPARAM: Failed to allocate memory to read parameter " "id\n"); goto out_param_buf; } - size = kzalloc(sizeof(*size) * count, GFP_KERNEL); + size = kcalloc(count, sizeof(*size), GFP_KERNEL); if (!size) { pr_err("SYSPARAM: Failed to allocate memory to read parameter " "size\n"); goto out_free_id; } - perm = kzalloc(sizeof(*perm) * count, GFP_KERNEL); + perm = kcalloc(count, sizeof(*perm), GFP_KERNEL); if (!perm) { pr_err("SYSPARAM: Failed to allocate memory to read supported " "action on the parameter"); @@ -235,7 +235,7 @@ void __init opal_sys_param_init(void) goto out_free_perm; } - attr = kzalloc(sizeof(*attr) * count, GFP_KERNEL); + attr = kcalloc(count, sizeof(*attr), GFP_KERNEL); if (!attr) { pr_err("SYSPARAM: Failed to allocate memory for parameter " "attributes\n"); diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index df062a154ca8..353b43972bbf 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -544,7 +544,7 @@ static void __init mpic_scan_ht_pics(struct mpic *mpic) printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n"); /* Allocate fixups array */ - mpic->fixups = kzalloc(128 * sizeof(*mpic->fixups), GFP_KERNEL); + mpic->fixups = kcalloc(128, sizeof(*mpic->fixups), GFP_KERNEL); BUG_ON(mpic->fixups == NULL); /* Init spinlock */ @@ -1324,7 +1324,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, if (psrc) { /* Allocate a bitmap with one bit per interrupt */ unsigned int mapsize = BITS_TO_LONGS(intvec_top + 1); - mpic->protected = kzalloc(mapsize*sizeof(long), GFP_KERNEL); + mpic->protected = kcalloc(mapsize, sizeof(long), GFP_KERNEL); BUG_ON(mpic->protected == NULL); for (i = 0; i < psize/sizeof(u32); i++) { if (psrc[i] > intvec_top) diff --git a/arch/powerpc/sysdev/xive/native.c b/arch/powerpc/sysdev/xive/native.c index 83bcd72b21cf..311185b9960a 100644 --- a/arch/powerpc/sysdev/xive/native.c +++ b/arch/powerpc/sysdev/xive/native.c @@ -489,7 +489,7 @@ static bool xive_parse_provisioning(struct device_node *np) if (rc == 0) return true; - xive_provision_chips = kzalloc(4 * xive_provision_chip_count, + xive_provision_chips = kcalloc(4, xive_provision_chip_count, GFP_KERNEL); if (WARN_ON(!xive_provision_chips)) return false; diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index cb6e8066b1ad..ee6a9c387c87 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -391,7 +391,7 @@ int appldata_register_ops(struct appldata_ops *ops) if (ops->size > APPLDATA_MAX_REC_SIZE) return -EINVAL; - ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL); + ops->ctl_table = kcalloc(4, sizeof(struct ctl_table), GFP_KERNEL); if (!ops->ctl_table) return -ENOMEM; diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c index f3a1c7c6824e..09abae40f917 100644 --- a/arch/s390/kernel/vdso.c +++ b/arch/s390/kernel/vdso.c @@ -285,7 +285,7 @@ static int __init vdso_init(void) + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1; /* Make sure pages are in the correct state */ - vdso32_pagelist = kzalloc(sizeof(struct page *) * (vdso32_pages + 1), + vdso32_pagelist = kcalloc(vdso32_pages + 1, sizeof(struct page *), GFP_KERNEL); BUG_ON(vdso32_pagelist == NULL); for (i = 0; i < vdso32_pages - 1; i++) { @@ -303,7 +303,7 @@ static int __init vdso_init(void) + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1; /* Make sure pages are in the correct state */ - vdso64_pagelist = kzalloc(sizeof(struct page *) * (vdso64_pages + 1), + vdso64_pagelist = kcalloc(vdso64_pages + 1, sizeof(struct page *), GFP_KERNEL); BUG_ON(vdso64_pagelist == NULL); for (i = 0; i < vdso64_pages - 1; i++) { diff --git a/arch/sh/drivers/dma/dmabrg.c b/arch/sh/drivers/dma/dmabrg.c index c0dd904483c7..e5a57a109d6c 100644 --- a/arch/sh/drivers/dma/dmabrg.c +++ b/arch/sh/drivers/dma/dmabrg.c @@ -154,7 +154,7 @@ static int __init dmabrg_init(void) unsigned long or; int ret; - dmabrg_handlers = kzalloc(10 * sizeof(struct dmabrg_handler), + dmabrg_handlers = kcalloc(10, sizeof(struct dmabrg_handler), GFP_KERNEL); if (!dmabrg_handlers) return -ENOMEM; diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c index 382e7ecf4c82..3d81a8b80942 100644 --- a/arch/sh/drivers/pci/pcie-sh7786.c +++ b/arch/sh/drivers/pci/pcie-sh7786.c @@ -561,7 +561,7 @@ static int __init sh7786_pcie_init(void) if (unlikely(nr_ports == 0)) return -ENODEV; - sh7786_pcie_ports = kzalloc(nr_ports * sizeof(struct sh7786_pcie_port), + sh7786_pcie_ports = kcalloc(nr_ports, sizeof(struct sh7786_pcie_port), GFP_KERNEL); if (unlikely(!sh7786_pcie_ports)) return -ENOMEM; diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index 33e351704f9f..63baa8aa9414 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -565,7 +565,8 @@ SYSCALL_DEFINE5(utrap_install, utrap_entry_t, type, } if (!current_thread_info()->utraps) { current_thread_info()->utraps = - kzalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long), GFP_KERNEL); + kcalloc(UT_TRAP_INSTRUCTION_31 + 1, sizeof(long), + GFP_KERNEL); if (!current_thread_info()->utraps) return -ENOMEM; current_thread_info()->utraps[0] = 1; diff --git a/arch/x86/events/amd/iommu.c b/arch/x86/events/amd/iommu.c index 38b5d41b0c37..3210fee27e7f 100644 --- a/arch/x86/events/amd/iommu.c +++ b/arch/x86/events/amd/iommu.c @@ -387,7 +387,7 @@ static __init int _init_events_attrs(void) while (amd_iommu_v2_event_descs[i].attr.attr.name) i++; - attrs = kzalloc(sizeof(struct attribute **) * (i + 1), GFP_KERNEL); + attrs = kcalloc(i + 1, sizeof(struct attribute **), GFP_KERNEL); if (!attrs) return -ENOMEM; diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c index e15cfad4f89b..27a461414b30 100644 --- a/arch/x86/events/intel/uncore.c +++ b/arch/x86/events/intel/uncore.c @@ -868,7 +868,7 @@ static int __init uncore_type_init(struct intel_uncore_type *type, bool setid) size_t size; int i, j; - pmus = kzalloc(sizeof(*pmus) * type->num_boxes, GFP_KERNEL); + pmus = kcalloc(type->num_boxes, sizeof(*pmus), GFP_KERNEL); if (!pmus) return -ENOMEM; diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index cd76380af79f..e4cf6ff1c2e1 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -1457,7 +1457,7 @@ static int __mcheck_cpu_mce_banks_init(void) int i; u8 num_banks = mca_cfg.banks; - mce_banks = kzalloc(num_banks * sizeof(struct mce_bank), GFP_KERNEL); + mce_banks = kcalloc(num_banks, sizeof(struct mce_bank), GFP_KERNEL); if (!mce_banks) return -ENOMEM; diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index f591b01930db..dd33c357548f 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -1384,7 +1384,7 @@ int mce_threshold_create_device(unsigned int cpu) if (bp) return 0; - bp = kzalloc(sizeof(struct threshold_bank *) * mca_cfg.banks, + bp = kcalloc(mca_cfg.banks, sizeof(struct threshold_bank *), GFP_KERNEL); if (!bp) return -ENOMEM; diff --git a/arch/x86/kernel/cpu/mtrr/if.c b/arch/x86/kernel/cpu/mtrr/if.c index c610f47373e4..4021d3859499 100644 --- a/arch/x86/kernel/cpu/mtrr/if.c +++ b/arch/x86/kernel/cpu/mtrr/if.c @@ -43,7 +43,7 @@ mtrr_file_add(unsigned long base, unsigned long size, max = num_var_ranges; if (fcount == NULL) { - fcount = kzalloc(max * sizeof *fcount, GFP_KERNEL); + fcount = kcalloc(max, sizeof(*fcount), GFP_KERNEL); if (!fcount) return -ENOMEM; FILE_FCOUNT(file) = fcount; diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index ddccdea0b63b..346b24883911 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -610,7 +610,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) if (!hpet_domain) return; - hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL); + hpet_devs = kcalloc(num_timers, sizeof(struct hpet_dev), GFP_KERNEL); if (!hpet_devs) return; diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 9542a746dc50..9112d1cb397b 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -168,7 +168,7 @@ static int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) if (type == PCI_CAP_ID_MSI && nvec > 1) return 1; - v = kzalloc(sizeof(int) * max(1, nvec), GFP_KERNEL); + v = kcalloc(max(1, nvec), sizeof(int), GFP_KERNEL); if (!v) return -ENOMEM; diff --git a/arch/x86/platform/uv/uv_time.c b/arch/x86/platform/uv/uv_time.c index b082d71b08ee..a36b368eea08 100644 --- a/arch/x86/platform/uv/uv_time.c +++ b/arch/x86/platform/uv/uv_time.c @@ -158,7 +158,7 @@ static __init int uv_rtc_allocate_timers(void) { int cpu; - blade_info = kzalloc(uv_possible_blades * sizeof(void *), GFP_KERNEL); + blade_info = kcalloc(uv_possible_blades, sizeof(void *), GFP_KERNEL); if (!blade_info) return -ENOMEM; diff --git a/block/bio.c b/block/bio.c index db9a40e9a136..9710e275f230 100644 --- a/block/bio.c +++ b/block/bio.c @@ -2091,7 +2091,8 @@ static int __init init_bio(void) { bio_slab_max = 2; bio_slab_nr = 0; - bio_slabs = kzalloc(bio_slab_max * sizeof(struct bio_slab), GFP_KERNEL); + bio_slabs = kcalloc(bio_slab_max, sizeof(struct bio_slab), + GFP_KERNEL); if (!bio_slabs) panic("bio: can't allocate bios\n"); diff --git a/block/blk-tag.c b/block/blk-tag.c index 09f19c6c52ce..24b20d86bcbc 100644 --- a/block/blk-tag.c +++ b/block/blk-tag.c @@ -99,12 +99,12 @@ init_tag_map(struct request_queue *q, struct blk_queue_tag *tags, int depth) __func__, depth); } - tag_index = kzalloc(depth * sizeof(struct request *), GFP_ATOMIC); + tag_index = kcalloc(depth, sizeof(struct request *), GFP_ATOMIC); if (!tag_index) goto fail; nr_ulongs = ALIGN(depth, BITS_PER_LONG) / BITS_PER_LONG; - tag_map = kzalloc(nr_ulongs * sizeof(unsigned long), GFP_ATOMIC); + tag_map = kcalloc(nr_ulongs, sizeof(unsigned long), GFP_ATOMIC); if (!tag_map) goto fail; diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 88cd949003f3..eaa60c94205a 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -82,7 +82,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, if (count < 0) { return NULL; } else if (count > 0) { - resources = kzalloc(count * sizeof(struct resource), + resources = kcalloc(count, sizeof(struct resource), GFP_KERNEL); if (!resources) { dev_err(&adev->dev, "No memory for resources\n"); diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 4fc59c3bc673..41324f0b1bee 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -857,12 +857,12 @@ void acpi_irq_stats_init(void) num_gpes = acpi_current_gpe_count; num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; - all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), + all_attrs = kcalloc(num_counters + 1, sizeof(struct attribute *), GFP_KERNEL); if (all_attrs == NULL) return; - all_counters = kzalloc(sizeof(struct event_counter) * (num_counters), + all_counters = kcalloc(num_counters, sizeof(struct event_counter), GFP_KERNEL); if (all_counters == NULL) goto fail; @@ -871,7 +871,7 @@ void acpi_irq_stats_init(void) if (ACPI_FAILURE(status)) goto fail; - counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), + counter_attrs = kcalloc(num_counters, sizeof(struct kobj_attribute), GFP_KERNEL); if (counter_attrs == NULL) goto fail; diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 4f382d51def1..2628806c64a2 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -692,8 +692,8 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, } } #endif - alloc->pages = kzalloc(sizeof(alloc->pages[0]) * - ((vma->vm_end - vma->vm_start) / PAGE_SIZE), + alloc->pages = kcalloc((vma->vm_end - vma->vm_start) / PAGE_SIZE, + sizeof(alloc->pages[0]), GFP_KERNEL); if (alloc->pages == NULL) { ret = -ENOMEM; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c41b9eeabe7c..27d15ed7fa3d 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6987,7 +6987,7 @@ static void __init ata_parse_force_param(void) if (*p == ',') size++; - ata_force_tbl = kzalloc(sizeof(ata_force_tbl[0]) * size, GFP_KERNEL); + ata_force_tbl = kcalloc(size, sizeof(ata_force_tbl[0]), GFP_KERNEL); if (!ata_force_tbl) { printk(KERN_WARNING "ata: failed to extend force table, " "libata.force ignored\n"); diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 85aa76116a30..2ae1799f4992 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -340,7 +340,7 @@ static int sata_pmp_init_links (struct ata_port *ap, int nr_ports) int i, err; if (!pmp_link) { - pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS, + pmp_link = kcalloc(SATA_PMP_MAX_PORTS, sizeof(pmp_link[0]), GFP_NOIO); if (!pmp_link) return -ENOMEM; diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index 6ebc4e4820fc..99a38115b0a8 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -2094,7 +2094,8 @@ static int fore200e_alloc_rx_buf(struct fore200e *fore200e) DPRINTK(2, "rx buffers %d / %d are being allocated\n", scheme, magn); /* allocate the array of receive buffers */ - buffer = bsq->buffer = kzalloc(nbr * sizeof(struct buffer), GFP_KERNEL); + buffer = bsq->buffer = kcalloc(nbr, sizeof(struct buffer), + GFP_KERNEL); if (buffer == NULL) return -ENOMEM; diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index be076606d30e..ff81a576347e 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -1618,7 +1618,7 @@ static int rx_init(struct atm_dev *dev) skb_queue_head_init(&iadev->rx_dma_q); iadev->rx_free_desc_qhead = NULL; - iadev->rx_open = kzalloc(4 * iadev->num_vc, GFP_KERNEL); + iadev->rx_open = kcalloc(4, iadev->num_vc, GFP_KERNEL); if (!iadev->rx_open) { printk(KERN_ERR DEV_LABEL "itf %d couldn't get free page\n", dev->number); diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 7655d6133139..a80809bd3057 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -511,7 +511,8 @@ static void drbd_calc_cpu_mask(cpumask_var_t *cpu_mask) { unsigned int *resources_per_cpu, min_index = ~0; - resources_per_cpu = kzalloc(nr_cpu_ids * sizeof(*resources_per_cpu), GFP_KERNEL); + resources_per_cpu = kcalloc(nr_cpu_ids, sizeof(*resources_per_cpu), + GFP_KERNEL); if (resources_per_cpu) { struct drbd_resource *resource; unsigned int cpu, min = ~0; diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 2bdadd7f1454..7948049f6c43 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -1575,12 +1575,12 @@ static int setup_commands(struct nullb_queue *nq) struct nullb_cmd *cmd; int i, tag_size; - nq->cmds = kzalloc(nq->queue_depth * sizeof(*cmd), GFP_KERNEL); + nq->cmds = kcalloc(nq->queue_depth, sizeof(*cmd), GFP_KERNEL); if (!nq->cmds) return -ENOMEM; tag_size = ALIGN(nq->queue_depth, BITS_PER_LONG) / BITS_PER_LONG; - nq->tag_map = kzalloc(tag_size * sizeof(unsigned long), GFP_KERNEL); + nq->tag_map = kcalloc(tag_size, sizeof(unsigned long), GFP_KERNEL); if (!nq->tag_map) { kfree(nq->cmds); return -ENOMEM; @@ -1598,8 +1598,9 @@ static int setup_commands(struct nullb_queue *nq) static int setup_queues(struct nullb *nullb) { - nullb->queues = kzalloc(nullb->dev->submit_queues * - sizeof(struct nullb_queue), GFP_KERNEL); + nullb->queues = kcalloc(nullb->dev->submit_queues, + sizeof(struct nullb_queue), + GFP_KERNEL); if (!nullb->queues) return -ENOMEM; diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index 8fa4533a1249..1e3d5de9d838 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -407,8 +407,9 @@ static int ps3vram_cache_init(struct ps3_system_bus_device *dev) priv->cache.page_count = CACHE_PAGE_COUNT; priv->cache.page_size = CACHE_PAGE_SIZE; - priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) * - CACHE_PAGE_COUNT, GFP_KERNEL); + priv->cache.tags = kcalloc(CACHE_PAGE_COUNT, + sizeof(struct ps3vram_tag), + GFP_KERNEL); if (!priv->cache.tags) return -ENOMEM; diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c index 09537bee387f..b7d71914a32a 100644 --- a/drivers/block/rsxx/core.c +++ b/drivers/block/rsxx/core.c @@ -873,7 +873,8 @@ static int rsxx_pci_probe(struct pci_dev *dev, dev_info(CARD_TO_DEV(card), "Failed reading the number of DMA targets\n"); - card->ctrl = kzalloc(card->n_targets * sizeof(*card->ctrl), GFP_KERNEL); + card->ctrl = kcalloc(card->n_targets, sizeof(*card->ctrl), + GFP_KERNEL); if (!card->ctrl) { st = -ENOMEM; goto failed_dma_setup; diff --git a/drivers/block/rsxx/dma.c b/drivers/block/rsxx/dma.c index beaccf197a5a..8fbc1bf6db3d 100644 --- a/drivers/block/rsxx/dma.c +++ b/drivers/block/rsxx/dma.c @@ -1038,7 +1038,7 @@ int rsxx_eeh_save_issued_dmas(struct rsxx_cardinfo *card) struct rsxx_dma *dma; struct list_head *issued_dmas; - issued_dmas = kzalloc(sizeof(*issued_dmas) * card->n_targets, + issued_dmas = kcalloc(card->n_targets, sizeof(*issued_dmas), GFP_KERNEL); if (!issued_dmas) return -ENOMEM; diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 66412eededda..a4bc74e72c39 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -139,7 +139,8 @@ static int xen_blkif_alloc_rings(struct xen_blkif *blkif) { unsigned int r; - blkif->rings = kzalloc(blkif->nr_rings * sizeof(struct xen_blkif_ring), GFP_KERNEL); + blkif->rings = kcalloc(blkif->nr_rings, sizeof(struct xen_blkif_ring), + GFP_KERNEL); if (!blkif->rings) return -ENOMEM; diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index ae00a82f350b..b5cedccb5d7d 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -1906,7 +1906,9 @@ static int negotiate_mq(struct blkfront_info *info) if (!info->nr_rings) info->nr_rings = 1; - info->rinfo = kzalloc(sizeof(struct blkfront_ring_info) * info->nr_rings, GFP_KERNEL); + info->rinfo = kcalloc(info->nr_rings, + sizeof(struct blkfront_ring_info), + GFP_KERNEL); if (!info->rinfo) { xenbus_dev_fatal(info->xbdev, -ENOMEM, "allocating ring_info structure"); return -ENOMEM; @@ -2216,15 +2218,18 @@ static int blkfront_setup_indirect(struct blkfront_ring_info *rinfo) } for (i = 0; i < BLK_RING_SIZE(info); i++) { - rinfo->shadow[i].grants_used = kzalloc( - sizeof(rinfo->shadow[i].grants_used[0]) * grants, - GFP_NOIO); - rinfo->shadow[i].sg = kzalloc(sizeof(rinfo->shadow[i].sg[0]) * psegs, GFP_NOIO); - if (info->max_indirect_segments) - rinfo->shadow[i].indirect_grants = kzalloc( - sizeof(rinfo->shadow[i].indirect_grants[0]) * - INDIRECT_GREFS(grants), + rinfo->shadow[i].grants_used = + kcalloc(grants, + sizeof(rinfo->shadow[i].grants_used[0]), GFP_NOIO); + rinfo->shadow[i].sg = kcalloc(psegs, + sizeof(rinfo->shadow[i].sg[0]), + GFP_NOIO); + if (info->max_indirect_segments) + rinfo->shadow[i].indirect_grants = + kcalloc(INDIRECT_GREFS(grants), + sizeof(rinfo->shadow[i].indirect_grants[0]), + GFP_NOIO); if ((rinfo->shadow[i].grants_used == NULL) || (rinfo->shadow[i].sg == NULL) || (info->max_indirect_segments && diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index b450544dcaf0..6914e4f0ce98 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -85,7 +85,8 @@ static int amd_create_gatt_pages(int nr_tables) int retval = 0; int i; - tables = kzalloc((nr_tables + 1) * sizeof(struct amd_page_map *),GFP_KERNEL); + tables = kcalloc(nr_tables + 1, sizeof(struct amd_page_map *), + GFP_KERNEL); if (tables == NULL) return -ENOMEM; diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 88b4cbee4dac..20bf5f78a362 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -108,7 +108,8 @@ static int ati_create_gatt_pages(int nr_tables) int retval = 0; int i; - tables = kzalloc((nr_tables + 1) * sizeof(struct ati_page_map *),GFP_KERNEL); + tables = kcalloc(nr_tables + 1, sizeof(struct ati_page_map *), + GFP_KERNEL); if (tables == NULL) return -ENOMEM; diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index 4dbdd3bc9bb8..7729414100ff 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -96,7 +96,7 @@ static int serverworks_create_gatt_pages(int nr_tables) int retval = 0; int i; - tables = kzalloc((nr_tables + 1) * sizeof(struct serverworks_page_map *), + tables = kcalloc(nr_tables + 1, sizeof(struct serverworks_page_map *), GFP_KERNEL); if (tables == NULL) return -ENOMEM; diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 22f634eb09fd..18e4650c233b 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -1757,7 +1757,8 @@ static unsigned short *ssif_address_list(void) list_for_each_entry(info, &ssif_infos, link) count++; - address_list = kzalloc(sizeof(*address_list) * (count + 1), GFP_KERNEL); + address_list = kcalloc(count + 1, sizeof(*address_list), + GFP_KERNEL); if (!address_list) return NULL; diff --git a/drivers/clk/renesas/clk-r8a7740.c b/drivers/clk/renesas/clk-r8a7740.c index d074f8e982d0..a7a30d2eca41 100644 --- a/drivers/clk/renesas/clk-r8a7740.c +++ b/drivers/clk/renesas/clk-r8a7740.c @@ -161,7 +161,7 @@ static void __init r8a7740_cpg_clocks_init(struct device_node *np) } cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); - clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); if (cpg == NULL || clks == NULL) { /* We're leaking memory on purpose, there's no point in cleaning * up as the system won't boot anyway. diff --git a/drivers/clk/renesas/clk-r8a7779.c b/drivers/clk/renesas/clk-r8a7779.c index 27fbfafaf2cd..5adcca4656c3 100644 --- a/drivers/clk/renesas/clk-r8a7779.c +++ b/drivers/clk/renesas/clk-r8a7779.c @@ -138,7 +138,7 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np) } cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); - clks = kzalloc(CPG_NUM_CLOCKS * sizeof(*clks), GFP_KERNEL); + clks = kcalloc(CPG_NUM_CLOCKS, sizeof(*clks), GFP_KERNEL); if (cpg == NULL || clks == NULL) { /* We're leaking memory on purpose, there's no point in cleaning * up as the system won't boot anyway. diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c index ee32a022e6da..bccd62f2cb09 100644 --- a/drivers/clk/renesas/clk-rcar-gen2.c +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -417,7 +417,7 @@ static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) } cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); - clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); if (cpg == NULL || clks == NULL) { /* We're leaking memory on purpose, there's no point in cleaning * up as the system won't boot anyway. diff --git a/drivers/clk/renesas/clk-rz.c b/drivers/clk/renesas/clk-rz.c index 67dd712aa723..ac2f86d626b6 100644 --- a/drivers/clk/renesas/clk-rz.c +++ b/drivers/clk/renesas/clk-rz.c @@ -97,7 +97,7 @@ static void __init rz_cpg_clocks_init(struct device_node *np) return; cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); - clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); BUG_ON(!cpg || !clks); cpg->data.clks = clks; diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index 14819d919df1..a79d81985c4e 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -874,7 +874,7 @@ static void __init st_of_create_quadfs_fsynths( return; clk_data->clk_num = QUADFS_MAX_CHAN; - clk_data->clks = kzalloc(QUADFS_MAX_CHAN * sizeof(struct clk *), + clk_data->clks = kcalloc(QUADFS_MAX_CHAN, sizeof(struct clk *), GFP_KERNEL); if (!clk_data->clks) { diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 25bda48a5d35..7a7106dc80bf 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -738,7 +738,7 @@ static void __init clkgen_c32_pll_setup(struct device_node *np, return; clk_data->clk_num = num_odfs; - clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), + clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), GFP_KERNEL); if (!clk_data->clks) diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c index fe0c3d169377..917fc27a33dd 100644 --- a/drivers/clk/sunxi/clk-usb.c +++ b/drivers/clk/sunxi/clk-usb.c @@ -122,7 +122,7 @@ static void __init sunxi_usb_clk_setup(struct device_node *node, if (!clk_data) return; - clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL); + clk_data->clks = kcalloc(qty + 1, sizeof(struct clk *), GFP_KERNEL); if (!clk_data->clks) { kfree(clk_data); return; diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 593d76a114f9..ffaf17f71860 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -216,14 +216,15 @@ struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks) if (WARN_ON(banks > ARRAY_SIZE(periph_regs))) return NULL; - periph_clk_enb_refcnt = kzalloc(32 * banks * - sizeof(*periph_clk_enb_refcnt), GFP_KERNEL); + periph_clk_enb_refcnt = kcalloc(32 * banks, + sizeof(*periph_clk_enb_refcnt), + GFP_KERNEL); if (!periph_clk_enb_refcnt) return NULL; periph_banks = banks; - clks = kzalloc(num * sizeof(struct clk *), GFP_KERNEL); + clks = kcalloc(num, sizeof(struct clk *), GFP_KERNEL); if (!clks) kfree(periph_clk_enb_refcnt); diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index 9498e9363b57..61c126a5d26a 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c @@ -206,7 +206,7 @@ static void __init of_dra7_apll_setup(struct device_node *node) goto cleanup; } - parent_names = kzalloc(sizeof(char *) * init->num_parents, GFP_KERNEL); + parent_names = kcalloc(init->num_parents, sizeof(char *), GFP_KERNEL); if (!parent_names) goto cleanup; diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index aaa277dd6d99..ccfb4d9a152a 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -366,7 +366,7 @@ int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div, num_dividers = i; - tmp = kzalloc(sizeof(*tmp) * (valid_div + 1), GFP_KERNEL); + tmp = kcalloc(valid_div + 1, sizeof(*tmp), GFP_KERNEL); if (!tmp) return -ENOMEM; @@ -496,7 +496,7 @@ __init ti_clk_get_div_table(struct device_node *node) return ERR_PTR(-EINVAL); } - table = kzalloc(sizeof(*table) * (valid_div + 1), GFP_KERNEL); + table = kcalloc(valid_div + 1, sizeof(*table), GFP_KERNEL); if (!table) return ERR_PTR(-ENOMEM); diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 7d33ca9042cb..dc86d07d0921 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -309,7 +309,7 @@ static void __init of_ti_dpll_setup(struct device_node *node, goto cleanup; } - parent_names = kzalloc(sizeof(char *) * init->num_parents, GFP_KERNEL); + parent_names = kcalloc(init->num_parents, sizeof(char *), GFP_KERNEL); if (!parent_names) goto cleanup; diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 70b3cf8e23d0..bbbf37c471a3 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -1000,7 +1000,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) /* Allocate and setup the channels. */ cmt->num_channels = hweight8(cmt->hw_channels); - cmt->channels = kzalloc(cmt->num_channels * sizeof(*cmt->channels), + cmt->channels = kcalloc(cmt->num_channels, sizeof(*cmt->channels), GFP_KERNEL); if (cmt->channels == NULL) { ret = -ENOMEM; diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 53aa7e92a7d7..6812e099b6a3 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -418,7 +418,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, /* Allocate and setup the channels. */ mtu->num_channels = 3; - mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels, + mtu->channels = kcalloc(mtu->num_channels, sizeof(*mtu->channels), GFP_KERNEL); if (mtu->channels == NULL) { ret = -ENOMEM; diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 31d881621e41..c74a6c543ca2 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -569,7 +569,7 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) } /* Allocate and setup the channels. */ - tmu->channels = kzalloc(sizeof(*tmu->channels) * tmu->num_channels, + tmu->channels = kcalloc(tmu->num_channels, sizeof(*tmu->channels), GFP_KERNEL); if (tmu->channels == NULL) { ret = -ENOMEM; diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 9449657d72f0..8ff1c9123834 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -759,8 +759,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) goto err_unreg; } - freq_table = kzalloc(sizeof(*freq_table) * - (perf->state_count+1), GFP_KERNEL); + freq_table = kcalloc(perf->state_count + 1, sizeof(*freq_table), + GFP_KERNEL); if (!freq_table) { result = -ENOMEM; goto err_unreg; diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 1d7ef5fc1977..cf62a1f64dd7 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -280,7 +280,7 @@ static int merge_cluster_tables(void) for (i = 0; i < MAX_CLUSTERS; i++) count += get_table_count(freq_table[i]); - table = kzalloc(sizeof(*table) * count, GFP_KERNEL); + table = kcalloc(count, sizeof(*table), GFP_KERNEL); if (!table) return -ENOMEM; diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 3464580ac3ca..a9d3eec32795 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -313,7 +313,8 @@ static int __init cppc_cpufreq_init(void) if (acpi_disabled) return -ENODEV; - all_cpu_data = kzalloc(sizeof(void *) * num_possible_cpus(), GFP_KERNEL); + all_cpu_data = kcalloc(num_possible_cpus(), sizeof(void *), + GFP_KERNEL); if (!all_cpu_data) return -ENOMEM; diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index 7974a2fdb760..dd5440d3372d 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -241,8 +241,8 @@ acpi_cpufreq_cpu_init ( } /* alloc freq_table */ - freq_table = kzalloc(sizeof(*freq_table) * - (data->acpi_data.state_count + 1), + freq_table = kcalloc(data->acpi_data.state_count + 1, + sizeof(*freq_table), GFP_KERNEL); if (!freq_table) { result = -ENOMEM; diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index 61a4c5b08219..279bd9e9fa95 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -474,8 +474,8 @@ static int longhaul_get_ranges(void) return -EINVAL; } - longhaul_table = kzalloc((numscales + 1) * sizeof(*longhaul_table), - GFP_KERNEL); + longhaul_table = kcalloc(numscales + 1, sizeof(*longhaul_table), + GFP_KERNEL); if (!longhaul_table) return -ENOMEM; diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index 7acc7fa4536d..9daa2cc318bb 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -93,7 +93,7 @@ static int setup_freqs_table(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table; int i; - table = kzalloc((num + 1) * sizeof(*table), GFP_KERNEL); + table = kcalloc(num + 1, sizeof(*table), GFP_KERNEL); if (table == NULL) return -ENOMEM; diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 909bd6e27639..3b291a2b0cb3 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -562,7 +562,7 @@ static int s3c_cpufreq_build_freq(void) size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0); size++; - ftab = kzalloc(sizeof(*ftab) * size, GFP_KERNEL); + ftab = kcalloc(size, sizeof(*ftab), GFP_KERNEL); if (!ftab) return -ENOMEM; diff --git a/drivers/cpufreq/sfi-cpufreq.c b/drivers/cpufreq/sfi-cpufreq.c index 9767afe05da2..978770432b13 100644 --- a/drivers/cpufreq/sfi-cpufreq.c +++ b/drivers/cpufreq/sfi-cpufreq.c @@ -95,8 +95,8 @@ static int __init sfi_cpufreq_init(void) if (ret) return ret; - freq_table = kzalloc(sizeof(*freq_table) * - (num_freq_table_entries + 1), GFP_KERNEL); + freq_table = kcalloc(num_freq_table_entries + 1, sizeof(*freq_table), + GFP_KERNEL); if (!freq_table) { ret = -ENOMEM; goto err_free_array; diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 195f27f9c1cb..4074e2615522 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -195,7 +195,7 @@ static int spear_cpufreq_probe(struct platform_device *pdev) cnt = prop->length / sizeof(u32); val = prop->value; - freq_tbl = kzalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL); + freq_tbl = kcalloc(cnt + 1, sizeof(*freq_tbl), GFP_KERNEL); if (!freq_tbl) { ret = -ENOMEM; goto out_put_node; diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index 9cb234c72549..05981ccd9901 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -141,11 +141,11 @@ static void crypto4xx_hw_init(struct crypto4xx_device *dev) int crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size) { - ctx->sa_in = kzalloc(size * 4, GFP_ATOMIC); + ctx->sa_in = kcalloc(size, 4, GFP_ATOMIC); if (ctx->sa_in == NULL) return -ENOMEM; - ctx->sa_out = kzalloc(size * 4, GFP_ATOMIC); + ctx->sa_out = kcalloc(size, 4, GFP_ATOMIC); if (ctx->sa_out == NULL) { kfree(ctx->sa_in); ctx->sa_in = NULL; @@ -180,8 +180,8 @@ static u32 crypto4xx_build_pdr(struct crypto4xx_device *dev) if (!dev->pdr) return -ENOMEM; - dev->pdr_uinfo = kzalloc(sizeof(struct pd_uinfo) * PPC4XX_NUM_PD, - GFP_KERNEL); + dev->pdr_uinfo = kcalloc(PPC4XX_NUM_PD, sizeof(struct pd_uinfo), + GFP_KERNEL); if (!dev->pdr_uinfo) { dma_free_coherent(dev->core_dev->device, sizeof(struct ce_pd) * PPC4XX_NUM_PD, diff --git a/drivers/crypto/inside-secure/safexcel_hash.c b/drivers/crypto/inside-secure/safexcel_hash.c index d138d6b8fec5..c77b0e1655a8 100644 --- a/drivers/crypto/inside-secure/safexcel_hash.c +++ b/drivers/crypto/inside-secure/safexcel_hash.c @@ -922,7 +922,7 @@ int safexcel_hmac_setkey(const char *alg, const u8 *key, unsigned int keylen, crypto_ahash_clear_flags(tfm, ~0); blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); - ipad = kzalloc(2 * blocksize, GFP_KERNEL); + ipad = kcalloc(2, blocksize, GFP_KERNEL); if (!ipad) { ret = -ENOMEM; goto free_request; diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c index e61b08566093..e34d80b6b7e5 100644 --- a/drivers/crypto/marvell/hash.c +++ b/drivers/crypto/marvell/hash.c @@ -1198,7 +1198,7 @@ static int mv_cesa_ahmac_setkey(const char *hash_alg_name, blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); - ipad = kzalloc(2 * blocksize, GFP_KERNEL); + ipad = kcalloc(2, blocksize, GFP_KERNEL); if (!ipad) { ret = -ENOMEM; goto free_req; diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c index 80e9c842aad4..ab6235b7ff22 100644 --- a/drivers/crypto/n2_core.c +++ b/drivers/crypto/n2_core.c @@ -1919,12 +1919,12 @@ static int grab_global_resources(void) goto out_hvapi_release; err = -ENOMEM; - cpu_to_cwq = kzalloc(sizeof(struct spu_queue *) * NR_CPUS, + cpu_to_cwq = kcalloc(NR_CPUS, sizeof(struct spu_queue *), GFP_KERNEL); if (!cpu_to_cwq) goto out_queue_cache_destroy; - cpu_to_mau = kzalloc(sizeof(struct spu_queue *) * NR_CPUS, + cpu_to_mau = kcalloc(NR_CPUS, sizeof(struct spu_queue *), GFP_KERNEL); if (!cpu_to_mau) goto out_free_cwq_table; diff --git a/drivers/crypto/qat/qat_common/qat_uclo.c b/drivers/crypto/qat/qat_common/qat_uclo.c index 98d22c2096e3..6bd8f6a2a24f 100644 --- a/drivers/crypto/qat/qat_common/qat_uclo.c +++ b/drivers/crypto/qat/qat_common/qat_uclo.c @@ -1162,8 +1162,9 @@ static int qat_uclo_map_suof(struct icp_qat_fw_loader_handle *handle, suof_handle->img_table.num_simgs = suof_ptr->num_chunks - 1; if (suof_handle->img_table.num_simgs != 0) { - suof_img_hdr = kzalloc(suof_handle->img_table.num_simgs * - sizeof(img_header), GFP_KERNEL); + suof_img_hdr = kcalloc(suof_handle->img_table.num_simgs, + sizeof(img_header), + GFP_KERNEL); if (!suof_img_hdr) return -ENOMEM; suof_handle->img_table.simg_hdr = suof_img_hdr; diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index 7792a9186f9c..4fa4c06c9edb 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -322,10 +322,10 @@ static int ioat_dma_self_test(struct ioatdma_device *ioat_dma) unsigned long tmo; unsigned long flags; - src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); + src = kzalloc(IOAT_TEST_SIZE, GFP_KERNEL); if (!src) return -ENOMEM; - dest = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); + dest = kzalloc(IOAT_TEST_SIZE, GFP_KERNEL); if (!dest) { kfree(src); return -ENOMEM; diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 4528b560dc4c..969534c1a6c6 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -781,7 +781,7 @@ static int mv_chan_memcpy_self_test(struct mv_xor_chan *mv_chan) if (!src) return -ENOMEM; - dest = kzalloc(sizeof(u8) * PAGE_SIZE, GFP_KERNEL); + dest = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!dest) { kfree(src); return -ENOMEM; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 6237069001c4..defcdde4d358 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1866,7 +1866,7 @@ static int dmac_alloc_threads(struct pl330_dmac *pl330) int i; /* Allocate 1 Manager and 'chans' Channel threads */ - pl330->channels = kzalloc((1 + chans) * sizeof(*thrd), + pl330->channels = kcalloc(1 + chans, sizeof(*thrd), GFP_KERNEL); if (!pl330->channels) return -ENOMEM; @@ -2990,7 +2990,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pl330->num_peripherals = num_chan; - pl330->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL); + pl330->peripherals = kcalloc(num_chan, sizeof(*pch), GFP_KERNEL); if (!pl330->peripherals) { ret = -ENOMEM; goto probe_err2; diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 12fa48e380cf..6b5626e299b2 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -1045,8 +1045,9 @@ EXPORT_SYMBOL(shdma_cleanup); static int __init shdma_enter(void) { - shdma_slave_used = kzalloc(DIV_ROUND_UP(slave_num, BITS_PER_LONG) * - sizeof(long), GFP_KERNEL); + shdma_slave_used = kcalloc(DIV_ROUND_UP(slave_num, BITS_PER_LONG), + sizeof(long), + GFP_KERNEL); if (!shdma_slave_used) return -ENOMEM; return 0; diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c index f14645817ed8..c74a88b65039 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c @@ -471,7 +471,7 @@ static int zynqmp_dma_alloc_chan_resources(struct dma_chan *dchan) if (ret < 0) return ret; - chan->sw_desc_pool = kzalloc(sizeof(*desc) * ZYNQMP_DMA_NUM_DESCS, + chan->sw_desc_pool = kcalloc(ZYNQMP_DMA_NUM_DESCS, sizeof(*desc), GFP_KERNEL); if (!chan->sw_desc_pool) return -ENOMEM; diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 329cb96f886f..18aeabb1d5ee 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -3451,7 +3451,7 @@ static int __init amd64_edac_init(void) opstate_init(); err = -ENOMEM; - ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL); + ecc_stngs = kcalloc(amd_nb_num(), sizeof(ecc_stngs[0]), GFP_KERNEL); if (!ecc_stngs) goto err_free; diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 4d0ea3563d47..8ed4dd9c571b 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -461,7 +461,7 @@ static struct i7core_dev *alloc_i7core_dev(u8 socket, if (!i7core_dev) return NULL; - i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs, + i7core_dev->pdev = kcalloc(table->n_devs, sizeof(*i7core_dev->pdev), GFP_KERNEL); if (!i7core_dev->pdev) { kfree(i7core_dev); diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 8bff5fd18185..af83ad58819c 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -1126,8 +1126,9 @@ int extcon_dev_register(struct extcon_dev *edev) char *str; struct extcon_cable *cable; - edev->cables = kzalloc(sizeof(struct extcon_cable) * - edev->max_supported, GFP_KERNEL); + edev->cables = kcalloc(edev->max_supported, + sizeof(struct extcon_cable), + GFP_KERNEL); if (!edev->cables) { ret = -ENOMEM; goto err_sysfs_alloc; @@ -1136,7 +1137,7 @@ int extcon_dev_register(struct extcon_dev *edev) cable = &edev->cables[index]; snprintf(buf, 10, "cable.%d", index); - str = kzalloc(sizeof(char) * (strlen(buf) + 1), + str = kzalloc(strlen(buf) + 1, GFP_KERNEL); if (!str) { for (index--; index >= 0; index--) { @@ -1177,15 +1178,17 @@ int extcon_dev_register(struct extcon_dev *edev) for (index = 0; edev->mutually_exclusive[index]; index++) ; - edev->attrs_muex = kzalloc(sizeof(struct attribute *) * - (index + 1), GFP_KERNEL); + edev->attrs_muex = kcalloc(index + 1, + sizeof(struct attribute *), + GFP_KERNEL); if (!edev->attrs_muex) { ret = -ENOMEM; goto err_muex; } - edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) * - index, GFP_KERNEL); + edev->d_attrs_muex = kcalloc(index, + sizeof(struct device_attribute), + GFP_KERNEL); if (!edev->d_attrs_muex) { ret = -ENOMEM; kfree(edev->attrs_muex); @@ -1194,7 +1197,7 @@ int extcon_dev_register(struct extcon_dev *edev) for (index = 0; edev->mutually_exclusive[index]; index++) { sprintf(buf, "0x%x", edev->mutually_exclusive[index]); - name = kzalloc(sizeof(char) * (strlen(buf) + 1), + name = kzalloc(strlen(buf) + 1, GFP_KERNEL); if (!name) { for (index--; index >= 0; index--) { @@ -1220,8 +1223,9 @@ int extcon_dev_register(struct extcon_dev *edev) if (edev->max_supported) { edev->extcon_dev_type.groups = - kzalloc(sizeof(struct attribute_group *) * - (edev->max_supported + 2), GFP_KERNEL); + kcalloc(edev->max_supported + 2, + sizeof(struct attribute_group *), + GFP_KERNEL); if (!edev->extcon_dev_type.groups) { ret = -ENOMEM; goto err_alloc_groups; diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c index 2f452f1f7c8a..fb8af5cb7c9b 100644 --- a/drivers/firmware/dell_rbu.c +++ b/drivers/firmware/dell_rbu.c @@ -146,7 +146,7 @@ static int create_packet(void *data, size_t length) packet_array_size = max( (unsigned int)(allocation_floor / rbu_data.packetsize), (unsigned int)1); - invalid_addr_packet_array = kzalloc(packet_array_size * sizeof(void*), + invalid_addr_packet_array = kcalloc(packet_array_size, sizeof(void *), GFP_KERNEL); if (!invalid_addr_packet_array) { diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c index 901b9306bf94..4938c29b7c5d 100644 --- a/drivers/firmware/efi/capsule.c +++ b/drivers/firmware/efi/capsule.c @@ -231,7 +231,7 @@ int efi_capsule_update(efi_capsule_header_t *capsule, phys_addr_t *pages) count = DIV_ROUND_UP(imagesize, PAGE_SIZE); sg_count = sg_pages_num(count); - sg_pages = kzalloc(sg_count * sizeof(*sg_pages), GFP_KERNEL); + sg_pages = kcalloc(sg_count, sizeof(*sg_pages), GFP_KERNEL); if (!sg_pages) return -ENOMEM; diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c index f377609ff141..84a11d0a8023 100644 --- a/drivers/firmware/efi/runtime-map.c +++ b/drivers/firmware/efi/runtime-map.c @@ -166,7 +166,7 @@ int __init efi_runtime_map_init(struct kobject *efi_kobj) if (!efi_enabled(EFI_MEMMAP)) return 0; - map_entries = kzalloc(efi.memmap.nr_map * sizeof(entry), GFP_KERNEL); + map_entries = kcalloc(efi.memmap.nr_map, sizeof(entry), GFP_KERNEL); if (!map_entries) { ret = -ENOMEM; goto out; diff --git a/drivers/fmc/fmc-sdb.c b/drivers/fmc/fmc-sdb.c index ffdc1762b580..d0e65b86dc22 100644 --- a/drivers/fmc/fmc-sdb.c +++ b/drivers/fmc/fmc-sdb.c @@ -48,8 +48,8 @@ static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc, arr = kzalloc(sizeof(*arr), GFP_KERNEL); if (!arr) return ERR_PTR(-ENOMEM); - arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL); - arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL); + arr->record = kcalloc(n, sizeof(arr->record[0]), GFP_KERNEL); + arr->subtree = kcalloc(n, sizeof(arr->subtree[0]), GFP_KERNEL); if (!arr->record || !arr->subtree) { kfree(arr->record); kfree(arr->subtree); diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index e2bee27eb526..b23d9a36be1f 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -443,7 +443,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, goto err_iomap; } - chip_save = kzalloc(sizeof(*chip) * 8, GFP_KERNEL); + chip_save = kcalloc(8, sizeof(*chip), GFP_KERNEL); if (chip_save == NULL) { ret = -ENOMEM; goto err_kzalloc; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 428e5eb3444f..f4c474a95875 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -310,20 +310,20 @@ static int acp_hw_init(void *handle) pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false); } - adev->acp.acp_cell = kzalloc(sizeof(struct mfd_cell) * ACP_DEVS, + adev->acp.acp_cell = kcalloc(ACP_DEVS, sizeof(struct mfd_cell), GFP_KERNEL); if (adev->acp.acp_cell == NULL) return -ENOMEM; - adev->acp.acp_res = kzalloc(sizeof(struct resource) * 4, GFP_KERNEL); + adev->acp.acp_res = kcalloc(4, sizeof(struct resource), GFP_KERNEL); if (adev->acp.acp_res == NULL) { kfree(adev->acp.acp_cell); return -ENOMEM; } - i2s_pdata = kzalloc(sizeof(struct i2s_platform_data) * 2, GFP_KERNEL); + i2s_pdata = kcalloc(2, sizeof(struct i2s_platform_data), GFP_KERNEL); if (i2s_pdata == NULL) { kfree(adev->acp.acp_res); kfree(adev->acp.acp_cell); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c index def1010ac05e..77ad59ade85c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c @@ -452,7 +452,7 @@ int amdgpu_parse_extended_power_table(struct amdgpu_device *adev) ATOM_PPLIB_PhaseSheddingLimits_Record *entry; adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries = - kzalloc(psl->ucNumEntries * + kcalloc(psl->ucNumEntries, sizeof(struct amdgpu_phase_shedding_limits_entry), GFP_KERNEL); if (!adev->pm.dpm.dyn_state.phase_shedding_limits_table.entries) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c index d167e8ab76d3..e3878256743a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_test.c @@ -53,7 +53,7 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev) n -= adev->irq.ih.ring_size; n /= size; - gtt_obj = kzalloc(n * sizeof(*gtt_obj), GFP_KERNEL); + gtt_obj = kcalloc(n, sizeof(*gtt_obj), GFP_KERNEL); if (!gtt_obj) { DRM_ERROR("Failed to allocate %d pointers\n", n); r = 1; diff --git a/drivers/gpu/drm/amd/amdgpu/atom.c b/drivers/gpu/drm/amd/amdgpu/atom.c index 69500a8b4e2d..e9934de1b9cf 100644 --- a/drivers/gpu/drm/amd/amdgpu/atom.c +++ b/drivers/gpu/drm/amd/amdgpu/atom.c @@ -1221,7 +1221,7 @@ static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, ectx.abort = false; ectx.last_jump = 0; if (ws) - ectx.ws = kzalloc(4 * ws, GFP_KERNEL); + ectx.ws = kcalloc(4, ws, GFP_KERNEL); else ectx.ws = NULL; diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c index a266dcf5daed..7fbad2f5f0bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c @@ -5679,8 +5679,9 @@ static int ci_parse_power_table(struct amdgpu_device *adev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - adev->pm.dpm.ps = kzalloc(sizeof(struct amdgpu_ps) * - state_array->ucNumEntries, GFP_KERNEL); + adev->pm.dpm.ps = kcalloc(state_array->ucNumEntries, + sizeof(struct amdgpu_ps), + GFP_KERNEL); if (!adev->pm.dpm.ps) return -ENOMEM; power_state_offset = (u8 *)state_array->states; @@ -5927,7 +5928,9 @@ static int ci_dpm_init(struct amdgpu_device *adev) ci_set_private_data_variables_based_on_pptable(adev); adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries = - kzalloc(4 * sizeof(struct amdgpu_clock_voltage_dependency_entry), GFP_KERNEL); + kcalloc(4, + sizeof(struct amdgpu_clock_voltage_dependency_entry), + GFP_KERNEL); if (!adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) { ci_dpm_fini(adev); return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c index 17f7f074cedc..7a1e77c93bf1 100644 --- a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c @@ -2727,8 +2727,9 @@ static int kv_parse_power_table(struct amdgpu_device *adev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - adev->pm.dpm.ps = kzalloc(sizeof(struct amdgpu_ps) * - state_array->ucNumEntries, GFP_KERNEL); + adev->pm.dpm.ps = kcalloc(state_array->ucNumEntries, + sizeof(struct amdgpu_ps), + GFP_KERNEL); if (!adev->pm.dpm.ps) return -ENOMEM; power_state_offset = (u8 *)state_array->states; diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c index b12d7c9d42a0..5c97a3671726 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c @@ -7242,8 +7242,9 @@ static int si_parse_power_table(struct amdgpu_device *adev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - adev->pm.dpm.ps = kzalloc(sizeof(struct amdgpu_ps) * - state_array->ucNumEntries, GFP_KERNEL); + adev->pm.dpm.ps = kcalloc(state_array->ucNumEntries, + sizeof(struct amdgpu_ps), + GFP_KERNEL); if (!adev->pm.dpm.ps) return -ENOMEM; power_state_offset = (u8 *)state_array->states; @@ -7346,7 +7347,9 @@ static int si_dpm_init(struct amdgpu_device *adev) return ret; adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries = - kzalloc(4 * sizeof(struct amdgpu_clock_voltage_dependency_entry), GFP_KERNEL); + kcalloc(4, + sizeof(struct amdgpu_clock_voltage_dependency_entry), + GFP_KERNEL); if (!adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) { amdgpu_free_extended_power_table(adev); return -ENOMEM; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index bd449351803f..ec304b1a5973 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -435,7 +435,7 @@ bool dm_helpers_submit_i2c( return false; } - msgs = kzalloc(num * sizeof(struct i2c_msg), GFP_KERNEL); + msgs = kcalloc(num, sizeof(struct i2c_msg), GFP_KERNEL); if (!msgs) return false; diff --git a/drivers/gpu/drm/amd/display/dc/basics/logger.c b/drivers/gpu/drm/amd/display/dc/basics/logger.c index 738a818d58d1..0866874ae8c6 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/logger.c +++ b/drivers/gpu/drm/amd/display/dc/basics/logger.c @@ -364,7 +364,7 @@ void dm_logger_open( entry->type = log_type; entry->logger = logger; - entry->buf = kzalloc(DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char), + entry->buf = kzalloc(DAL_LOGGER_BUFFER_MAX_SIZE, GFP_KERNEL); entry->buf_offset = 0; diff --git a/drivers/gpu/drm/amd/display/dc/basics/vector.c b/drivers/gpu/drm/amd/display/dc/basics/vector.c index 217b8f1f7bf6..d28e9cf0e961 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/vector.c +++ b/drivers/gpu/drm/amd/display/dc/basics/vector.c @@ -40,7 +40,7 @@ bool dal_vector_construct( return false; } - vector->container = kzalloc(struct_size * capacity, GFP_KERNEL); + vector->container = kcalloc(capacity, struct_size, GFP_KERNEL); if (vector->container == NULL) return false; vector->capacity = capacity; @@ -67,7 +67,7 @@ bool dal_vector_presized_costruct( return false; } - vector->container = kzalloc(struct_size * count, GFP_KERNEL); + vector->container = kcalloc(count, struct_size, GFP_KERNEL); if (vector->container == NULL) return false; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 599c7ab6befe..88b09dd758ba 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -1079,13 +1079,15 @@ static void get_ss_info_from_atombios( if (*ss_entries_num == 0) return; - ss_info = kzalloc(sizeof(struct spread_spectrum_info) * (*ss_entries_num), + ss_info = kcalloc(*ss_entries_num, + sizeof(struct spread_spectrum_info), GFP_KERNEL); ss_info_cur = ss_info; if (ss_info == NULL) return; - ss_data = kzalloc(sizeof(struct spread_spectrum_data) * (*ss_entries_num), + ss_data = kcalloc(*ss_entries_num, + sizeof(struct spread_spectrum_data), GFP_KERNEL); if (ss_data == NULL) goto out_free_info; diff --git a/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c b/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c index 80038e0e610f..ab5483c0c502 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c @@ -98,7 +98,8 @@ struct gpio_service *dal_gpio_service_create( if (number_of_bits) { uint32_t index_of_uint = 0; - slot = kzalloc(number_of_uints * sizeof(uint32_t), + slot = kcalloc(number_of_uints, + sizeof(uint32_t), GFP_KERNEL); if (!slot) { diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c index 0cd111d59018..2533274e9cef 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c @@ -1413,13 +1413,15 @@ bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf, output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; - rgb_user = kzalloc(sizeof(*rgb_user) * (GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS), - GFP_KERNEL); + rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS, + sizeof(*rgb_user), + GFP_KERNEL); if (!rgb_user) goto rgb_user_alloc_fail; - rgb_regamma = kzalloc(sizeof(*rgb_regamma) * (MAX_HW_POINTS + _EXTRA_POINTS), - GFP_KERNEL); + rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS, + sizeof(*rgb_regamma), + GFP_KERNEL); if (!rgb_regamma) goto rgb_regamma_alloc_fail; diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 27d4003aa2c7..fa344ceafc17 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -155,7 +155,8 @@ struct mod_freesync *mod_freesync_create(struct dc *dc) if (core_freesync == NULL) goto fail_alloc_context; - core_freesync->map = kzalloc(sizeof(struct freesync_entity) * MOD_FREESYNC_MAX_CONCURRENT_STREAMS, + core_freesync->map = kcalloc(MOD_FREESYNC_MAX_CONCURRENT_STREAMS, + sizeof(struct freesync_entity), GFP_KERNEL); if (core_freesync->map == NULL) diff --git a/drivers/gpu/drm/amd/display/modules/stats/stats.c b/drivers/gpu/drm/amd/display/modules/stats/stats.c index 3f7d47fdc367..710852ad03f3 100644 --- a/drivers/gpu/drm/amd/display/modules/stats/stats.c +++ b/drivers/gpu/drm/amd/display/modules/stats/stats.c @@ -141,19 +141,17 @@ struct mod_stats *mod_stats_create(struct dc *dc) else core_stats->entries = reg_data; } - core_stats->time = kzalloc( - sizeof(struct stats_time_cache) * - core_stats->entries, + core_stats->time = kcalloc(core_stats->entries, + sizeof(struct stats_time_cache), GFP_KERNEL); if (core_stats->time == NULL) goto fail_construct_time; core_stats->event_entries = DAL_STATS_EVENT_ENTRIES_DEFAULT; - core_stats->events = kzalloc( - sizeof(struct stats_event_cache) * - core_stats->event_entries, - GFP_KERNEL); + core_stats->events = kcalloc(core_stats->event_entries, + sizeof(struct stats_event_cache), + GFP_KERNEL); if (core_stats->events == NULL) goto fail_construct_events; diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c b/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c index 0af13c154328..e45a1fcc7f08 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/pp_psm.c @@ -50,7 +50,7 @@ int psm_init_power_state_table(struct pp_hwmgr *hwmgr) return 0; } - hwmgr->ps = kzalloc(size * table_entries, GFP_KERNEL); + hwmgr->ps = kcalloc(table_entries, size, GFP_KERNEL); if (hwmgr->ps == NULL) return -ENOMEM; diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 2e0a02a80fe4..572a18c2bfb5 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -121,7 +121,7 @@ int intel_gvt_init_vgpu_types(struct intel_gvt *gvt) high_avail = gvt_hidden_sz(gvt) - HOST_HIGH_GM_SIZE; num_types = sizeof(vgpu_types) / sizeof(vgpu_types[0]); - gvt->types = kzalloc(num_types * sizeof(struct intel_vgpu_type), + gvt->types = kcalloc(num_types, sizeof(struct intel_vgpu_type), GFP_KERNEL); if (!gvt->types) return -ENOMEM; diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 2db5da550a1c..0cc6a861bcf8 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -429,7 +429,7 @@ int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port, if (num_downstream == 0) return -EINVAL; - ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL); + ksv_fifo = kcalloc(DRM_HDCP_KSV_LEN, num_downstream, GFP_KERNEL); if (!ksv_fifo) return -ENOMEM; diff --git a/drivers/gpu/drm/i915/selftests/intel_uncore.c b/drivers/gpu/drm/i915/selftests/intel_uncore.c index f76f2597df5c..47bc5b2ddb56 100644 --- a/drivers/gpu/drm/i915/selftests/intel_uncore.c +++ b/drivers/gpu/drm/i915/selftests/intel_uncore.c @@ -137,7 +137,7 @@ static int intel_uncore_check_forcewake_domains(struct drm_i915_private *dev_pri if (!IS_ENABLED(CONFIG_DRM_I915_SELFTEST_BROKEN)) return 0; - valid = kzalloc(BITS_TO_LONGS(FW_RANGE) * sizeof(*valid), + valid = kcalloc(BITS_TO_LONGS(FW_RANGE), sizeof(*valid), GFP_KERNEL); if (!valid) return -ENOMEM; diff --git a/drivers/gpu/drm/nouveau/nvif/fifo.c b/drivers/gpu/drm/nouveau/nvif/fifo.c index 99d4fd17543c..e84a2e2ff043 100644 --- a/drivers/gpu/drm/nouveau/nvif/fifo.c +++ b/drivers/gpu/drm/nouveau/nvif/fifo.c @@ -50,8 +50,8 @@ nvif_fifo_runlists(struct nvif_device *device) goto done; device->runlists = fls64(a->v.runlists.data); - device->runlist = kzalloc(sizeof(*device->runlist) * - device->runlists, GFP_KERNEL); + device->runlist = kcalloc(device->runlists, sizeof(*device->runlist), + GFP_KERNEL); if (!device->runlist) { ret = -ENOMEM; goto done; diff --git a/drivers/gpu/drm/nouveau/nvif/object.c b/drivers/gpu/drm/nouveau/nvif/object.c index 40adfe9b334b..ef3f62840e83 100644 --- a/drivers/gpu/drm/nouveau/nvif/object.c +++ b/drivers/gpu/drm/nouveau/nvif/object.c @@ -83,7 +83,7 @@ nvif_object_sclass_get(struct nvif_object *object, struct nvif_sclass **psclass) return ret; } - *psclass = kzalloc(sizeof(**psclass) * args->sclass.count, GFP_KERNEL); + *psclass = kcalloc(args->sclass.count, sizeof(**psclass), GFP_KERNEL); if (*psclass) { for (i = 0; i < args->sclass.count; i++) { (*psclass)[i].oclass = args->sclass.oclass[i].oclass; diff --git a/drivers/gpu/drm/nouveau/nvkm/core/event.c b/drivers/gpu/drm/nouveau/nvkm/core/event.c index 4e8d3fa042df..006618d77aa4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/event.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/event.c @@ -84,7 +84,8 @@ int nvkm_event_init(const struct nvkm_event_func *func, int types_nr, int index_nr, struct nvkm_event *event) { - event->refs = kzalloc(sizeof(*event->refs) * index_nr * types_nr, + event->refs = kzalloc(array3_size(index_nr, types_nr, + sizeof(*event->refs)), GFP_KERNEL); if (!event->refs) return -ENOMEM; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c index a99046414a18..afccf9721cf0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c @@ -910,7 +910,7 @@ gk104_fifo_oneinit(struct nvkm_fifo *base) nvkm_debug(subdev, "%d PBDMA(s)\n", fifo->pbdma_nr); /* Read PBDMA->runlist(s) mapping from HW. */ - if (!(map = kzalloc(sizeof(*map) * fifo->pbdma_nr, GFP_KERNEL))) + if (!(map = kcalloc(fifo->pbdma_nr, sizeof(*map), GFP_KERNEL))) return -ENOMEM; for (i = 0; i < fifo->pbdma_nr; i++) diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 3ea716875151..17a53d207978 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -268,7 +268,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) } } } else { - addrs = kzalloc(npages * sizeof(*addrs), GFP_KERNEL); + addrs = kcalloc(npages, sizeof(*addrs), GFP_KERNEL); if (!addrs) { ret = -ENOMEM; goto free_pages; diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index 6a2e091aa7b6..e55cbeee7a53 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -1176,7 +1176,7 @@ static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32 ectx.abort = false; ectx.last_jump = 0; if (ws) - ectx.ws = kzalloc(4 * ws, GFP_KERNEL); + ectx.ws = kcalloc(4, ws, GFP_KERNEL); else ectx.ws = NULL; diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 95652e643da1..0aef4937c901 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -2581,7 +2581,9 @@ int btc_dpm_init(struct radeon_device *rdev) return ret; rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries = - kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL); + kcalloc(4, + sizeof(struct radeon_clock_voltage_dependency_entry), + GFP_KERNEL); if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) { r600_free_extended_power_table(rdev); return -ENOMEM; diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 7e1b04dc5593..b9302c918271 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -5568,8 +5568,9 @@ static int ci_parse_power_table(struct radeon_device *rdev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - state_array->ucNumEntries, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(state_array->ucNumEntries, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; power_state_offset = (u8 *)state_array->states; @@ -5770,7 +5771,9 @@ int ci_dpm_init(struct radeon_device *rdev) ci_set_private_data_variables_based_on_pptable(rdev); rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries = - kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL); + kcalloc(4, + sizeof(struct radeon_clock_voltage_dependency_entry), + GFP_KERNEL); if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) { ci_dpm_fini(rdev); return -ENOMEM; diff --git a/drivers/gpu/drm/radeon/kv_dpm.c b/drivers/gpu/drm/radeon/kv_dpm.c index ae1529b0ef6f..f055d6ea3522 100644 --- a/drivers/gpu/drm/radeon/kv_dpm.c +++ b/drivers/gpu/drm/radeon/kv_dpm.c @@ -2660,8 +2660,9 @@ static int kv_parse_power_table(struct radeon_device *rdev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - state_array->ucNumEntries, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(state_array->ucNumEntries, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; power_state_offset = (u8 *)state_array->states; diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 9416e72f86aa..0fd8d6ba9828 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -3998,8 +3998,9 @@ static int ni_parse_power_table(struct radeon_device *rdev) return -EINVAL; power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - power_info->pplib.ucNumStates, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(power_info->pplib.ucNumStates, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; @@ -4075,7 +4076,9 @@ int ni_dpm_init(struct radeon_device *rdev) return ret; rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries = - kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL); + kcalloc(4, + sizeof(struct radeon_clock_voltage_dependency_entry), + GFP_KERNEL); if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) { r600_free_extended_power_table(rdev); return -ENOMEM; diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index 31d1b4710844..73d4c5348116 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -991,7 +991,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev) ATOM_PPLIB_PhaseSheddingLimits_Record *entry; rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries = - kzalloc(psl->ucNumEntries * + kcalloc(psl->ucNumEntries, sizeof(struct radeon_phase_shedding_limits_entry), GFP_KERNEL); if (!rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries) { diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 4134759a6823..f422a8d6aec4 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -2126,13 +2126,16 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev) num_modes = ATOM_MAX_NUMBEROF_POWER_BLOCK; if (num_modes == 0) return state_index; - rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * num_modes, GFP_KERNEL); + rdev->pm.power_state = kcalloc(num_modes, + sizeof(struct radeon_power_state), + GFP_KERNEL); if (!rdev->pm.power_state) return state_index; /* last mode is usually default, array is low to high */ for (i = 0; i < num_modes; i++) { rdev->pm.power_state[state_index].clock_info = - kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL); + kcalloc(1, sizeof(struct radeon_pm_clock_info), + GFP_KERNEL); if (!rdev->pm.power_state[state_index].clock_info) return state_index; rdev->pm.power_state[state_index].num_clock_modes = 1; @@ -2587,8 +2590,9 @@ static int radeon_atombios_parse_power_table_4_5(struct radeon_device *rdev) radeon_atombios_add_pplib_thermal_controller(rdev, &power_info->pplib.sThermalController); if (power_info->pplib.ucNumStates == 0) return state_index; - rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * - power_info->pplib.ucNumStates, GFP_KERNEL); + rdev->pm.power_state = kcalloc(power_info->pplib.ucNumStates, + sizeof(struct radeon_power_state), + GFP_KERNEL); if (!rdev->pm.power_state) return state_index; /* first mode is usually default, followed by low to high */ @@ -2603,10 +2607,11 @@ static int radeon_atombios_parse_power_table_4_5(struct radeon_device *rdev) le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + (power_state->v1.ucNonClockStateIndex * power_info->pplib.ucNonClockSize)); - rdev->pm.power_state[i].clock_info = kzalloc(sizeof(struct radeon_pm_clock_info) * - ((power_info->pplib.ucStateEntrySize - 1) ? - (power_info->pplib.ucStateEntrySize - 1) : 1), - GFP_KERNEL); + rdev->pm.power_state[i].clock_info = + kcalloc((power_info->pplib.ucStateEntrySize - 1) ? + (power_info->pplib.ucStateEntrySize - 1) : 1, + sizeof(struct radeon_pm_clock_info), + GFP_KERNEL); if (!rdev->pm.power_state[i].clock_info) return state_index; if (power_info->pplib.ucStateEntrySize - 1) { @@ -2688,8 +2693,9 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev) le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); if (state_array->ucNumEntries == 0) return state_index; - rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * - state_array->ucNumEntries, GFP_KERNEL); + rdev->pm.power_state = kcalloc(state_array->ucNumEntries, + sizeof(struct radeon_power_state), + GFP_KERNEL); if (!rdev->pm.power_state) return state_index; power_state_offset = (u8 *)state_array->states; @@ -2699,10 +2705,11 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev) non_clock_array_index = power_state->v2.nonClockInfoIndex; non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) &non_clock_info_array->nonClockInfo[non_clock_array_index]; - rdev->pm.power_state[i].clock_info = kzalloc(sizeof(struct radeon_pm_clock_info) * - (power_state->v2.ucNumDPMLevels ? - power_state->v2.ucNumDPMLevels : 1), - GFP_KERNEL); + rdev->pm.power_state[i].clock_info = + kcalloc(power_state->v2.ucNumDPMLevels ? + power_state->v2.ucNumDPMLevels : 1, + sizeof(struct radeon_pm_clock_info), + GFP_KERNEL); if (!rdev->pm.power_state[i].clock_info) return state_index; if (power_state->v2.ucNumDPMLevels) { @@ -2782,7 +2789,9 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev) rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state), GFP_KERNEL); if (rdev->pm.power_state) { rdev->pm.power_state[0].clock_info = - kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL); + kcalloc(1, + sizeof(struct radeon_pm_clock_info), + GFP_KERNEL); if (rdev->pm.power_state[0].clock_info) { /* add the default mode */ rdev->pm.power_state[state_index].type = diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 3178ba0c537c..60a61d33f607 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -2642,13 +2642,16 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev) rdev->pm.default_power_state_index = -1; /* allocate 2 power states */ - rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) * 2, GFP_KERNEL); + rdev->pm.power_state = kcalloc(2, sizeof(struct radeon_power_state), + GFP_KERNEL); if (rdev->pm.power_state) { /* allocate 1 clock mode per state */ rdev->pm.power_state[0].clock_info = - kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL); + kcalloc(1, sizeof(struct radeon_pm_clock_info), + GFP_KERNEL); rdev->pm.power_state[1].clock_info = - kzalloc(sizeof(struct radeon_pm_clock_info) * 1, GFP_KERNEL); + kcalloc(1, sizeof(struct radeon_pm_clock_info), + GFP_KERNEL); if (!rdev->pm.power_state[0].clock_info || !rdev->pm.power_state[1].clock_info) goto pm_failed; diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index f5e9abfadb56..48f4b273e316 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -59,7 +59,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) n = rdev->mc.gtt_size - rdev->gart_pin_size; n /= size; - gtt_obj = kzalloc(n * sizeof(*gtt_obj), GFP_KERNEL); + gtt_obj = kcalloc(n, sizeof(*gtt_obj), GFP_KERNEL); if (!gtt_obj) { DRM_ERROR("Failed to allocate %d pointers\n", n); r = 1; diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c index b5e4e09a8996..694b7b3e9799 100644 --- a/drivers/gpu/drm/radeon/rs780_dpm.c +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -804,8 +804,9 @@ static int rs780_parse_power_table(struct radeon_device *rdev) return -EINVAL; power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - power_info->pplib.ucNumStates, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(power_info->pplib.ucNumStates, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c index d91aa3944593..6986051fbb89 100644 --- a/drivers/gpu/drm/radeon/rv6xx_dpm.c +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c @@ -1888,8 +1888,9 @@ static int rv6xx_parse_power_table(struct radeon_device *rdev) return -EINVAL; power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - power_info->pplib.ucNumStates, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(power_info->pplib.ucNumStates, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index cb2a7ec4e217..c765ae7ea806 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -2282,8 +2282,9 @@ int rv7xx_parse_power_table(struct radeon_device *rdev) return -EINVAL; power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - power_info->pplib.ucNumStates, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(power_info->pplib.ucNumStates, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 90d5b41007bf..fea88078cf8e 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -6832,8 +6832,9 @@ static int si_parse_power_table(struct radeon_device *rdev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - state_array->ucNumEntries, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(state_array->ucNumEntries, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; power_state_offset = (u8 *)state_array->states; @@ -6941,7 +6942,9 @@ int si_dpm_init(struct radeon_device *rdev) return ret; rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries = - kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL); + kcalloc(4, + sizeof(struct radeon_clock_voltage_dependency_entry), + GFP_KERNEL); if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) { r600_free_extended_power_table(rdev); return -ENOMEM; diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index fd4804829e46..1e4975f3374c 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -1482,8 +1482,9 @@ static int sumo_parse_power_table(struct radeon_device *rdev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - state_array->ucNumEntries, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(state_array->ucNumEntries, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; power_state_offset = (u8 *)state_array->states; diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 2ef7c4e5e495..5d317f763eea 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -1757,8 +1757,9 @@ static int trinity_parse_power_table(struct radeon_device *rdev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * - state_array->ucNumEntries, GFP_KERNEL); + rdev->pm.dpm.ps = kcalloc(state_array->ucNumEntries, + sizeof(struct radeon_ps), + GFP_KERNEL); if (!rdev->pm.dpm.ps) return -ENOMEM; power_state_offset = (u8 *)state_array->states; diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c index 7cc935d7b7aa..ab6c6c9c5b5c 100644 --- a/drivers/gpu/drm/selftests/test-drm_mm.c +++ b/drivers/gpu/drm/selftests/test-drm_mm.c @@ -1631,7 +1631,7 @@ static int igt_topdown(void *ignored) if (!nodes) goto err; - bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), + bitmap = kcalloc(count / BITS_PER_LONG, sizeof(unsigned long), GFP_KERNEL); if (!bitmap) goto err_nodes; @@ -1745,7 +1745,7 @@ static int igt_bottomup(void *ignored) if (!nodes) goto err; - bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), + bitmap = kcalloc(count / BITS_PER_LONG, sizeof(unsigned long), GFP_KERNEL); if (!bitmap) goto err_nodes; diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 6d99534ac691..8469b6964ff6 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -457,7 +457,7 @@ static char *resolv_usage_page(unsigned page, struct seq_file *f) { char *buf = NULL; if (!f) { - buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); + buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_ATOMIC); if (!buf) return ERR_PTR(-ENOMEM); } @@ -1088,7 +1088,7 @@ static int hid_debug_events_open(struct inode *inode, struct file *file) goto out; } - if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) { + if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) { err = -ENOMEM; kfree(list); goto out; diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 9b82549cbbc8..658dc765753b 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -190,7 +190,7 @@ int hv_synic_alloc(void) { int cpu; - hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids, + hv_context.hv_numa_map = kcalloc(nr_node_ids, sizeof(struct cpumask), GFP_KERNEL); if (hv_context.hv_numa_map == NULL) { pr_err("Unable to allocate NUMA map\n"); diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 3c836c099a8f..be3c8b10b84a 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -202,7 +202,7 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, * First page holds struct hv_ring_buffer, do wraparound mapping for * the rest. */ - pages_wraparound = kzalloc(sizeof(struct page *) * (page_cnt * 2 - 1), + pages_wraparound = kcalloc(page_cnt * 2 - 1, sizeof(struct page *), GFP_KERNEL); if (!pages_wraparound) return -ENOMEM; diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 14a94d90c028..34e45b97629e 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -575,8 +575,9 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource) if (!pss->package.count) goto end; - resource->domain_devices = kzalloc(sizeof(struct acpi_device *) * - pss->package.count, GFP_KERNEL); + resource->domain_devices = kcalloc(pss->package.count, + sizeof(struct acpi_device *), + GFP_KERNEL); if (!resource->domain_devices) { res = -ENOMEM; goto end; @@ -796,7 +797,7 @@ static int read_capabilities(struct acpi_power_meter_resource *resource) goto error; } - *str = kzalloc(sizeof(u8) * (element->string.length + 1), + *str = kcalloc(element->string.length + 1, sizeof(u8), GFP_KERNEL); if (!*str) { res = -ENOMEM; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 72c338eb5fae..10645c9bb7be 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -742,7 +742,7 @@ static int __init coretemp_init(void) return -ENODEV; max_packages = topology_max_packages(); - pkg_devices = kzalloc(max_packages * sizeof(struct platform_device *), + pkg_devices = kcalloc(max_packages, sizeof(struct platform_device *), GFP_KERNEL); if (!pkg_devices) return -ENOMEM; diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index 9397d2f0e79a..a4edc43dd060 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -274,8 +274,9 @@ static int i5k_amb_hwmon_init(struct platform_device *pdev) num_ambs += hweight16(data->amb_present[i] & 0x7fff); /* Set up sysfs stuff */ - data->attrs = kzalloc(sizeof(*data->attrs) * num_ambs * KNOBS_PER_AMB, - GFP_KERNEL); + data->attrs = kzalloc(array3_size(num_ambs, KNOBS_PER_AMB, + sizeof(*data->attrs)), + GFP_KERNEL); if (!data->attrs) return -ENOMEM; data->num_attrs = 0; diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 21b9c72f16bd..ab72cabf5a95 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -387,7 +387,7 @@ static int ibmpex_find_sensors(struct ibmpex_bmc_data *data) return -ENOENT; data->num_sensors = err; - data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors), + data->sensors = kcalloc(data->num_sensors, sizeof(*data->sensors), GFP_KERNEL); if (!data->sensors) return -ENOMEM; diff --git a/drivers/i2c/busses/i2c-amd756-s4882.c b/drivers/i2c/busses/i2c-amd756-s4882.c index 65e324054970..a2f5f992af7a 100644 --- a/drivers/i2c/busses/i2c-amd756-s4882.c +++ b/drivers/i2c/busses/i2c-amd756-s4882.c @@ -169,12 +169,12 @@ static int __init amd756_s4882_init(void) printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n"); /* Define the 5 virtual adapters and algorithms structures */ - if (!(s4882_adapter = kzalloc(5 * sizeof(struct i2c_adapter), + if (!(s4882_adapter = kcalloc(5, sizeof(struct i2c_adapter), GFP_KERNEL))) { error = -ENOMEM; goto ERROR1; } - if (!(s4882_algo = kzalloc(5 * sizeof(struct i2c_algorithm), + if (!(s4882_algo = kcalloc(5, sizeof(struct i2c_algorithm), GFP_KERNEL))) { error = -ENOMEM; goto ERROR2; diff --git a/drivers/i2c/busses/i2c-nforce2-s4985.c b/drivers/i2c/busses/i2c-nforce2-s4985.c index 88eda09e73c0..58a0fbf0e074 100644 --- a/drivers/i2c/busses/i2c-nforce2-s4985.c +++ b/drivers/i2c/busses/i2c-nforce2-s4985.c @@ -164,12 +164,12 @@ static int __init nforce2_s4985_init(void) printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4985\n"); /* Define the 5 virtual adapters and algorithms structures */ - s4985_adapter = kzalloc(5 * sizeof(struct i2c_adapter), GFP_KERNEL); + s4985_adapter = kcalloc(5, sizeof(struct i2c_adapter), GFP_KERNEL); if (!s4985_adapter) { error = -ENOMEM; goto ERROR1; } - s4985_algo = kzalloc(5 * sizeof(struct i2c_algorithm), GFP_KERNEL); + s4985_algo = kcalloc(5, sizeof(struct i2c_algorithm), GFP_KERNEL); if (!s4985_algo) { error = -ENOMEM; goto ERROR2; diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 3241bb9d6c18..f6a1272c5854 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -381,7 +381,7 @@ static int nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id) int res1, res2; /* we support 2 SMBus adapters */ - smbuses = kzalloc(2 * sizeof(struct nforce2_smbus), GFP_KERNEL); + smbuses = kcalloc(2, sizeof(struct nforce2_smbus), GFP_KERNEL); if (!smbuses) return -ENOMEM; pci_set_drvdata(dev, smbuses); diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c index 4a9ad91c5ba3..f31ec0861979 100644 --- a/drivers/i2c/i2c-stub.c +++ b/drivers/i2c/i2c-stub.c @@ -338,8 +338,9 @@ static int __init i2c_stub_allocate_banks(int i) chip->bank_mask >>= 1; } - chip->bank_words = kzalloc(chip->bank_mask * chip->bank_size * - sizeof(u16), GFP_KERNEL); + chip->bank_words = kcalloc(chip->bank_mask * chip->bank_size, + sizeof(u16), + GFP_KERNEL); if (!chip->bank_words) return -ENOMEM; diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c index 4b5dc0162e67..e52c58c29d9a 100644 --- a/drivers/ide/hpt366.c +++ b/drivers/ide/hpt366.c @@ -1455,7 +1455,7 @@ static int hpt366_init_one(struct pci_dev *dev, const struct pci_device_id *id) if (info == &hpt36x || info == &hpt374) dev2 = pci_get_slot(dev->bus, dev->devfn + 1); - dyn_info = kzalloc(sizeof(*dyn_info) * (dev2 ? 2 : 1), GFP_KERNEL); + dyn_info = kcalloc(dev2 ? 2 : 1, sizeof(*dyn_info), GFP_KERNEL); if (dyn_info == NULL) { printk(KERN_ERR "%s %s: out of memory!\n", d.name, pci_name(dev)); diff --git a/drivers/ide/it821x.c b/drivers/ide/it821x.c index 04029d18a696..36a64c8ea575 100644 --- a/drivers/ide/it821x.c +++ b/drivers/ide/it821x.c @@ -652,7 +652,7 @@ static int it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id) struct it821x_dev *itdevs; int rc; - itdevs = kzalloc(2 * sizeof(*itdevs), GFP_KERNEL); + itdevs = kcalloc(2, sizeof(*itdevs), GFP_KERNEL); if (itdevs == NULL) { printk(KERN_ERR DRV_NAME " %s: out of memory\n", pci_name(dev)); return -ENOMEM; diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 36607d52fee0..76643c5571aa 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -38,7 +38,7 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, if (!adis->xfer) return -ENOMEM; - adis->buffer = kzalloc(indio_dev->scan_bytes * 2, GFP_KERNEL); + adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL); if (!adis->buffer) return -ENOMEM; diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index ec98790e2a28..06ca3f7fcc44 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -436,7 +436,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev) } /* NULL terminated array to save passing size */ - chans = kzalloc(sizeof(*chans)*(nummaps + 1), GFP_KERNEL); + chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); if (chans == NULL) { ret = -ENOMEM; goto error_ret; diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index 71a34bee453d..81d66f56e38f 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -1245,8 +1245,9 @@ int ib_cache_setup_one(struct ib_device *device) rwlock_init(&device->cache.lock); device->cache.ports = - kzalloc(sizeof(*device->cache.ports) * - (rdma_end_port(device) - rdma_start_port(device) + 1), GFP_KERNEL); + kcalloc(rdma_end_port(device) - rdma_start_port(device) + 1, + sizeof(*device->cache.ports), + GFP_KERNEL); if (!device->cache.ports) return -ENOMEM; diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 84f51386e1e3..6fa4c59dc7a7 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -336,8 +336,8 @@ static int read_port_immutable(struct ib_device *device) * Therefore port_immutable is declared as a 1 based array with * potential empty slots at the beginning. */ - device->port_immutable = kzalloc(sizeof(*device->port_immutable) - * (end_port + 1), + device->port_immutable = kcalloc(end_port + 1, + sizeof(*device->port_immutable), GFP_KERNEL); if (!device->port_immutable) return -ENOMEM; diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index da12da1c36f6..cdb63f3f4de7 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -56,14 +56,16 @@ int iwpm_init(u8 nl_client) int ret = 0; mutex_lock(&iwpm_admin_lock); if (atomic_read(&iwpm_admin.refcount) == 0) { - iwpm_hash_bucket = kzalloc(IWPM_MAPINFO_HASH_SIZE * - sizeof(struct hlist_head), GFP_KERNEL); + iwpm_hash_bucket = kcalloc(IWPM_MAPINFO_HASH_SIZE, + sizeof(struct hlist_head), + GFP_KERNEL); if (!iwpm_hash_bucket) { ret = -ENOMEM; goto init_exit; } - iwpm_reminfo_bucket = kzalloc(IWPM_REMINFO_HASH_SIZE * - sizeof(struct hlist_head), GFP_KERNEL); + iwpm_reminfo_bucket = kcalloc(IWPM_REMINFO_HASH_SIZE, + sizeof(struct hlist_head), + GFP_KERNEL); if (!iwpm_reminfo_bucket) { kfree(iwpm_hash_bucket); ret = -ENOMEM; diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c index 3328acc53c2a..dcb4bba522ba 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.c +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c @@ -279,7 +279,7 @@ int cxio_create_qp(struct cxio_rdev *rdev_p, u32 kernel_domain, if (!wq->qpid) return -ENOMEM; - wq->rq = kzalloc(depth * sizeof(struct t3_swrq), GFP_KERNEL); + wq->rq = kcalloc(depth, sizeof(struct t3_swrq), GFP_KERNEL); if (!wq->rq) goto err1; @@ -287,7 +287,7 @@ int cxio_create_qp(struct cxio_rdev *rdev_p, u32 kernel_domain, if (!wq->rq_addr) goto err2; - wq->sq = kzalloc(depth * sizeof(struct t3_swsq), GFP_KERNEL); + wq->sq = kcalloc(depth, sizeof(struct t3_swsq), GFP_KERNEL); if (!wq->sq) goto err3; diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index 44161ca4d2a8..a3c3418afd73 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -859,8 +859,9 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev) rdev->status_page->cq_size = rdev->lldi.vr->cq.size; if (c4iw_wr_log) { - rdev->wr_log = kzalloc((1 << c4iw_wr_log_size_order) * - sizeof(*rdev->wr_log), GFP_KERNEL); + rdev->wr_log = kcalloc(1 << c4iw_wr_log_size_order, + sizeof(*rdev->wr_log), + GFP_KERNEL); if (rdev->wr_log) { rdev->wr_log_size = 1 << c4iw_wr_log_size_order; atomic_set(&rdev->wr_log_idx, 0); @@ -1445,7 +1446,7 @@ static void recover_queues(struct uld_ctx *ctx) ctx->dev->db_state = RECOVERY; idr_for_each(&ctx->dev->qpidr, count_qps, &count); - qp_list.qps = kzalloc(count * sizeof *qp_list.qps, GFP_ATOMIC); + qp_list.qps = kcalloc(count, sizeof(*qp_list.qps), GFP_ATOMIC); if (!qp_list.qps) { spin_unlock_irq(&ctx->dev->lock); return; diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 4106eed1b8fb..aef53305f1c3 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -216,15 +216,15 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, } if (!user) { - wq->sq.sw_sq = kzalloc(wq->sq.size * sizeof *wq->sq.sw_sq, - GFP_KERNEL); + wq->sq.sw_sq = kcalloc(wq->sq.size, sizeof(*wq->sq.sw_sq), + GFP_KERNEL); if (!wq->sq.sw_sq) { ret = -ENOMEM; goto free_rq_qid; } - wq->rq.sw_rq = kzalloc(wq->rq.size * sizeof *wq->rq.sw_rq, - GFP_KERNEL); + wq->rq.sw_rq = kcalloc(wq->rq.size, sizeof(*wq->rq.sw_rq), + GFP_KERNEL); if (!wq->rq.sw_rq) { ret = -ENOMEM; goto free_sw_sq; diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 0e8dad68910a..a6e11be0ea0f 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -3177,7 +3177,7 @@ static int hns_roce_v2_modify_qp(struct ib_qp *ibqp, struct device *dev = hr_dev->dev; int ret = -EINVAL; - context = kzalloc(2 * sizeof(*context), GFP_KERNEL); + context = kcalloc(2, sizeof(*context), GFP_KERNEL); if (!context) return -ENOMEM; diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index d604b3d5aa3e..90a3e2642c2e 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -1613,7 +1613,8 @@ static int mlx4_ib_alloc_pv_bufs(struct mlx4_ib_demux_pv_ctx *ctx, tun_qp = &ctx->qp[qp_type]; - tun_qp->ring = kzalloc(sizeof (struct mlx4_ib_buf) * MLX4_NUM_TUNNEL_BUFS, + tun_qp->ring = kcalloc(MLX4_NUM_TUNNEL_BUFS, + sizeof(struct mlx4_ib_buf), GFP_KERNEL); if (!tun_qp->ring) return -ENOMEM; diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c index dc3c2346045c..6686042aafb4 100644 --- a/drivers/infiniband/hw/mthca/mthca_mr.c +++ b/drivers/infiniband/hw/mthca/mthca_mr.c @@ -144,7 +144,7 @@ static int mthca_buddy_init(struct mthca_buddy *buddy, int max_order) buddy->max_order = max_order; spin_lock_init(&buddy->lock); - buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *), + buddy->bits = kcalloc(buddy->max_order + 1, sizeof(long *), GFP_KERNEL); buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free, GFP_KERNEL); diff --git a/drivers/infiniband/hw/mthca/mthca_profile.c b/drivers/infiniband/hw/mthca/mthca_profile.c index 15d064479ef6..7ea970774839 100644 --- a/drivers/infiniband/hw/mthca/mthca_profile.c +++ b/drivers/infiniband/hw/mthca/mthca_profile.c @@ -79,7 +79,7 @@ s64 mthca_make_profile(struct mthca_dev *dev, struct mthca_resource *profile; int i, j; - profile = kzalloc(MTHCA_RES_NUM * sizeof *profile, GFP_KERNEL); + profile = kcalloc(MTHCA_RES_NUM, sizeof(*profile), GFP_KERNEL); if (!profile) return -ENOMEM; diff --git a/drivers/infiniband/hw/nes/nes_mgt.c b/drivers/infiniband/hw/nes/nes_mgt.c index 21e0ebd39a05..9bdb84dc225c 100644 --- a/drivers/infiniband/hw/nes/nes_mgt.c +++ b/drivers/infiniband/hw/nes/nes_mgt.c @@ -878,7 +878,8 @@ int nes_init_mgt_qp(struct nes_device *nesdev, struct net_device *netdev, struct int ret; /* Allocate space the all mgt QPs once */ - mgtvnic = kzalloc(NES_MGT_QP_COUNT * sizeof(struct nes_vnic_mgt), GFP_KERNEL); + mgtvnic = kcalloc(NES_MGT_QP_COUNT, sizeof(struct nes_vnic_mgt), + GFP_KERNEL); if (!mgtvnic) return -ENOMEM; diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 1040a6e34230..32f26556c808 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -2254,8 +2254,9 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, ibmr = ERR_PTR(-ENOMEM); goto reg_user_mr_err; } - root_vpbl.leaf_vpbl = kzalloc(sizeof(*root_vpbl.leaf_vpbl)*1024, - GFP_KERNEL); + root_vpbl.leaf_vpbl = kcalloc(1024, + sizeof(*root_vpbl.leaf_vpbl), + GFP_KERNEL); if (!root_vpbl.leaf_vpbl) { ib_umem_release(region); pci_free_consistent(nesdev->pcidev, 8192, root_vpbl.pbl_vbase, diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c index 2c260e1c29d1..6c136e5017fe 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c @@ -3096,7 +3096,7 @@ static int ocrdma_create_eqs(struct ocrdma_dev *dev) if (!num_eq) return -EINVAL; - dev->eq_tbl = kzalloc(sizeof(struct ocrdma_eq) * num_eq, GFP_KERNEL); + dev->eq_tbl = kcalloc(num_eq, sizeof(struct ocrdma_eq), GFP_KERNEL); if (!dev->eq_tbl) return -ENOMEM; diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index eb8b6a935016..5962c0ed9847 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -221,19 +221,20 @@ static int ocrdma_register_device(struct ocrdma_dev *dev) static int ocrdma_alloc_resources(struct ocrdma_dev *dev) { mutex_init(&dev->dev_lock); - dev->cq_tbl = kzalloc(sizeof(struct ocrdma_cq *) * - OCRDMA_MAX_CQ, GFP_KERNEL); + dev->cq_tbl = kcalloc(OCRDMA_MAX_CQ, sizeof(struct ocrdma_cq *), + GFP_KERNEL); if (!dev->cq_tbl) goto alloc_err; if (dev->attr.max_qp) { - dev->qp_tbl = kzalloc(sizeof(struct ocrdma_qp *) * - OCRDMA_MAX_QP, GFP_KERNEL); + dev->qp_tbl = kcalloc(OCRDMA_MAX_QP, + sizeof(struct ocrdma_qp *), + GFP_KERNEL); if (!dev->qp_tbl) goto alloc_err; } - dev->stag_arr = kzalloc(sizeof(u64) * OCRDMA_MAX_STAG, GFP_KERNEL); + dev->stag_arr = kcalloc(OCRDMA_MAX_STAG, sizeof(u64), GFP_KERNEL); if (dev->stag_arr == NULL) goto alloc_err; diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index eb9f9e9e213b..82e20fc32890 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -843,8 +843,8 @@ static int ocrdma_build_pbl_tbl(struct ocrdma_dev *dev, struct ocrdma_hw_mr *mr) void *va; dma_addr_t pa; - mr->pbl_table = kzalloc(sizeof(struct ocrdma_pbl) * - mr->num_pbls, GFP_KERNEL); + mr->pbl_table = kcalloc(mr->num_pbls, sizeof(struct ocrdma_pbl), + GFP_KERNEL); if (!mr->pbl_table) return -ENOMEM; @@ -1323,12 +1323,12 @@ static void ocrdma_set_qp_db(struct ocrdma_dev *dev, struct ocrdma_qp *qp, static int ocrdma_alloc_wr_id_tbl(struct ocrdma_qp *qp) { qp->wqe_wr_id_tbl = - kzalloc(sizeof(*(qp->wqe_wr_id_tbl)) * qp->sq.max_cnt, + kcalloc(qp->sq.max_cnt, sizeof(*(qp->wqe_wr_id_tbl)), GFP_KERNEL); if (qp->wqe_wr_id_tbl == NULL) return -ENOMEM; qp->rqe_wr_id_tbl = - kzalloc(sizeof(u64) * qp->rq.max_cnt, GFP_KERNEL); + kcalloc(qp->rq.max_cnt, sizeof(u64), GFP_KERNEL); if (qp->rqe_wr_id_tbl == NULL) return -ENOMEM; @@ -1865,8 +1865,8 @@ struct ib_srq *ocrdma_create_srq(struct ib_pd *ibpd, if (udata == NULL) { status = -ENOMEM; - srq->rqe_wr_id_tbl = kzalloc(sizeof(u64) * srq->rq.max_cnt, - GFP_KERNEL); + srq->rqe_wr_id_tbl = kcalloc(srq->rq.max_cnt, sizeof(u64), + GFP_KERNEL); if (srq->rqe_wr_id_tbl == NULL) goto arm_err; diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index f4cb60b658ea..ad22b32bbd9c 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -317,8 +317,8 @@ static int qedr_alloc_resources(struct qedr_dev *dev) u16 n_entries; int i, rc; - dev->sgid_tbl = kzalloc(sizeof(union ib_gid) * - QEDR_MAX_SGID, GFP_KERNEL); + dev->sgid_tbl = kcalloc(QEDR_MAX_SGID, sizeof(union ib_gid), + GFP_KERNEL); if (!dev->sgid_tbl) return -ENOMEM; diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 710032f1fad7..f7ac8fc9b531 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -1614,7 +1614,7 @@ static int qedr_create_kernel_qp(struct qedr_dev *dev, qp->sq.max_wr = min_t(u32, attrs->cap.max_send_wr * dev->wq_multiplier, dev->attr.max_sqe); - qp->wqe_wr_id = kzalloc(qp->sq.max_wr * sizeof(*qp->wqe_wr_id), + qp->wqe_wr_id = kcalloc(qp->sq.max_wr, sizeof(*qp->wqe_wr_id), GFP_KERNEL); if (!qp->wqe_wr_id) { DP_ERR(dev, "create qp: failed SQ shadow memory allocation\n"); @@ -1632,7 +1632,7 @@ static int qedr_create_kernel_qp(struct qedr_dev *dev, qp->rq.max_wr = (u16) max_t(u32, attrs->cap.max_recv_wr, 1); /* Allocate driver internal RQ array */ - qp->rqe_wr_id = kzalloc(qp->rq.max_wr * sizeof(*qp->rqe_wr_id), + qp->rqe_wr_id = kcalloc(qp->rq.max_wr, sizeof(*qp->rqe_wr_id), GFP_KERNEL); if (!qp->rqe_wr_id) { DP_ERR(dev, diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index 27155d92f810..bf5e222eed8e 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -7295,8 +7295,9 @@ struct qib_devdata *qib_init_iba7322_funcs(struct pci_dev *pdev, actual_cnt -= dd->num_pports; tabsize = actual_cnt; - dd->cspec->msix_entries = kzalloc(tabsize * - sizeof(struct qib_msix_entry), GFP_KERNEL); + dd->cspec->msix_entries = kcalloc(tabsize, + sizeof(struct qib_msix_entry), + GFP_KERNEL); if (!dd->cspec->msix_entries) tabsize = 0; diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c index 015520289735..dd4547f537f7 100644 --- a/drivers/infiniband/hw/qib/qib_init.c +++ b/drivers/infiniband/hw/qib/qib_init.c @@ -1134,8 +1134,8 @@ struct qib_devdata *qib_alloc_devdata(struct pci_dev *pdev, size_t extra) if (!qib_cpulist_count) { u32 count = num_online_cpus(); - qib_cpulist = kzalloc(BITS_TO_LONGS(count) * - sizeof(long), GFP_KERNEL); + qib_cpulist = kcalloc(BITS_TO_LONGS(count), sizeof(long), + GFP_KERNEL); if (qib_cpulist) qib_cpulist_count = count; } diff --git a/drivers/infiniband/hw/usnic/usnic_ib_qp_grp.c b/drivers/infiniband/hw/usnic/usnic_ib_qp_grp.c index 912d8ef04352..bf5136533d49 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_qp_grp.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_qp_grp.c @@ -543,7 +543,7 @@ alloc_res_chunk_list(struct usnic_vnic *vnic, /* Do Nothing */ } - res_chunk_list = kzalloc(sizeof(*res_chunk_list)*(res_lst_sz+1), + res_chunk_list = kcalloc(res_lst_sz + 1, sizeof(*res_chunk_list), GFP_ATOMIC); if (!res_chunk_list) return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/usnic/usnic_vnic.c b/drivers/infiniband/hw/usnic/usnic_vnic.c index e7b0030254da..ebe08f348453 100644 --- a/drivers/infiniband/hw/usnic/usnic_vnic.c +++ b/drivers/infiniband/hw/usnic/usnic_vnic.c @@ -312,7 +312,7 @@ static int usnic_vnic_alloc_res_chunk(struct usnic_vnic *vnic, } chunk->cnt = chunk->free_cnt = cnt; - chunk->res = kzalloc(sizeof(*(chunk->res))*cnt, GFP_KERNEL); + chunk->res = kcalloc(cnt, sizeof(*(chunk->res)), GFP_KERNEL); if (!chunk->res) return -ENOMEM; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 2ce40a7ff604..0d74c807110e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1526,7 +1526,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv) return -ENOMEM; set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); size = roundup_pow_of_two(arp_tbl.gc_thresh3); - buckets = kzalloc(size * sizeof(*buckets), GFP_KERNEL); + buckets = kcalloc(size, sizeof(*buckets), GFP_KERNEL); if (!buckets) { kfree(htbl); return -ENOMEM; @@ -1704,8 +1704,9 @@ static int ipoib_dev_init_default(struct net_device *dev) ipoib_napi_add(dev); /* Allocate RX/TX "rings" to hold queued skbs */ - priv->rx_ring = kzalloc(ipoib_recvq_size * sizeof *priv->rx_ring, - GFP_KERNEL); + priv->rx_ring = kcalloc(ipoib_recvq_size, + sizeof(*priv->rx_ring), + GFP_KERNEL); if (!priv->rx_ring) goto out; diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index f2f9318e1f49..cccbcf0eb035 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -181,8 +181,9 @@ isert_alloc_rx_descriptors(struct isert_conn *isert_conn) u64 dma_addr; int i, j; - isert_conn->rx_descs = kzalloc(ISERT_QP_MAX_RECV_DTOS * - sizeof(struct iser_rx_desc), GFP_KERNEL); + isert_conn->rx_descs = kcalloc(ISERT_QP_MAX_RECV_DTOS, + sizeof(struct iser_rx_desc), + GFP_KERNEL); if (!isert_conn->rx_descs) return -ENOMEM; diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 940d38b08e6b..46406345742b 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -337,7 +337,8 @@ static int omap4_keypad_probe(struct platform_device *pdev) keypad_data->row_shift = get_count_order(keypad_data->cols); max_keys = keypad_data->rows << keypad_data->row_shift; - keypad_data->keymap = kzalloc(max_keys * sizeof(keypad_data->keymap[0]), + keypad_data->keymap = kcalloc(max_keys, + sizeof(keypad_data->keymap[0]), GFP_KERNEL); if (!keypad_data->keymap) { dev_err(&pdev->dev, "Not enough memory for keymap\n"); diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 4321f7704b23..75456b5aa825 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1458,7 +1458,7 @@ int dmar_enable_qi(struct intel_iommu *iommu) qi->desc = page_address(desc_page); - qi->desc_status = kzalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC); + qi->desc_status = kcalloc(QI_LENGTH, sizeof(int), GFP_ATOMIC); if (!qi->desc_status) { free_page((unsigned long) qi->desc); kfree(qi); diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 89e49a429c57..14e4b3722428 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3189,7 +3189,7 @@ static int copy_translation_tables(struct intel_iommu *iommu) /* This is too big for the stack - allocate it from slab */ ctxt_table_entries = ext ? 512 : 256; ret = -ENOMEM; - ctxt_tbls = kzalloc(ctxt_table_entries * sizeof(void *), GFP_KERNEL); + ctxt_tbls = kcalloc(ctxt_table_entries, sizeof(void *), GFP_KERNEL); if (!ctxt_tbls) goto out_unmap; @@ -4032,7 +4032,7 @@ static int iommu_suspend(void) unsigned long flag; for_each_active_iommu(iommu, drhd) { - iommu->iommu_state = kzalloc(sizeof(u32) * MAX_SR_DMAR_REGS, + iommu->iommu_state = kcalloc(MAX_SR_DMAR_REGS, sizeof(u32), GFP_ATOMIC); if (!iommu->iommu_state) goto nomem; diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index c33b7b104e72..af4a8e7fcd27 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -1455,7 +1455,7 @@ static int omap_iommu_add_device(struct device *dev) if (num_iommus < 0) return 0; - arch_data = kzalloc((num_iommus + 1) * sizeof(*arch_data), GFP_KERNEL); + arch_data = kcalloc(num_iommus + 1, sizeof(*arch_data), GFP_KERNEL); if (!arch_data) return -ENOMEM; diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c index 9b23843dcad4..a16b320739b4 100644 --- a/drivers/ipack/carriers/tpci200.c +++ b/drivers/ipack/carriers/tpci200.c @@ -457,8 +457,8 @@ static int tpci200_install(struct tpci200_board *tpci200) { int res; - tpci200->slots = kzalloc( - TPCI200_NB_SLOT * sizeof(struct tpci200_slot), GFP_KERNEL); + tpci200->slots = kcalloc(TPCI200_NB_SLOT, sizeof(struct tpci200_slot), + GFP_KERNEL); if (tpci200->slots == NULL) return -ENOMEM; diff --git a/drivers/irqchip/irq-alpine-msi.c b/drivers/irqchip/irq-alpine-msi.c index 63d980995d17..23a3b877f7f1 100644 --- a/drivers/irqchip/irq-alpine-msi.c +++ b/drivers/irqchip/irq-alpine-msi.c @@ -268,7 +268,8 @@ static int alpine_msix_init(struct device_node *node, goto err_priv; } - priv->msi_map = kzalloc(sizeof(*priv->msi_map) * BITS_TO_LONGS(priv->num_spis), + priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_spis), + sizeof(*priv->msi_map), GFP_KERNEL); if (!priv->msi_map) { ret = -ENOMEM; diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 1ff38aff9f29..0f52d44b3f69 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -361,7 +361,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, break; } - v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis), + v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long), GFP_KERNEL); if (!v2m->bm) { ret = -ENOMEM; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 4e7ce74e558d..5377d7e2afba 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1239,7 +1239,7 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) if (!its_dev->event_map.vm) { struct its_vlpi_map *maps; - maps = kzalloc(sizeof(*maps) * its_dev->event_map.nr_lpis, + maps = kcalloc(its_dev->event_map.nr_lpis, sizeof(*maps), GFP_KERNEL); if (!maps) { ret = -ENOMEM; @@ -1437,7 +1437,7 @@ static int __init its_lpi_init(u32 id_bits) { lpi_chunks = its_lpi_to_chunk(1UL << id_bits); - lpi_bitmap = kzalloc(BITS_TO_LONGS(lpi_chunks) * sizeof(long), + lpi_bitmap = kcalloc(BITS_TO_LONGS(lpi_chunks), sizeof(long), GFP_KERNEL); if (!lpi_bitmap) { lpi_chunks = 0; @@ -1471,7 +1471,8 @@ static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids) if (!nr_chunks) goto out; - bitmap = kzalloc(BITS_TO_LONGS(nr_chunks * IRQS_PER_CHUNK) * sizeof (long), + bitmap = kcalloc(BITS_TO_LONGS(nr_chunks * IRQS_PER_CHUNK), + sizeof(long), GFP_ATOMIC); if (!bitmap) goto out; @@ -1823,7 +1824,7 @@ static int its_alloc_tables(struct its_node *its) static int its_alloc_collections(struct its_node *its) { - its->collections = kzalloc(nr_cpu_ids * sizeof(*its->collections), + its->collections = kcalloc(nr_cpu_ids, sizeof(*its->collections), GFP_KERNEL); if (!its->collections) return -ENOMEM; @@ -2124,10 +2125,10 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, if (alloc_lpis) { lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis); if (lpi_map) - col_map = kzalloc(sizeof(*col_map) * nr_lpis, + col_map = kcalloc(nr_lpis, sizeof(*col_map), GFP_KERNEL); } else { - col_map = kzalloc(sizeof(*col_map) * nr_ites, GFP_KERNEL); + col_map = kcalloc(nr_ites, sizeof(*col_map), GFP_KERNEL); nr_lpis = 0; lpi_base = 0; } @@ -3183,7 +3184,7 @@ static int its_init_vpe_domain(void) its = list_first_entry(&its_nodes, struct its_node, entry); entries = roundup_pow_of_two(nr_cpu_ids); - vpe_proxy.vpes = kzalloc(sizeof(*vpe_proxy.vpes) * entries, + vpe_proxy.vpes = kcalloc(entries, sizeof(*vpe_proxy.vpes), GFP_KERNEL); if (!vpe_proxy.vpes) { pr_err("ITS: Can't allocate GICv4 proxy device array\n"); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 5a67ec084588..76ea56d779a1 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1167,7 +1167,7 @@ static void __init gic_populate_ppi_partitions(struct device_node *gic_node) if (!nr_parts) goto out_put_node; - parts = kzalloc(sizeof(*parts) * nr_parts, GFP_KERNEL); + parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); if (WARN_ON(!parts)) goto out_put_node; @@ -1289,7 +1289,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions)) nr_redist_regions = 1; - rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL); + rdist_regs = kcalloc(nr_redist_regions, sizeof(*rdist_regs), + GFP_KERNEL); if (!rdist_regs) { err = -ENOMEM; goto out_unmap_dist; diff --git a/drivers/irqchip/irq-partition-percpu.c b/drivers/irqchip/irq-partition-percpu.c index ccd72c2cbc23..1f7cc5933cd5 100644 --- a/drivers/irqchip/irq-partition-percpu.c +++ b/drivers/irqchip/irq-partition-percpu.c @@ -229,7 +229,7 @@ struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode, goto out; desc->domain = d; - desc->bitmap = kzalloc(sizeof(long) * BITS_TO_LONGS(nr_parts), + desc->bitmap = kcalloc(BITS_TO_LONGS(nr_parts), sizeof(long), GFP_KERNEL); if (WARN_ON(!desc->bitmap)) goto out; diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index ec0e6a8cdb75..f6fd57ebe6e6 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -1261,7 +1261,7 @@ static int __init s3c_init_intc_of(struct device_node *np, return -ENOMEM; intc->domain = domain; - intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32, + intc->irqs = kcalloc(32, sizeof(struct s3c_irq_data), GFP_KERNEL); if (!intc->irqs) { kfree(intc); diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index baa1ee2bc2ac..6e0c2814d032 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1260,7 +1260,7 @@ static int __init capinc_tty_init(void) if (capi_ttyminors <= 0) capi_ttyminors = CAPINC_NR_PORTS; - capiminors = kzalloc(sizeof(struct capiminor *) * capi_ttyminors, + capiminors = kcalloc(capi_ttyminors, sizeof(struct capiminor *), GFP_KERNEL); if (!capiminors) return -ENOMEM; diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c index fd13ed44a54e..9cb2ab57fa4a 100644 --- a/drivers/isdn/gigaset/capi.c +++ b/drivers/isdn/gigaset/capi.c @@ -1370,7 +1370,7 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8; /* build command table */ - commands = kzalloc(AT_NUM * (sizeof *commands), GFP_KERNEL); + commands = kcalloc(AT_NUM, sizeof(*commands), GFP_KERNEL); if (!commands) goto oom; diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c index 2d75329007f1..b5b389e95edd 100644 --- a/drivers/isdn/gigaset/i4l.c +++ b/drivers/isdn/gigaset/i4l.c @@ -243,7 +243,7 @@ static int command_from_LL(isdn_ctrl *cntrl) dev_kfree_skb(bcs->rx_skb); gigaset_new_rx_skb(bcs); - commands = kzalloc(AT_NUM * (sizeof *commands), GFP_ATOMIC); + commands = kcalloc(AT_NUM, sizeof(*commands), GFP_ATOMIC); if (!commands) { gigaset_free_channel(bcs); dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n"); diff --git a/drivers/isdn/hardware/avm/b1.c b/drivers/isdn/hardware/avm/b1.c index 5ee5489d3f15..4ac378e48902 100644 --- a/drivers/isdn/hardware/avm/b1.c +++ b/drivers/isdn/hardware/avm/b1.c @@ -72,7 +72,7 @@ avmcard *b1_alloc_card(int nr_controllers) if (!card) return NULL; - cinfo = kzalloc(sizeof(*cinfo) * nr_controllers, GFP_KERNEL); + cinfo = kcalloc(nr_controllers, sizeof(*cinfo), GFP_KERNEL); if (!cinfo) { kfree(card); return NULL; diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index 3e020ec0f65e..80ba82f77c63 100644 --- a/drivers/isdn/hisax/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -27,7 +27,9 @@ FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount) int i; fsm->jumpmatrix = - kzalloc(sizeof(FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL); + kzalloc(array3_size(sizeof(FSMFNPTR), fsm->state_count, + fsm->event_count), + GFP_KERNEL); if (!fsm->jumpmatrix) return -ENOMEM; diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index 1644ac52548b..7a501dbe7123 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -2070,14 +2070,14 @@ isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding) if ((adding) && (d->rcverr)) kfree(d->rcverr); - if (!(d->rcverr = kzalloc(sizeof(int) * m, GFP_ATOMIC))) { + if (!(d->rcverr = kcalloc(m, sizeof(int), GFP_ATOMIC))) { printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n"); return -1; } if ((adding) && (d->rcvcount)) kfree(d->rcvcount); - if (!(d->rcvcount = kzalloc(sizeof(int) * m, GFP_ATOMIC))) { + if (!(d->rcvcount = kcalloc(m, sizeof(int), GFP_ATOMIC))) { printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n"); if (!adding) kfree(d->rcverr); diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c index cabcb906e0b5..9a8d08d677a4 100644 --- a/drivers/isdn/mISDN/fsm.c +++ b/drivers/isdn/mISDN/fsm.c @@ -32,8 +32,10 @@ mISDN_FsmNew(struct Fsm *fsm, { int i; - fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count * - fsm->event_count, GFP_KERNEL); + fsm->jumpmatrix = + kzalloc(array3_size(sizeof(FSMFNPTR), fsm->state_count, + fsm->event_count), + GFP_KERNEL); if (fsm->jumpmatrix == NULL) return -ENOMEM; diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c index f497a77423a2..c7a7c2de0672 100644 --- a/drivers/lightnvm/pblk-init.c +++ b/drivers/lightnvm/pblk-init.c @@ -379,7 +379,7 @@ static int pblk_core_init(struct pblk *pblk) return -EINVAL; } - pblk->pad_dist = kzalloc((pblk->min_write_pgs - 1) * sizeof(atomic64_t), + pblk->pad_dist = kcalloc(pblk->min_write_pgs - 1, sizeof(atomic64_t), GFP_KERNEL); if (!pblk->pad_dist) return -ENOMEM; diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index fc3c237daef2..311e91b1a14f 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -466,7 +466,8 @@ static int __init acpi_pcc_probe(void) return -EINVAL; } - pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) * count, GFP_KERNEL); + pcc_mbox_channels = kcalloc(count, sizeof(struct mbox_chan), + GFP_KERNEL); if (!pcc_mbox_channels) { pr_err("Could not allocate space for PCC mbox channels\n"); return -ENOMEM; diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index a31e55bcc4e5..ec5f70d021de 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1715,7 +1715,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb) iter_size = (sb->bucket_size / sb->block_size + 1) * sizeof(struct btree_iter_set); - if (!(c->devices = kzalloc(c->nr_uuids * sizeof(void *), GFP_KERNEL)) || + if (!(c->devices = kcalloc(c->nr_uuids, sizeof(void *), GFP_KERNEL)) || mempool_init_slab_pool(&c->search, 32, bch_search_cache) || mempool_init_kmalloc_pool(&c->bio_meta, 2, sizeof(struct bbio) + sizeof(struct bio_vec) * @@ -2043,8 +2043,9 @@ static int cache_alloc(struct cache *ca) !init_heap(&ca->heap, free << 3, GFP_KERNEL) || !(ca->buckets = vzalloc(sizeof(struct bucket) * ca->sb.nbuckets)) || - !(ca->prio_buckets = kzalloc(sizeof(uint64_t) * prio_buckets(ca) * - 2, GFP_KERNEL)) || + !(ca->prio_buckets = kzalloc(array3_size(sizeof(uint64_t), + prio_buckets(ca), 2), + GFP_KERNEL)) || !(ca->disk_buckets = alloc_bucket_pages(GFP_KERNEL, ca))) return -ENOMEM; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index da02f4d8e4b9..57ca92dc0c3e 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1878,8 +1878,9 @@ static int crypt_alloc_tfms_skcipher(struct crypt_config *cc, char *ciphermode) unsigned i; int err; - cc->cipher_tfm.tfms = kzalloc(cc->tfms_count * - sizeof(struct crypto_skcipher *), GFP_KERNEL); + cc->cipher_tfm.tfms = kcalloc(cc->tfms_count, + sizeof(struct crypto_skcipher *), + GFP_KERNEL); if (!cc->cipher_tfm.tfms) return -ENOMEM; diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index 01c8329b512d..f983c3fdf204 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -2117,7 +2117,7 @@ int bitmap_resize(struct bitmap *bitmap, sector_t blocks, pages = DIV_ROUND_UP(chunks, PAGE_COUNTER_RATIO); - new_bp = kzalloc(pages * sizeof(*new_bp), GFP_KERNEL); + new_bp = kcalloc(pages, sizeof(*new_bp), GFP_KERNEL); ret = -ENOMEM; if (!new_bp) { bitmap_file_unmap(&store); diff --git a/drivers/md/md-cluster.c b/drivers/md/md-cluster.c index 79bfbc840385..021cbf9ef1bf 100644 --- a/drivers/md/md-cluster.c +++ b/drivers/md/md-cluster.c @@ -1380,9 +1380,9 @@ static int lock_all_bitmaps(struct mddev *mddev) char str[64]; struct md_cluster_info *cinfo = mddev->cluster_info; - cinfo->other_bitmap_lockres = kzalloc((mddev->bitmap_info.nodes - 1) * - sizeof(struct dlm_lock_resource *), - GFP_KERNEL); + cinfo->other_bitmap_lockres = + kcalloc(mddev->bitmap_info.nodes - 1, + sizeof(struct dlm_lock_resource *), GFP_KERNEL); if (!cinfo->other_bitmap_lockres) { pr_err("md: can't alloc mem for other bitmap locks\n"); return 0; diff --git a/drivers/md/md-multipath.c b/drivers/md/md-multipath.c index f71fcdb9b39c..881487de1e25 100644 --- a/drivers/md/md-multipath.c +++ b/drivers/md/md-multipath.c @@ -399,7 +399,8 @@ static int multipath_run (struct mddev *mddev) if (!conf) goto out; - conf->multipaths = kzalloc(sizeof(struct multipath_info)*mddev->raid_disks, + conf->multipaths = kcalloc(mddev->raid_disks, + sizeof(struct multipath_info), GFP_KERNEL); if (!conf->multipaths) goto out_free_conf; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 65ae47a02218..ac1cffd2a09b 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -159,12 +159,14 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) } err = -ENOMEM; - conf->strip_zone = kzalloc(sizeof(struct strip_zone)* - conf->nr_strip_zones, GFP_KERNEL); + conf->strip_zone = kcalloc(conf->nr_strip_zones, + sizeof(struct strip_zone), + GFP_KERNEL); if (!conf->strip_zone) goto abort; - conf->devlist = kzalloc(sizeof(struct md_rdev*)* - conf->nr_strip_zones*mddev->raid_disks, + conf->devlist = kzalloc(array3_size(sizeof(struct md_rdev *), + conf->nr_strip_zones, + mddev->raid_disks), GFP_KERNEL); if (!conf->devlist) goto abort; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index e7c0ecd19234..8e05c1092aef 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2936,9 +2936,9 @@ static struct r1conf *setup_conf(struct mddev *mddev) if (!conf->barrier) goto abort; - conf->mirrors = kzalloc(sizeof(struct raid1_info) - * mddev->raid_disks * 2, - GFP_KERNEL); + conf->mirrors = kzalloc(array3_size(sizeof(struct raid1_info), + mddev->raid_disks, 2), + GFP_KERNEL); if (!conf->mirrors) goto abort; @@ -3241,7 +3241,8 @@ static int raid1_reshape(struct mddev *mddev) kfree(newpoolinfo); return ret; } - newmirrors = kzalloc(sizeof(struct raid1_info) * raid_disks * 2, + newmirrors = kzalloc(array3_size(sizeof(struct raid1_info), + raid_disks, 2), GFP_KERNEL); if (!newmirrors) { kfree(newpoolinfo); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index e35db73b9b9e..478cf446827f 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -3688,8 +3688,8 @@ static struct r10conf *setup_conf(struct mddev *mddev) goto out; /* FIXME calc properly */ - conf->mirrors = kzalloc(sizeof(struct raid10_info)*(mddev->raid_disks + - max(0,-mddev->delta_disks)), + conf->mirrors = kcalloc(mddev->raid_disks + max(0, -mddev->delta_disks), + sizeof(struct raid10_info), GFP_KERNEL); if (!conf->mirrors) goto out; @@ -4129,11 +4129,10 @@ static int raid10_check_reshape(struct mddev *mddev) conf->mirrors_new = NULL; if (mddev->delta_disks > 0) { /* allocate new 'mirrors' list */ - conf->mirrors_new = kzalloc( - sizeof(struct raid10_info) - *(mddev->raid_disks + - mddev->delta_disks), - GFP_KERNEL); + conf->mirrors_new = + kcalloc(mddev->raid_disks + mddev->delta_disks, + sizeof(struct raid10_info), + GFP_KERNEL); if (!conf->mirrors_new) return -ENOMEM; } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 73489446bbcb..2031506a0ecd 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2396,7 +2396,7 @@ static int resize_stripes(struct r5conf *conf, int newsize) * is completely stalled, so now is a good time to resize * conf->disks and the scribble region */ - ndisks = kzalloc(newsize * sizeof(struct disk_info), GFP_NOIO); + ndisks = kcalloc(newsize, sizeof(struct disk_info), GFP_NOIO); if (ndisks) { for (i = 0; i < conf->pool_size; i++) ndisks[i] = conf->disks[i]; @@ -6664,9 +6664,9 @@ static int alloc_thread_groups(struct r5conf *conf, int cnt, } *group_cnt = num_possible_nodes(); size = sizeof(struct r5worker) * cnt; - workers = kzalloc(size * *group_cnt, GFP_NOIO); - *worker_groups = kzalloc(sizeof(struct r5worker_group) * - *group_cnt, GFP_NOIO); + workers = kcalloc(size, *group_cnt, GFP_NOIO); + *worker_groups = kcalloc(*group_cnt, sizeof(struct r5worker_group), + GFP_NOIO); if (!*worker_groups || !workers) { kfree(workers); kfree(*worker_groups); @@ -6894,8 +6894,9 @@ static struct r5conf *setup_conf(struct mddev *mddev) goto abort; INIT_LIST_HEAD(&conf->free_list); INIT_LIST_HEAD(&conf->pending_list); - conf->pending_data = kzalloc(sizeof(struct r5pending_data) * - PENDING_IO_MAX, GFP_KERNEL); + conf->pending_data = kcalloc(PENDING_IO_MAX, + sizeof(struct r5pending_data), + GFP_KERNEL); if (!conf->pending_data) goto abort; for (i = 0; i < PENDING_IO_MAX; i++) @@ -6944,7 +6945,7 @@ static struct r5conf *setup_conf(struct mddev *mddev) conf->previous_raid_disks = mddev->raid_disks - mddev->delta_disks; max_disks = max(conf->raid_disks, conf->previous_raid_disks); - conf->disks = kzalloc(max_disks * sizeof(struct disk_info), + conf->disks = kcalloc(max_disks, sizeof(struct disk_info), GFP_KERNEL); if (!conf->disks) diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 902af482448e..5a8dbc0b25fb 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -2018,10 +2018,10 @@ static int dib7000pc_detection(struct i2c_adapter *i2c_adap) }; int ret = 0; - tx = kzalloc(2*sizeof(u8), GFP_KERNEL); + tx = kzalloc(2, GFP_KERNEL); if (!tx) return -ENOMEM; - rx = kzalloc(2*sizeof(u8), GFP_KERNEL); + rx = kzalloc(2, GFP_KERNEL); if (!rx) { ret = -ENOMEM; goto rx_memory_error; diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 6f35173d2968..22eec8f65485 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -4271,12 +4271,12 @@ static int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 new_addr = 0; struct i2c_device client = {.adap = host }; - client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); + client.i2c_write_buffer = kzalloc(4, GFP_KERNEL); if (!client.i2c_write_buffer) { dprintk("%s: not enough memory\n", __func__); return -ENOMEM; } - client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); + client.i2c_read_buffer = kzalloc(4, GFP_KERNEL); if (!client.i2c_read_buffer) { dprintk("%s: not enough memory\n", __func__); ret = -ENOMEM; diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c index f9289f488de7..b8edb55696bb 100644 --- a/drivers/media/dvb-frontends/dib9000.c +++ b/drivers/media/dvb-frontends/dib9000.c @@ -2381,12 +2381,12 @@ int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defaul u8 new_addr = 0; struct i2c_device client = {.i2c_adap = i2c }; - client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); + client.i2c_write_buffer = kzalloc(4, GFP_KERNEL); if (!client.i2c_write_buffer) { dprintk("%s: not enough memory\n", __func__); return -ENOMEM; } - client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); + client.i2c_read_buffer = kzalloc(4, GFP_KERNEL); if (!client.i2c_read_buffer) { dprintk("%s: not enough memory\n", __func__); ret = -ENOMEM; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 964cd7bcdd2c..70e187971590 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -217,14 +217,14 @@ static int au0828_init_isoc(struct au0828_dev *dev, int max_packets, dev->isoc_ctl.isoc_copy = isoc_copy; dev->isoc_ctl.num_bufs = num_bufs; - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + dev->isoc_ctl.urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!dev->isoc_ctl.urb) { au0828_isocdbg("cannot alloc memory for usb buffers\n"); return -ENOMEM; } - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); + dev->isoc_ctl.transfer_buffer = kcalloc(num_bufs, sizeof(void *), + GFP_KERNEL); if (!dev->isoc_ctl.transfer_buffer) { au0828_isocdbg("cannot allocate memory for usb transfer\n"); kfree(dev->isoc_ctl.urb); diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 4f43668df15d..53d846dea3d2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -1034,7 +1034,7 @@ int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, dma_q->partial_buf[i] = 0; dev->video_mode.isoc_ctl.urb = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!dev->video_mode.isoc_ctl.urb) { dev_err(dev->dev, "cannot alloc memory for usb buffers\n"); @@ -1042,7 +1042,7 @@ int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, } dev->video_mode.isoc_ctl.transfer_buffer = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!dev->video_mode.isoc_ctl.transfer_buffer) { dev_err(dev->dev, "cannot allocate memory for usbtransfer\n"); @@ -1169,7 +1169,7 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, dma_q->partial_buf[i] = 0; dev->video_mode.bulk_ctl.urb = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!dev->video_mode.bulk_ctl.urb) { dev_err(dev->dev, "cannot alloc memory for usb buffers\n"); @@ -1177,7 +1177,7 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, } dev->video_mode.bulk_ctl.transfer_buffer = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!dev->video_mode.bulk_ctl.transfer_buffer) { dev_err(dev->dev, "cannot allocate memory for usbtransfer\n"); diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index d3bfe8e23b1f..b621cf1aa96b 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -415,7 +415,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, for (i = 0; i < 8; i++) dma_q->partial_buf[i] = 0; - dev->vbi_mode.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs, + dev->vbi_mode.bulk_ctl.urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!dev->vbi_mode.bulk_ctl.urb) { dev_err(dev->dev, @@ -424,7 +424,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, } dev->vbi_mode.bulk_ctl.transfer_buffer = - kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!dev->vbi_mode.bulk_ctl.transfer_buffer) { dev_err(dev->dev, "cannot allocate memory for usbtransfer\n"); diff --git a/drivers/media/usb/go7007/go7007-fw.c b/drivers/media/usb/go7007/go7007-fw.c index 87b4fc48ef09..24f5b615dc7a 100644 --- a/drivers/media/usb/go7007/go7007-fw.c +++ b/drivers/media/usb/go7007/go7007-fw.c @@ -1579,7 +1579,7 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) GO7007_FW_NAME); return -1; } - code = kzalloc(codespace * 2, GFP_KERNEL); + code = kcalloc(codespace, 2, GFP_KERNEL); if (code == NULL) goto fw_failed; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index e0353161ccd6..a8519da0020b 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -2413,7 +2413,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->control_cnt = CTRLDEF_COUNT; hdw->control_cnt += MPEGDEF_COUNT; - hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, + hdw->controls = kcalloc(hdw->control_cnt, sizeof(struct pvr2_ctrl), GFP_KERNEL); if (!hdw->controls) goto fail; hdw->hdw_desc = hdw_desc; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c index 21bb20dba82c..6b651f8b54df 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c @@ -361,7 +361,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, std_cnt); if (!std_cnt) return NULL; // paranoia - stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt, + stddefs = kcalloc(std_cnt, sizeof(struct v4l2_standard), GFP_KERNEL); if (!stddefs) return NULL; diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index 423c03a0638d..2811f612820f 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -439,14 +439,14 @@ int stk1160_alloc_isoc(struct stk1160 *dev) dev->isoc_ctl.buf = NULL; dev->isoc_ctl.max_pkt_size = dev->max_pkt_size; - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + dev->isoc_ctl.urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!dev->isoc_ctl.urb) { stk1160_err("out of memory for urb array\n"); return -ENOMEM; } - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); + dev->isoc_ctl.transfer_buffer = kcalloc(num_bufs, sizeof(void *), + GFP_KERNEL); if (!dev->isoc_ctl.transfer_buffer) { stk1160_err("out of memory for usb transfers\n"); kfree(dev->isoc_ctl.urb); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 22389b56ec24..5accb5241072 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -567,8 +567,9 @@ static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) if (dev->sio_bufs != NULL) pr_err("sio_bufs already allocated\n"); else { - dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer), - GFP_KERNEL); + dev->sio_bufs = kcalloc(n_sbufs, + sizeof(struct stk_sio_buffer), + GFP_KERNEL); if (dev->sio_bufs == NULL) return -ENOMEM; for (i = 0; i < n_sbufs; i++) { diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index ce79df643c7e..36a9a4017185 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -507,7 +507,7 @@ static struct urb *usbtv_setup_iso_transfer(struct usbtv *usbtv) ip->pipe = usb_rcvisocpipe(usbtv->udev, USBTV_VIDEO_ENDP); ip->interval = 1; ip->transfer_flags = URB_ISO_ASAP; - ip->transfer_buffer = kzalloc(size * USBTV_ISOC_PACKETS, + ip->transfer_buffer = kcalloc(USBTV_ISOC_PACKETS, size, GFP_KERNEL); if (!ip->transfer_buffer) { usb_free_urb(ip); diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 4199cdd4ff89..306e1fd109bd 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -299,13 +299,14 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec) resp = (struct ec_response_motion_sense *)msg->data; sensor_num = resp->dump.sensor_count; /* Allocate 1 extra sensors in FIFO are needed */ - sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 1), + sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell), GFP_KERNEL); if (sensor_cells == NULL) goto error; - sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) * - (sensor_num + 1), GFP_KERNEL); + sensor_platforms = kcalloc(sensor_num + 1, + sizeof(struct cros_ec_sensor_platform), + GFP_KERNEL); if (sensor_platforms == NULL) goto error_platforms; diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index c57e407020f1..94e3f32ce935 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -158,7 +158,7 @@ static int mfd_add_device(struct device *parent, int id, if (!pdev) goto fail_alloc; - res = kzalloc(sizeof(*res) * cell->num_resources, GFP_KERNEL); + res = kcalloc(cell->num_resources, sizeof(*res), GFP_KERNEL); if (!res) goto fail_device; diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 7c13d2e7061c..05ecf828b2ab 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -707,8 +707,8 @@ static int timb_probe(struct pci_dev *dev, goto err_config; } - msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries), - GFP_KERNEL); + msix_entries = kcalloc(TIMBERDALE_NR_IRQS, sizeof(*msix_entries), + GFP_KERNEL); if (!msix_entries) goto err_config; diff --git a/drivers/misc/altera-stapl/altera.c b/drivers/misc/altera-stapl/altera.c index f53e217e963f..ef83a9078646 100644 --- a/drivers/misc/altera-stapl/altera.c +++ b/drivers/misc/altera-stapl/altera.c @@ -304,13 +304,13 @@ static int altera_execute(struct altera_state *astate, if (sym_count <= 0) goto exit_done; - vars = kzalloc(sym_count * sizeof(long), GFP_KERNEL); + vars = kcalloc(sym_count, sizeof(long), GFP_KERNEL); if (vars == NULL) status = -ENOMEM; if (status == 0) { - var_size = kzalloc(sym_count * sizeof(s32), GFP_KERNEL); + var_size = kcalloc(sym_count, sizeof(s32), GFP_KERNEL); if (var_size == NULL) status = -ENOMEM; @@ -1136,7 +1136,7 @@ exit_done: /* Allocate a writable buffer for this array */ count = var_size[variable_id]; long_tmp = vars[variable_id]; - longptr_tmp = kzalloc(count * sizeof(long), + longptr_tmp = kcalloc(count, sizeof(long), GFP_KERNEL); vars[variable_id] = (long)longptr_tmp; diff --git a/drivers/misc/cxl/guest.c b/drivers/misc/cxl/guest.c index f58b4b6c79f2..4644f16606a3 100644 --- a/drivers/misc/cxl/guest.c +++ b/drivers/misc/cxl/guest.c @@ -89,7 +89,7 @@ static ssize_t guest_collect_vpd(struct cxl *adapter, struct cxl_afu *afu, mod = 0; } - vpd_buf = kzalloc(entries * sizeof(unsigned long *), GFP_KERNEL); + vpd_buf = kcalloc(entries, sizeof(unsigned long *), GFP_KERNEL); if (!vpd_buf) return -ENOMEM; diff --git a/drivers/misc/cxl/of.c b/drivers/misc/cxl/of.c index ec175ea5dfba..aff181cd0bf2 100644 --- a/drivers/misc/cxl/of.c +++ b/drivers/misc/cxl/of.c @@ -302,7 +302,7 @@ static int read_adapter_irq_config(struct cxl *adapter, struct device_node *np) if (nranges == 0 || (nranges * 2 * sizeof(int)) != len) return -EINVAL; - adapter->guest->irq_avail = kzalloc(nranges * sizeof(struct irq_avail), + adapter->guest->irq_avail = kcalloc(nranges, sizeof(struct irq_avail), GFP_KERNEL); if (adapter->guest->irq_avail == NULL) return -ENOMEM; diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c index b7f8d35c17a9..656449cb4476 100644 --- a/drivers/misc/genwqe/card_ddcb.c +++ b/drivers/misc/genwqe/card_ddcb.c @@ -1048,15 +1048,16 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue) "[%s] **err: could not allocate DDCB **\n", __func__); return -ENOMEM; } - queue->ddcb_req = kzalloc(sizeof(struct ddcb_requ *) * - queue->ddcb_max, GFP_KERNEL); + queue->ddcb_req = kcalloc(queue->ddcb_max, sizeof(struct ddcb_requ *), + GFP_KERNEL); if (!queue->ddcb_req) { rc = -ENOMEM; goto free_ddcbs; } - queue->ddcb_waitqs = kzalloc(sizeof(wait_queue_head_t) * - queue->ddcb_max, GFP_KERNEL); + queue->ddcb_waitqs = kcalloc(queue->ddcb_max, + sizeof(wait_queue_head_t), + GFP_KERNEL); if (!queue->ddcb_waitqs) { rc = -ENOMEM; goto free_requs; diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index 0c775d6fcf59..83fc748a91a7 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c @@ -416,7 +416,8 @@ xpc_setup_ch_structures(struct xpc_partition *part) * memory. */ DBUG_ON(part->channels != NULL); - part->channels = kzalloc(sizeof(struct xpc_channel) * XPC_MAX_NCHANNELS, + part->channels = kcalloc(XPC_MAX_NCHANNELS, + sizeof(struct xpc_channel), GFP_KERNEL); if (part->channels == NULL) { dev_err(xpc_chan, "can't get memory for channels\n"); @@ -905,8 +906,9 @@ xpc_setup_partitions(void) short partid; struct xpc_partition *part; - xpc_partitions = kzalloc(sizeof(struct xpc_partition) * - xp_max_npartitions, GFP_KERNEL); + xpc_partitions = kcalloc(xp_max_npartitions, + sizeof(struct xpc_partition), + GFP_KERNEL); if (xpc_partitions == NULL) { dev_err(xpc_part, "can't get memory for partition structure\n"); return -ENOMEM; diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c index 6956f7e7d439..7284413dabfd 100644 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ b/drivers/misc/sgi-xp/xpc_partition.c @@ -425,7 +425,7 @@ xpc_discovery(void) if (remote_rp == NULL) return; - discovered_nasids = kzalloc(sizeof(long) * xpc_nasid_mask_nlongs, + discovered_nasids = kcalloc(xpc_nasid_mask_nlongs, sizeof(long), GFP_KERNEL); if (discovered_nasids == NULL) { kfree(remote_rp_base); diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 216d5c756236..44d750d98bc8 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -520,8 +520,9 @@ xpnet_init(void) dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME); - xpnet_broadcast_partitions = kzalloc(BITS_TO_LONGS(xp_max_npartitions) * - sizeof(long), GFP_KERNEL); + xpnet_broadcast_partitions = kcalloc(BITS_TO_LONGS(xp_max_npartitions), + sizeof(long), + GFP_KERNEL); if (xpnet_broadcast_partitions == NULL) return -ENOMEM; diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index fc0415771c00..e2e31b65bc5a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -185,7 +185,7 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) * after the reserved blocks from the dt are processed. */ nblocks = (np) ? of_get_available_child_count(np) + 1 : 1; - rblocks = kzalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL); + rblocks = kcalloc(nblocks, sizeof(*rblocks), GFP_KERNEL); if (!rblocks) return -ENOMEM; diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c index 90575deff0ae..fc15ec58230a 100644 --- a/drivers/mtd/ar7part.c +++ b/drivers/mtd/ar7part.c @@ -55,7 +55,7 @@ static int create_mtd_partitions(struct mtd_info *master, int retries = 10; struct mtd_partition *ar7_parts; - ar7_parts = kzalloc(sizeof(*ar7_parts) * AR7_PARTS, GFP_KERNEL); + ar7_parts = kcalloc(AR7_PARTS, sizeof(*ar7_parts), GFP_KERNEL); if (!ar7_parts) return -ENOMEM; ar7_parts[0].name = "loader"; diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 0f93d2239352..fc424b185b08 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -110,7 +110,7 @@ static int bcm47xxpart_parse(struct mtd_info *master, blocksize = 0x1000; /* Alloc */ - parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS, + parts = kcalloc(BCM47XXPART_MAX_PARTS, sizeof(struct mtd_partition), GFP_KERNEL); if (!parts) return -ENOMEM; diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 5a81bd8073bc..6e8e7b1bb34b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -608,8 +608,9 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) mtd->size = devsize * cfi->numchips; mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; - mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info) - * mtd->numeraseregions, GFP_KERNEL); + mtd->eraseregions = kcalloc(mtd->numeraseregions, + sizeof(struct mtd_erase_region_info), + GFP_KERNEL); if (!mtd->eraseregions) goto setup_err; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 22506d22194e..a0c655628d6d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2636,7 +2636,7 @@ static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, * first check the locking status of all sectors and save * it for future use. */ - sect = kzalloc(MAX_SECTORS * sizeof(struct ppb_lock), GFP_KERNEL); + sect = kcalloc(MAX_SECTORS, sizeof(struct ppb_lock), GFP_KERNEL); if (!sect) return -ENOMEM; diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 802d8f159e90..a0d485f52cbe 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1827,7 +1827,7 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev) mtd->dev.parent = dev; bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, 8 * DOC_LAYOUT_PAGE_SIZE); - docg3->bbt = kzalloc(bbt_nbpages * DOC_LAYOUT_PAGE_SIZE, GFP_KERNEL); + docg3->bbt = kcalloc(DOC_LAYOUT_PAGE_SIZE, bbt_nbpages, GFP_KERNEL); if (!docg3->bbt) goto nomem3; diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c index 527b1682381f..4129535b8e46 100644 --- a/drivers/mtd/maps/physmap_of_core.c +++ b/drivers/mtd/maps/physmap_of_core.c @@ -124,7 +124,7 @@ static const char * const *of_get_probes(struct device_node *dp) if (count < 0) return part_probe_types_def; - res = kzalloc((count + 1) * sizeof(*res), GFP_KERNEL); + res = kcalloc(count + 1, sizeof(*res), GFP_KERNEL); if (!res) return NULL; @@ -197,7 +197,7 @@ static int of_flash_probe(struct platform_device *dev) dev_set_drvdata(&dev->dev, info); - mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL); + mtd_list = kcalloc(count, sizeof(*mtd_list), GFP_KERNEL); if (!mtd_list) goto err_flash_remove; diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index b7105192cb12..4ca4b194e7d7 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -3721,8 +3721,10 @@ static int onenand_probe(struct mtd_info *mtd) this->dies = ONENAND_IS_DDP(this) ? 2 : 1; /* Maximum possible erase regions */ mtd->numeraseregions = this->dies << 1; - mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info) - * (this->dies << 1), GFP_KERNEL); + mtd->eraseregions = + kcalloc(this->dies << 1, + sizeof(struct mtd_erase_region_info), + GFP_KERNEL); if (!mtd->eraseregions) return -ENOMEM; } diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 615f8c173162..6b21a92d3622 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -71,7 +71,7 @@ static int parse_fixed_partitions(struct mtd_info *master, if (nr_parts == 0) return 0; - parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL); + parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); if (!parts) return -ENOMEM; @@ -177,7 +177,7 @@ static int parse_ofoldpart_partitions(struct mtd_info *master, nr_parts = plen / sizeof(part[0]); - parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL); + parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); if (!parts) return -ENOMEM; diff --git a/drivers/mtd/parsers/parser_trx.c b/drivers/mtd/parsers/parser_trx.c index df360a75e1eb..17ac33599783 100644 --- a/drivers/mtd/parsers/parser_trx.c +++ b/drivers/mtd/parsers/parser_trx.c @@ -62,7 +62,7 @@ static int parser_trx_parse(struct mtd_info *mtd, uint8_t curr_part = 0, i = 0; int err; - parts = kzalloc(sizeof(struct mtd_partition) * TRX_PARSER_MAX_PARTS, + parts = kcalloc(TRX_PARSER_MAX_PARTS, sizeof(struct mtd_partition), GFP_KERNEL); if (!parts) return -ENOMEM; diff --git a/drivers/mtd/parsers/sharpslpart.c b/drivers/mtd/parsers/sharpslpart.c index 8893dc82a5c8..e5ea6127ab5a 100644 --- a/drivers/mtd/parsers/sharpslpart.c +++ b/drivers/mtd/parsers/sharpslpart.c @@ -362,8 +362,9 @@ static int sharpsl_parse_mtd_partitions(struct mtd_info *master, return err; } - sharpsl_nand_parts = kzalloc(sizeof(*sharpsl_nand_parts) * - SHARPSL_NAND_PARTS, GFP_KERNEL); + sharpsl_nand_parts = kcalloc(SHARPSL_NAND_PARTS, + sizeof(*sharpsl_nand_parts), + GFP_KERNEL); if (!sharpsl_nand_parts) return -ENOMEM; diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 9d019ce1589e..f3bd86e13603 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -82,7 +82,7 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) /* Create array of pointers to the attributes */ - attributes = kzalloc(sizeof(struct attribute *) * (NUM_ATTRIBUTES + 1), + attributes = kcalloc(NUM_ATTRIBUTES + 1, sizeof(struct attribute *), GFP_KERNEL); if (!attributes) goto error3; @@ -1137,7 +1137,7 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) goto error2; /* Allocate zone array, it will be initialized on demand */ - ftl->zones = kzalloc(sizeof(struct ftl_zone) * ftl->zone_count, + ftl->zones = kcalloc(ftl->zone_count, sizeof(struct ftl_zone), GFP_KERNEL); if (!ftl->zones) goto error3; diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c index bc303cac9f43..75687369bc20 100644 --- a/drivers/mtd/tests/pagetest.c +++ b/drivers/mtd/tests/pagetest.c @@ -127,7 +127,7 @@ static int crosstest(void) unsigned char *pp1, *pp2, *pp3, *pp4; pr_info("crosstest\n"); - pp1 = kzalloc(pgsize * 4, GFP_KERNEL); + pp1 = kcalloc(pgsize, 4, GFP_KERNEL); if (!pp1) return -ENOMEM; pp2 = pp1 + pgsize; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index f66b3b22f328..6f2ac865ff05 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1592,7 +1592,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); err = -ENOMEM; - ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); + ubi->lookuptbl = kcalloc(ubi->peb_count, sizeof(void *), GFP_KERNEL); if (!ubi->lookuptbl) return err; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index bd53a71f6b00..63e3844c5bec 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2418,7 +2418,7 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev, struct list_head *iter; if (start_dev == end_dev) { - tags = kzalloc(sizeof(*tags) * (level + 1), GFP_ATOMIC); + tags = kcalloc(level + 1, sizeof(*tags), GFP_ATOMIC); if (!tags) return ERR_PTR(-ENOMEM); tags[level].vlan_proto = VLAN_N_VID; diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c index 2d3046afa80d..7eec1d9f86a0 100644 --- a/drivers/net/can/grcan.c +++ b/drivers/net/can/grcan.c @@ -1057,7 +1057,7 @@ static int grcan_open(struct net_device *dev) return err; } - priv->echo_skb = kzalloc(dma->tx.size * sizeof(*priv->echo_skb), + priv->echo_skb = kcalloc(dma->tx.size, sizeof(*priv->echo_skb), GFP_KERNEL); if (!priv->echo_skb) { err = -ENOMEM; @@ -1066,7 +1066,7 @@ static int grcan_open(struct net_device *dev) priv->can.echo_skb_max = dma->tx.size; priv->can.echo_skb = priv->echo_skb; - priv->txdlc = kzalloc(dma->tx.size * sizeof(*priv->txdlc), GFP_KERNEL); + priv->txdlc = kcalloc(dma->tx.size, sizeof(*priv->txdlc), GFP_KERNEL); if (!priv->txdlc) { err = -ENOMEM; goto exit_free_echo_skb; diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index 89d60d8e467c..aa97dbc797b6 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -703,7 +703,7 @@ static int __init slcan_init(void) pr_info("slcan: serial line CAN interface driver\n"); pr_info("slcan: %d dynamic interface channels.\n", maxdev); - slcan_devs = kzalloc(sizeof(struct net_device *)*maxdev, GFP_KERNEL); + slcan_devs = kcalloc(maxdev, sizeof(struct net_device *), GFP_KERNEL); if (!slcan_devs) return -ENOMEM; diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 14a59e51db67..897302adc38e 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -2150,7 +2150,7 @@ static int bcm_enetsw_open(struct net_device *dev) priv->tx_desc_alloc_size = size; priv->tx_desc_cpu = p; - priv->tx_skb = kzalloc(sizeof(struct sk_buff *) * priv->tx_ring_size, + priv->tx_skb = kcalloc(priv->tx_ring_size, sizeof(struct sk_buff *), GFP_KERNEL); if (!priv->tx_skb) { dev_err(kdev, "cannot allocate rx skb queue\n"); @@ -2164,7 +2164,7 @@ static int bcm_enetsw_open(struct net_device *dev) spin_lock_init(&priv->tx_lock); /* init & fill rx ring with skbs */ - priv->rx_skb = kzalloc(sizeof(struct sk_buff *) * priv->rx_ring_size, + priv->rx_skb = kcalloc(priv->rx_ring_size, sizeof(struct sk_buff *), GFP_KERNEL); if (!priv->rx_skb) { dev_err(kdev, "cannot allocate rx skb queue\n"); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index ffa7959f6b31..dc77bfded865 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -571,7 +571,7 @@ int bnx2x_vf_mcast(struct bnx2x *bp, struct bnx2x_virtf *vf, else set_bit(RAMROD_COMP_WAIT, &mcast.ramrod_flags); if (mc_num) { - mc = kzalloc(mc_num * sizeof(struct bnx2x_mcast_list_elem), + mc = kcalloc(mc_num, sizeof(struct bnx2x_mcast_list_elem), GFP_KERNEL); if (!mc) { BNX2X_ERR("Cannot Configure multicasts due to lack of memory\n"); @@ -1253,8 +1253,9 @@ int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param, num_vfs_param, iov->nr_virtfn); /* allocate the vf array */ - bp->vfdb->vfs = kzalloc(sizeof(struct bnx2x_virtf) * - BNX2X_NR_VIRTFN(bp), GFP_KERNEL); + bp->vfdb->vfs = kcalloc(BNX2X_NR_VIRTFN(bp), + sizeof(struct bnx2x_virtf), + GFP_KERNEL); if (!bp->vfdb->vfs) { BNX2X_ERR("failed to allocate vf array\n"); err = -ENOMEM; @@ -1278,9 +1279,9 @@ int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param, } /* allocate the queue arrays for all VFs */ - bp->vfdb->vfqs = kzalloc( - BNX2X_MAX_NUM_VF_QUEUES * sizeof(struct bnx2x_vf_queue), - GFP_KERNEL); + bp->vfdb->vfqs = kcalloc(BNX2X_MAX_NUM_VF_QUEUES, + sizeof(struct bnx2x_vf_queue), + GFP_KERNEL); if (!bp->vfdb->vfqs) { BNX2X_ERR("failed to allocate vf queue array\n"); diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 8bc126a156e8..30273a7717e2 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -660,7 +660,7 @@ static int cnic_init_id_tbl(struct cnic_id_tbl *id_tbl, u32 size, u32 start_id, id_tbl->max = size; id_tbl->next = next; spin_lock_init(&id_tbl->lock); - id_tbl->table = kzalloc(DIV_ROUND_UP(size, 32) * 4, GFP_KERNEL); + id_tbl->table = kcalloc(DIV_ROUND_UP(size, 32), 4, GFP_KERNEL); if (!id_tbl->table) return -ENOMEM; @@ -1255,13 +1255,13 @@ static int cnic_alloc_bnx2x_resc(struct cnic_dev *dev) cp->fcoe_init_cid = 0x10; } - cp->iscsi_tbl = kzalloc(sizeof(struct cnic_iscsi) * MAX_ISCSI_TBL_SZ, + cp->iscsi_tbl = kcalloc(MAX_ISCSI_TBL_SZ, sizeof(struct cnic_iscsi), GFP_KERNEL); if (!cp->iscsi_tbl) goto error; - cp->ctx_tbl = kzalloc(sizeof(struct cnic_context) * - cp->max_cid_space, GFP_KERNEL); + cp->ctx_tbl = kcalloc(cp->max_cid_space, sizeof(struct cnic_context), + GFP_KERNEL); if (!cp->ctx_tbl) goto error; @@ -4100,7 +4100,7 @@ static int cnic_cm_alloc_mem(struct cnic_dev *dev) struct cnic_local *cp = dev->cnic_priv; u32 port_id; - cp->csk_tbl = kzalloc(sizeof(struct cnic_sock) * MAX_CM_SK_TBL_SZ, + cp->csk_tbl = kcalloc(MAX_CM_SK_TBL_SZ, sizeof(struct cnic_sock), GFP_KERNEL); if (!cp->csk_tbl) return -ENOMEM; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 9f59b1270a7c..3be87efdc93d 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -8631,8 +8631,9 @@ static int tg3_mem_tx_acquire(struct tg3 *tp) tnapi++; for (i = 0; i < tp->txq_cnt; i++, tnapi++) { - tnapi->tx_buffers = kzalloc(sizeof(struct tg3_tx_ring_info) * - TG3_TX_RING_SIZE, GFP_KERNEL); + tnapi->tx_buffers = kcalloc(TG3_TX_RING_SIZE, + sizeof(struct tg3_tx_ring_info), + GFP_KERNEL); if (!tnapi->tx_buffers) goto err_out; diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 69cc3e0119d6..ea5f32ea308a 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -3141,7 +3141,7 @@ bnad_set_rx_ucast_fltr(struct bnad *bnad) if (uc_count > bna_attr(&bnad->bna)->num_ucmac) goto mode_default; - mac_list = kzalloc(uc_count * ETH_ALEN, GFP_ATOMIC); + mac_list = kcalloc(ETH_ALEN, uc_count, GFP_ATOMIC); if (mac_list == NULL) goto mode_default; @@ -3182,7 +3182,7 @@ bnad_set_rx_mcast_fltr(struct bnad *bnad) if (mc_count > bna_attr(&bnad->bna)->num_mcmac) goto mode_allmulti; - mac_list = kzalloc((mc_count + 1) * ETH_ALEN, GFP_ATOMIC); + mac_list = kcalloc(mc_count + 1, ETH_ALEN, GFP_ATOMIC); if (mac_list == NULL) goto mode_allmulti; diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 2bd7c638b178..2c63afff1382 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -739,7 +739,7 @@ static int xgmac_dma_desc_rings_init(struct net_device *dev) netdev_dbg(priv->dev, "mtu [%d] bfsize [%d]\n", dev->mtu, bfsize); - priv->rx_skbuff = kzalloc(sizeof(struct sk_buff *) * DMA_RX_RING_SZ, + priv->rx_skbuff = kcalloc(DMA_RX_RING_SZ, sizeof(struct sk_buff *), GFP_KERNEL); if (!priv->rx_skbuff) return -ENOMEM; @@ -752,7 +752,7 @@ static int xgmac_dma_desc_rings_init(struct net_device *dev) if (!priv->dma_rx) goto err_dma_rx; - priv->tx_skbuff = kzalloc(sizeof(struct sk_buff *) * DMA_TX_RING_SZ, + priv->tx_skbuff = kcalloc(DMA_TX_RING_SZ, sizeof(struct sk_buff *), GFP_KERNEL); if (!priv->tx_skbuff) goto err_tx_skb; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index d42704d07484..187a249ff2d1 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -292,8 +292,8 @@ static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, rbdr->is_xdp = true; } rbdr->pgcnt = roundup_pow_of_two(rbdr->pgcnt); - rbdr->pgcache = kzalloc(sizeof(*rbdr->pgcache) * - rbdr->pgcnt, GFP_KERNEL); + rbdr->pgcache = kcalloc(rbdr->pgcnt, sizeof(*rbdr->pgcache), + GFP_KERNEL); if (!rbdr->pgcache) return -ENOMEM; rbdr->pgidx = 0; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c index a95cde0fadf7..4bc211093c98 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c @@ -561,13 +561,13 @@ int t4_uld_mem_alloc(struct adapter *adap) if (!adap->uld) return -ENOMEM; - s->uld_rxq_info = kzalloc(CXGB4_ULD_MAX * + s->uld_rxq_info = kcalloc(CXGB4_ULD_MAX, sizeof(struct sge_uld_rxq_info *), GFP_KERNEL); if (!s->uld_rxq_info) goto err_uld; - s->uld_txq_info = kzalloc(CXGB4_TX_MAX * + s->uld_txq_info = kcalloc(CXGB4_TX_MAX, sizeof(struct sge_uld_txq_info *), GFP_KERNEL); if (!s->uld_txq_info) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index ff9eb45f67f8..6d7404f66f84 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -910,8 +910,8 @@ static int geth_setup_freeq(struct gemini_ethernet *geth) } /* Allocate a mapping to page look-up index */ - geth->freeq_pages = kzalloc(pages * sizeof(*geth->freeq_pages), - GFP_KERNEL); + geth->freeq_pages = kcalloc(pages, sizeof(*geth->freeq_pages), + GFP_KERNEL); if (!geth->freeq_pages) goto err_freeq; geth->num_freeq_pages = pages; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 1ccb6443d2ed..ef9ef703d13a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -2197,7 +2197,8 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv) return -EINVAL; } - priv->ring_data = kzalloc(h->q_num * sizeof(*priv->ring_data) * 2, + priv->ring_data = kzalloc(array3_size(h->q_num, + sizeof(*priv->ring_data), 2), GFP_KERNEL); if (!priv->ring_data) return -ENOMEM; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index acf1e8b52b8e..3ba0c90e7055 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -3312,7 +3312,7 @@ static int e1000e_write_mc_addr_list(struct net_device *netdev) return 0; } - mta_list = kzalloc(netdev_mc_count(netdev) * ETH_ALEN, GFP_ATOMIC); + mta_list = kcalloc(netdev_mc_count(netdev), ETH_ALEN, GFP_ATOMIC); if (!mta_list) return -ENOMEM; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index c33821d2afb3..f707709969ac 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3763,8 +3763,9 @@ static int igb_sw_init(struct igb_adapter *adapter) /* Assume MSI-X interrupts, will be checked during IRQ allocation */ adapter->flags |= IGB_FLAG_HAS_MSIX; - adapter->mac_table = kzalloc(sizeof(struct igb_mac_addr) * - hw->mac.rar_entry_count, GFP_ATOMIC); + adapter->mac_table = kcalloc(hw->mac.rar_entry_count, + sizeof(struct igb_mac_addr), + GFP_ATOMIC); if (!adapter->mac_table) return -ENOMEM; @@ -4752,7 +4753,7 @@ static int igb_write_mc_addr_list(struct net_device *netdev) return 0; } - mta_list = kzalloc(netdev_mc_count(netdev) * 6, GFP_ATOMIC); + mta_list = kcalloc(netdev_mc_count(netdev), 6, GFP_ATOMIC); if (!mta_list) return -ENOMEM; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 4929f7265598..0b1ba3ae159c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -6034,8 +6034,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter, for (i = 1; i < IXGBE_MAX_LINK_HANDLE; i++) adapter->jump_tables[i] = NULL; - adapter->mac_table = kzalloc(sizeof(struct ixgbe_mac_addr) * - hw->mac.num_rar_entries, + adapter->mac_table = kcalloc(hw->mac.num_rar_entries, + sizeof(struct ixgbe_mac_addr), GFP_ATOMIC); if (!adapter->mac_table) return -ENOMEM; diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 8a165842fa85..06ff185eb188 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -589,8 +589,9 @@ jme_setup_tx_resources(struct jme_adapter *jme) atomic_set(&txring->next_to_clean, 0); atomic_set(&txring->nr_free, jme->tx_ring_size); - txring->bufinf = kzalloc(sizeof(struct jme_buffer_info) * - jme->tx_ring_size, GFP_ATOMIC); + txring->bufinf = kcalloc(jme->tx_ring_size, + sizeof(struct jme_buffer_info), + GFP_ATOMIC); if (unlikely(!(txring->bufinf))) goto err_free_txring; @@ -838,8 +839,9 @@ jme_setup_rx_resources(struct jme_adapter *jme) rxring->next_to_use = 0; atomic_set(&rxring->next_to_clean, 0); - rxring->bufinf = kzalloc(sizeof(struct jme_buffer_info) * - jme->rx_ring_size, GFP_ATOMIC); + rxring->bufinf = kcalloc(jme->rx_ring_size, + sizeof(struct jme_buffer_info), + GFP_ATOMIC); if (unlikely(!(rxring->bufinf))) goto err_free_rxring; diff --git a/drivers/net/ethernet/mellanox/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c index 6dabd983e7e0..4bdf25059542 100644 --- a/drivers/net/ethernet/mellanox/mlx4/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c @@ -185,8 +185,8 @@ int mlx4_bitmap_init(struct mlx4_bitmap *bitmap, u32 num, u32 mask, bitmap->avail = num - reserved_top - reserved_bot; bitmap->effective_len = bitmap->avail; spin_lock_init(&bitmap->lock); - bitmap->table = kzalloc(BITS_TO_LONGS(bitmap->max) * - sizeof(long), GFP_KERNEL); + bitmap->table = kcalloc(BITS_TO_LONGS(bitmap->max), sizeof(long), + GFP_KERNEL); if (!bitmap->table) return -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 03375c705df7..e65bc3c95630 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -2377,20 +2377,23 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) struct mlx4_vf_admin_state *vf_admin; priv->mfunc.master.slave_state = - kzalloc(dev->num_slaves * - sizeof(struct mlx4_slave_state), GFP_KERNEL); + kcalloc(dev->num_slaves, + sizeof(struct mlx4_slave_state), + GFP_KERNEL); if (!priv->mfunc.master.slave_state) goto err_comm; priv->mfunc.master.vf_admin = - kzalloc(dev->num_slaves * - sizeof(struct mlx4_vf_admin_state), GFP_KERNEL); + kcalloc(dev->num_slaves, + sizeof(struct mlx4_vf_admin_state), + GFP_KERNEL); if (!priv->mfunc.master.vf_admin) goto err_comm_admin; priv->mfunc.master.vf_oper = - kzalloc(dev->num_slaves * - sizeof(struct mlx4_vf_oper_state), GFP_KERNEL); + kcalloc(dev->num_slaves, + sizeof(struct mlx4_vf_oper_state), + GFP_KERNEL); if (!priv->mfunc.master.vf_oper) goto err_comm_oper; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 9670b33fc9b1..65eb06e017e4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2229,13 +2229,15 @@ static int mlx4_en_copy_priv(struct mlx4_en_priv *dst, if (!dst->tx_ring_num[t]) continue; - dst->tx_ring[t] = kzalloc(sizeof(struct mlx4_en_tx_ring *) * - MAX_TX_RINGS, GFP_KERNEL); + dst->tx_ring[t] = kcalloc(MAX_TX_RINGS, + sizeof(struct mlx4_en_tx_ring *), + GFP_KERNEL); if (!dst->tx_ring[t]) goto err_free_tx; - dst->tx_cq[t] = kzalloc(sizeof(struct mlx4_en_cq *) * - MAX_TX_RINGS, GFP_KERNEL); + dst->tx_cq[t] = kcalloc(MAX_TX_RINGS, + sizeof(struct mlx4_en_cq *), + GFP_KERNEL); if (!dst->tx_cq[t]) { kfree(dst->tx_ring[t]); goto err_free_tx; @@ -3320,14 +3322,16 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, if (!priv->tx_ring_num[t]) continue; - priv->tx_ring[t] = kzalloc(sizeof(struct mlx4_en_tx_ring *) * - MAX_TX_RINGS, GFP_KERNEL); + priv->tx_ring[t] = kcalloc(MAX_TX_RINGS, + sizeof(struct mlx4_en_tx_ring *), + GFP_KERNEL); if (!priv->tx_ring[t]) { err = -ENOMEM; goto out; } - priv->tx_cq[t] = kzalloc(sizeof(struct mlx4_en_cq *) * - MAX_TX_RINGS, GFP_KERNEL); + priv->tx_cq[t] = kcalloc(MAX_TX_RINGS, + sizeof(struct mlx4_en_cq *), + GFP_KERNEL); if (!priv->tx_cq[t]) { err = -ENOMEM; goto out; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 0a30d81aab3b..872014702fc1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -2982,7 +2982,8 @@ static int mlx4_init_steering(struct mlx4_dev *dev) int num_entries = dev->caps.num_ports; int i, j; - priv->steer = kzalloc(sizeof(struct mlx4_steer) * num_entries, GFP_KERNEL); + priv->steer = kcalloc(num_entries, sizeof(struct mlx4_steer), + GFP_KERNEL); if (!priv->steer) return -ENOMEM; @@ -3103,7 +3104,7 @@ static u64 mlx4_enable_sriov(struct mlx4_dev *dev, struct pci_dev *pdev, } } - dev->dev_vfs = kzalloc(total_vfs * sizeof(*dev->dev_vfs), GFP_KERNEL); + dev->dev_vfs = kcalloc(total_vfs, sizeof(*dev->dev_vfs), GFP_KERNEL); if (NULL == dev->dev_vfs) { mlx4_err(dev, "Failed to allocate memory for VFs\n"); goto disable_sriov; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index b0e11255a355..7b1b5ac986d0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -487,7 +487,7 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev) int max_vfs_guarantee_counter = get_max_gauranteed_vfs_counter(dev); priv->mfunc.master.res_tracker.slave_list = - kzalloc(dev->num_slaves * sizeof(struct slave_list), + kcalloc(dev->num_slaves, sizeof(struct slave_list), GFP_KERNEL); if (!priv->mfunc.master.res_tracker.slave_list) return -ENOMEM; @@ -514,14 +514,14 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev) sizeof(int), GFP_KERNEL); if (i == RES_MAC || i == RES_VLAN) - res_alloc->allocated = kzalloc(MLX4_MAX_PORTS * - (dev->persist->num_vfs - + 1) * - sizeof(int), GFP_KERNEL); + res_alloc->allocated = + kcalloc(MLX4_MAX_PORTS * + (dev->persist->num_vfs + 1), + sizeof(int), GFP_KERNEL); else - res_alloc->allocated = kzalloc((dev->persist-> - num_vfs + 1) * - sizeof(int), GFP_KERNEL); + res_alloc->allocated = + kcalloc(dev->persist->num_vfs + 1, + sizeof(int), GFP_KERNEL); /* Reduce the sink counter */ if (i == RES_COUNTER) res_alloc->res_free = dev->caps.max_counters - 1; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c index a0433b48e833..5645a4facad2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c @@ -381,7 +381,7 @@ int mlx5_fpga_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters, count = mlx5_fpga_ipsec_counters_count(mdev); - data = kzalloc(sizeof(*data) * count * 2, GFP_KERNEL); + data = kzalloc(array3_size(sizeof(*data), count, 2), GFP_KERNEL); if (!data) { ret = -ENOMEM; goto out; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c index 857035583ccd..1e062e6b2587 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c @@ -394,8 +394,9 @@ static int mlx5_init_pin_config(struct mlx5_clock *clock) int i; clock->ptp_info.pin_config = - kzalloc(sizeof(*clock->ptp_info.pin_config) * - clock->ptp_info.n_pins, GFP_KERNEL); + kcalloc(clock->ptp_info.n_pins, + sizeof(*clock->ptp_info.pin_config), + GFP_KERNEL); if (!clock->ptp_info.pin_config) return -ENOMEM; clock->ptp_info.enable = mlx5_ptp_enable; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c index 91262b0573e3..cad603c35271 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c @@ -740,7 +740,8 @@ int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff; mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; - mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc) * IEEE_8021QAZ_MAX_TCS, + mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS, + sizeof(*mlxsw_sp_qdisc), GFP_KERNEL); if (!mlxsw_sp_qdisc) goto err_tclass_qdiscs_init; diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 52207508744c..b72d1bd11296 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -4372,7 +4372,7 @@ static void ksz_update_timer(struct ksz_timer_info *info) */ static int ksz_alloc_soft_desc(struct ksz_desc_info *desc_info, int transmit) { - desc_info->ring = kzalloc(sizeof(struct ksz_desc) * desc_info->alloc, + desc_info->ring = kcalloc(desc_info->alloc, sizeof(struct ksz_desc), GFP_KERNEL); if (!desc_info->ring) return 1; diff --git a/drivers/net/ethernet/neterion/vxge/vxge-config.c b/drivers/net/ethernet/neterion/vxge/vxge-config.c index c60da9e8bf14..8d0295655933 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-config.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-config.c @@ -2220,22 +2220,22 @@ __vxge_hw_channel_allocate(struct __vxge_hw_vpath_handle *vph, channel->length = length; channel->vp_id = vp_id; - channel->work_arr = kzalloc(sizeof(void *)*length, GFP_KERNEL); + channel->work_arr = kcalloc(length, sizeof(void *), GFP_KERNEL); if (channel->work_arr == NULL) goto exit1; - channel->free_arr = kzalloc(sizeof(void *)*length, GFP_KERNEL); + channel->free_arr = kcalloc(length, sizeof(void *), GFP_KERNEL); if (channel->free_arr == NULL) goto exit1; channel->free_ptr = length; - channel->reserve_arr = kzalloc(sizeof(void *)*length, GFP_KERNEL); + channel->reserve_arr = kcalloc(length, sizeof(void *), GFP_KERNEL); if (channel->reserve_arr == NULL) goto exit1; channel->reserve_ptr = length; channel->reserve_top = 0; - channel->orig_arr = kzalloc(sizeof(void *)*length, GFP_KERNEL); + channel->orig_arr = kcalloc(length, sizeof(void *), GFP_KERNEL); if (channel->orig_arr == NULL) goto exit1; diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index a8918bb7c802..5ae3fa82909f 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -3429,8 +3429,8 @@ static int vxge_device_register(struct __vxge_hw_device *hldev, vxge_initialize_ethtool_ops(ndev); /* Allocate memory for vpath */ - vdev->vpaths = kzalloc((sizeof(struct vxge_vpath)) * - no_of_vpath, GFP_KERNEL); + vdev->vpaths = kcalloc(no_of_vpath, sizeof(struct vxge_vpath), + GFP_KERNEL); if (!vdev->vpaths) { vxge_debug_init(VXGE_ERR, "%s: vpath memory allocation failed", diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index 07a2eb3781b1..8a31a02c9f47 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -390,8 +390,9 @@ static int pasemi_mac_setup_rx_resources(const struct net_device *dev) spin_lock_init(&ring->lock); ring->size = RX_RING_SIZE; - ring->ring_info = kzalloc(sizeof(struct pasemi_mac_buffer) * - RX_RING_SIZE, GFP_KERNEL); + ring->ring_info = kcalloc(RX_RING_SIZE, + sizeof(struct pasemi_mac_buffer), + GFP_KERNEL); if (!ring->ring_info) goto out_ring_info; @@ -473,8 +474,9 @@ pasemi_mac_setup_tx_resources(const struct net_device *dev) spin_lock_init(&ring->lock); ring->size = TX_RING_SIZE; - ring->ring_info = kzalloc(sizeof(struct pasemi_mac_buffer) * - TX_RING_SIZE, GFP_KERNEL); + ring->ring_info = kcalloc(TX_RING_SIZE, + sizeof(struct pasemi_mac_buffer), + GFP_KERNEL); if (!ring->ring_info) goto out_ring_info; diff --git a/drivers/net/ethernet/qlogic/qed/qed_debug.c b/drivers/net/ethernet/qlogic/qed/qed_debug.c index b9ec460dd996..a14e48489029 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_debug.c +++ b/drivers/net/ethernet/qlogic/qed/qed_debug.c @@ -6617,7 +6617,8 @@ static enum dbg_status qed_mcp_trace_alloc_meta(struct qed_hwfn *p_hwfn, /* Read no. of modules and allocate memory for their pointers */ meta->modules_num = qed_read_byte_from_buf(meta_buf_bytes, &offset); - meta->modules = kzalloc(meta->modules_num * sizeof(char *), GFP_KERNEL); + meta->modules = kcalloc(meta->modules_num, sizeof(char *), + GFP_KERNEL); if (!meta->modules) return DBG_STATUS_VIRT_MEM_ALLOC_FAILED; @@ -6645,7 +6646,7 @@ static enum dbg_status qed_mcp_trace_alloc_meta(struct qed_hwfn *p_hwfn, /* Read number of formats and allocate memory for all formats */ meta->formats_num = qed_read_dword_from_buf(meta_buf_bytes, &offset); - meta->formats = kzalloc(meta->formats_num * + meta->formats = kcalloc(meta->formats_num, sizeof(struct mcp_trace_format), GFP_KERNEL); if (!meta->formats) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index b285edc8d6a1..329781cda77f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -814,26 +814,26 @@ static int qed_alloc_qm_data(struct qed_hwfn *p_hwfn) if (rc) goto alloc_err; - qm_info->qm_pq_params = kzalloc(sizeof(*qm_info->qm_pq_params) * - qed_init_qm_get_num_pqs(p_hwfn), + qm_info->qm_pq_params = kcalloc(qed_init_qm_get_num_pqs(p_hwfn), + sizeof(*qm_info->qm_pq_params), GFP_KERNEL); if (!qm_info->qm_pq_params) goto alloc_err; - qm_info->qm_vport_params = kzalloc(sizeof(*qm_info->qm_vport_params) * - qed_init_qm_get_num_vports(p_hwfn), + qm_info->qm_vport_params = kcalloc(qed_init_qm_get_num_vports(p_hwfn), + sizeof(*qm_info->qm_vport_params), GFP_KERNEL); if (!qm_info->qm_vport_params) goto alloc_err; - qm_info->qm_port_params = kzalloc(sizeof(*qm_info->qm_port_params) * - p_hwfn->cdev->num_ports_in_engine, + qm_info->qm_port_params = kcalloc(p_hwfn->cdev->num_ports_in_engine, + sizeof(*qm_info->qm_port_params), GFP_KERNEL); if (!qm_info->qm_port_params) goto alloc_err; - qm_info->wfq_data = kzalloc(sizeof(*qm_info->wfq_data) * - qed_init_qm_get_num_vports(p_hwfn), + qm_info->wfq_data = kcalloc(qed_init_qm_get_num_vports(p_hwfn), + sizeof(*qm_info->wfq_data), GFP_KERNEL); if (!qm_info->wfq_data) goto alloc_err; diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_ops.c b/drivers/net/ethernet/qlogic/qed/qed_init_ops.c index 3bb76da6baa2..d9ab5add27a8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_init_ops.c +++ b/drivers/net/ethernet/qlogic/qed/qed_init_ops.c @@ -149,12 +149,12 @@ int qed_init_alloc(struct qed_hwfn *p_hwfn) if (IS_VF(p_hwfn->cdev)) return 0; - rt_data->b_valid = kzalloc(sizeof(bool) * RUNTIME_ARRAY_SIZE, + rt_data->b_valid = kcalloc(RUNTIME_ARRAY_SIZE, sizeof(bool), GFP_KERNEL); if (!rt_data->b_valid) return -ENOMEM; - rt_data->init_val = kzalloc(sizeof(u32) * RUNTIME_ARRAY_SIZE, + rt_data->init_val = kcalloc(RUNTIME_ARRAY_SIZE, sizeof(u32), GFP_KERNEL); if (!rt_data->init_val) { kfree(rt_data->b_valid); diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 1f6ac848109d..de1c70843efd 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -98,7 +98,7 @@ int qed_l2_alloc(struct qed_hwfn *p_hwfn) p_l2_info->queues = max_t(u8, rx, tx); } - pp_qids = kzalloc(sizeof(unsigned long *) * p_l2_info->queues, + pp_qids = kcalloc(p_l2_info->queues, sizeof(unsigned long *), GFP_KERNEL); if (!pp_qids) return -ENOMEM; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 1b5f7d57b6f8..8c6724063231 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -1025,15 +1025,17 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter) act_pci_func = ahw->total_nic_func; - adapter->npars = kzalloc(sizeof(struct qlcnic_npar_info) * - act_pci_func, GFP_KERNEL); + adapter->npars = kcalloc(act_pci_func, + sizeof(struct qlcnic_npar_info), + GFP_KERNEL); if (!adapter->npars) { ret = -ENOMEM; goto err_pci_info; } - adapter->eswitch = kzalloc(sizeof(struct qlcnic_eswitch) * - QLCNIC_NIU_MAX_XG_PORTS, GFP_KERNEL); + adapter->eswitch = kcalloc(QLCNIC_NIU_MAX_XG_PORTS, + sizeof(struct qlcnic_eswitch), + GFP_KERNEL); if (!adapter->eswitch) { ret = -ENOMEM; goto err_npars; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index c58180f40844..0c744b9c6e0a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -157,8 +157,8 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) adapter->ahw->sriov = sriov; sriov->num_vfs = num_vfs; bc = &sriov->bc; - sriov->vf_info = kzalloc(sizeof(struct qlcnic_vf_info) * - num_vfs, GFP_KERNEL); + sriov->vf_info = kcalloc(num_vfs, sizeof(struct qlcnic_vf_info), + GFP_KERNEL); if (!sriov->vf_info) { err = -ENOMEM; goto qlcnic_free_sriov; @@ -450,7 +450,7 @@ static int qlcnic_sriov_set_guest_vlan_mode(struct qlcnic_adapter *adapter, return 0; num_vlans = sriov->num_allowed_vlans; - sriov->allowed_vlans = kzalloc(sizeof(u16) * num_vlans, GFP_KERNEL); + sriov->allowed_vlans = kcalloc(num_vlans, sizeof(u16), GFP_KERNEL); if (!sriov->allowed_vlans) return -ENOMEM; @@ -706,7 +706,7 @@ static inline int qlcnic_sriov_alloc_bc_trans(struct qlcnic_bc_trans **trans) static inline int qlcnic_sriov_alloc_bc_msg(struct qlcnic_bc_hdr **hdr, u32 size) { - *hdr = kzalloc(sizeof(struct qlcnic_bc_hdr) * size, GFP_ATOMIC); + *hdr = kcalloc(size, sizeof(struct qlcnic_bc_hdr), GFP_ATOMIC); if (!*hdr) return -ENOMEM; diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index ce8071fc90c4..e080d3e7c582 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -973,7 +973,7 @@ static int netsec_alloc_dring(struct netsec_priv *priv, enum ring_id id) goto err; } - dring->desc = kzalloc(DESC_NUM * sizeof(*dring->desc), GFP_KERNEL); + dring->desc = kcalloc(DESC_NUM, sizeof(*dring->desc), GFP_KERNEL); if (!dring->desc) { ret = -ENOMEM; goto err; diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c index eed18f88bdff..302079e22b06 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c @@ -2320,8 +2320,9 @@ static struct net_device *gelic_wl_alloc(struct gelic_card *card) pr_debug("%s: wl=%p port=%p\n", __func__, wl, port); /* allocate scan list */ - wl->networks = kzalloc(sizeof(struct gelic_wl_scan_info) * - GELIC_WL_BSS_MAX_ENT, GFP_KERNEL); + wl->networks = kcalloc(GELIC_WL_BSS_MAX_ENT, + sizeof(struct gelic_wl_scan_info), + GFP_KERNEL); if (!wl->networks) goto fail_bss; diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index a6c87793d899..79e9b103188b 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -1097,8 +1097,9 @@ static struct dp83640_clock *dp83640_clock_get_bus(struct mii_bus *bus) if (!clock) goto out; - clock->caps.pin_config = kzalloc(sizeof(struct ptp_pin_desc) * - DP83640_N_PINS, GFP_KERNEL); + clock->caps.pin_config = kcalloc(DP83640_N_PINS, + sizeof(struct ptp_pin_desc), + GFP_KERNEL); if (!clock->caps.pin_config) { kfree(clock); clock = NULL; diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index 8940417c30e5..b008266e91ea 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -1307,7 +1307,7 @@ static int __init slip_init(void) printk(KERN_INFO "SLIP linefill/keepalive option.\n"); #endif - slip_devs = kzalloc(sizeof(struct net_device *)*slip_maxdev, + slip_devs = kcalloc(slip_maxdev, sizeof(struct net_device *), GFP_KERNEL); if (!slip_devs) return -ENOMEM; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ca0af0e15a2c..b070959737ff 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -280,7 +280,7 @@ static int __team_options_register(struct team *team, struct team_option **dst_opts; int err; - dst_opts = kzalloc(sizeof(struct team_option *) * option_count, + dst_opts = kcalloc(option_count, sizeof(struct team_option *), GFP_KERNEL); if (!dst_opts) return -ENOMEM; diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 309b88acd3d0..06b4d290784d 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1661,7 +1661,7 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) } if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { - u32 *filter_mask = kzalloc(sizeof(u32) * 32, GFP_KERNEL); + u32 *filter_mask = kcalloc(32, sizeof(u32), GFP_KERNEL); u32 command[2]; u32 offset[2]; u32 crc[4]; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 15b9a83bbd9d..b6c9a2af3732 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2552,7 +2552,7 @@ static int virtnet_find_vqs(struct virtnet_info *vi) virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ); /* Allocate space for find_vqs parameters */ - vqs = kzalloc(total_vqs * sizeof(*vqs), GFP_KERNEL); + vqs = kcalloc(total_vqs, sizeof(*vqs), GFP_KERNEL); if (!vqs) goto err_vq; callbacks = kmalloc_array(total_vqs, sizeof(*callbacks), GFP_KERNEL); @@ -2562,7 +2562,7 @@ static int virtnet_find_vqs(struct virtnet_info *vi) if (!names) goto err_names; if (!vi->big_packets || vi->mergeable_rx_bufs) { - ctx = kzalloc(total_vqs * sizeof(*ctx), GFP_KERNEL); + ctx = kcalloc(total_vqs, sizeof(*ctx), GFP_KERNEL); if (!ctx) goto err_ctx; } else { @@ -2626,10 +2626,10 @@ static int virtnet_alloc_queues(struct virtnet_info *vi) vi->ctrl = kzalloc(sizeof(*vi->ctrl), GFP_KERNEL); if (!vi->ctrl) goto err_ctrl; - vi->sq = kzalloc(sizeof(*vi->sq) * vi->max_queue_pairs, GFP_KERNEL); + vi->sq = kcalloc(vi->max_queue_pairs, sizeof(*vi->sq), GFP_KERNEL); if (!vi->sq) goto err_sq; - vi->rq = kzalloc(sizeof(*vi->rq) * vi->max_queue_pairs, GFP_KERNEL); + vi->rq = kcalloc(vi->max_queue_pairs, sizeof(*vi->rq), GFP_KERNEL); if (!vi->rq) goto err_rq; diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c index 4205dfd19da3..9b09c9d0d0fb 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -198,12 +198,14 @@ static int uhdlc_init(struct ucc_hdlc_private *priv) goto free_tx_bd; } - priv->rx_skbuff = kzalloc(priv->rx_ring_size * sizeof(*priv->rx_skbuff), + priv->rx_skbuff = kcalloc(priv->rx_ring_size, + sizeof(*priv->rx_skbuff), GFP_KERNEL); if (!priv->rx_skbuff) goto free_ucc_pram; - priv->tx_skbuff = kzalloc(priv->tx_ring_size * sizeof(*priv->tx_skbuff), + priv->tx_skbuff = kcalloc(priv->tx_ring_size, + sizeof(*priv->tx_skbuff), GFP_KERNEL); if (!priv->tx_skbuff) goto free_rx_skbuff; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index bd23f6940488..c72d8af122a2 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -582,7 +582,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) } htt->rx_ring.netbufs_ring = - kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *), + kcalloc(htt->rx_ring.size, sizeof(struct sk_buff *), GFP_KERNEL); if (!htt->rx_ring.netbufs_ring) goto err_netbuf; diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 2e34a1fc5ba6..8c49a26fc571 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -155,7 +155,7 @@ ath10k_wmi_tlv_parse_alloc(struct ath10k *ar, const void *ptr, const void **tb; int ret; - tb = kzalloc(sizeof(*tb) * WMI_TLV_TAG_MAX, gfp); + tb = kcalloc(WMI_TLV_TAG_MAX, sizeof(*tb), gfp); if (!tb) return ERR_PTR(-ENOMEM); diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 2ba8cf3f38af..0687697d5e2d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1041,7 +1041,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, n_channels = request->n_channels; - channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL); + channels = kcalloc(n_channels, sizeof(u16), GFP_KERNEL); if (channels == NULL) { ath6kl_warn("failed to set scan channels, scan all channels"); n_channels = 0; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 29e93c953d93..7f1bdea742b8 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1958,7 +1958,7 @@ static int carl9170_parse_eeprom(struct ar9170 *ar) if (!bands) return -EINVAL; - ar->survey = kzalloc(sizeof(struct survey_info) * chans, GFP_KERNEL); + ar->survey = kcalloc(chans, sizeof(struct survey_info), GFP_KERNEL); if (!ar->survey) return -ENOMEM; ar->num_channels = chans; @@ -1988,8 +1988,9 @@ int carl9170_register(struct ar9170 *ar) if (WARN_ON(ar->mem_bitmap)) return -EINVAL; - ar->mem_bitmap = kzalloc(roundup(ar->fw.mem_blocks, BITS_PER_LONG) * - sizeof(unsigned long), GFP_KERNEL); + ar->mem_bitmap = kcalloc(roundup(ar->fw.mem_blocks, BITS_PER_LONG), + sizeof(unsigned long), + GFP_KERNEL); if (!ar->mem_bitmap) return -ENOMEM; diff --git a/drivers/net/wireless/broadcom/b43/phy_n.c b/drivers/net/wireless/broadcom/b43/phy_n.c index f2a2f41e3c96..44ab080d6518 100644 --- a/drivers/net/wireless/broadcom/b43/phy_n.c +++ b/drivers/net/wireless/broadcom/b43/phy_n.c @@ -1518,7 +1518,7 @@ static int b43_nphy_load_samples(struct b43_wldev *dev, u16 i; u32 *data; - data = kzalloc(len * sizeof(u32), GFP_KERNEL); + data = kcalloc(len, sizeof(u32), GFP_KERNEL); if (!data) { b43err(dev->wl, "allocation for samples loading failed\n"); return -ENOMEM; diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index f1e3dad57629..55f411925960 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -3300,8 +3300,8 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev) if ((phy->type == B43legacy_PHYTYPE_B) || (phy->type == B43legacy_PHYTYPE_G)) { - phy->_lo_pairs = kzalloc(sizeof(struct b43legacy_lopair) - * B43legacy_LO_COUNT, + phy->_lo_pairs = kcalloc(B43legacy_LO_COUNT, + sizeof(struct b43legacy_lopair), GFP_KERNEL); if (!phy->_lo_pairs) return -ENOMEM; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 49d37ad96958..c40ba8855cd5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -1486,8 +1486,9 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) (struct brcmf_commonring **)if_msgbuf->commonrings; msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; msgbuf->max_flowrings = if_msgbuf->max_flowrings; - msgbuf->flowring_dma_handle = kzalloc(msgbuf->max_flowrings * - sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL); + msgbuf->flowring_dma_handle = + kcalloc(msgbuf->max_flowrings, + sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL); if (!msgbuf->flowring_dma_handle) goto fail; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 4b2149b48362..3e9c4f2f5dd1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -1058,7 +1058,7 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) channel_cnt = AF_PEER_SEARCH_CNT; else channel_cnt = SOCIAL_CHAN_CNT; - default_chan_list = kzalloc(channel_cnt * sizeof(*default_chan_list), + default_chan_list = kcalloc(channel_cnt, sizeof(*default_chan_list), GFP_KERNEL); if (default_chan_list == NULL) { brcmf_err("channel list allocation failed\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c index 0a14942b8216..7d4e8f589fdc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c @@ -507,7 +507,7 @@ brcms_c_attach_malloc(uint unit, uint *err, uint devid) wlc->hw->wlc = wlc; wlc->hw->bandstate[0] = - kzalloc(sizeof(struct brcms_hw_band) * MAXBANDS, GFP_ATOMIC); + kcalloc(MAXBANDS, sizeof(struct brcms_hw_band), GFP_ATOMIC); if (wlc->hw->bandstate[0] == NULL) { *err = 1006; goto fail; @@ -521,7 +521,8 @@ brcms_c_attach_malloc(uint unit, uint *err, uint devid) } wlc->modulecb = - kzalloc(sizeof(struct modulecb) * BRCMS_MAXMODULES, GFP_ATOMIC); + kcalloc(BRCMS_MAXMODULES, sizeof(struct modulecb), + GFP_ATOMIC); if (wlc->modulecb == NULL) { *err = 1009; goto fail; @@ -553,7 +554,7 @@ brcms_c_attach_malloc(uint unit, uint *err, uint devid) } wlc->bandstate[0] = - kzalloc(sizeof(struct brcms_band)*MAXBANDS, GFP_ATOMIC); + kcalloc(MAXBANDS, sizeof(struct brcms_band), GFP_ATOMIC); if (wlc->bandstate[0] == NULL) { *err = 1025; goto fail; diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c index 063e19ced7c8..6514baf799fe 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.c +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -922,7 +922,7 @@ il_init_channel_map(struct il_priv *il) D_EEPROM("Parsing data for %d channels.\n", il->channel_count); il->channel_info = - kzalloc(sizeof(struct il_channel_info) * il->channel_count, + kcalloc(il->channel_count, sizeof(struct il_channel_info), GFP_KERNEL); if (!il->channel_info) { IL_ERR("Could not allocate channel_info\n"); @@ -3041,9 +3041,9 @@ il_tx_queue_init(struct il_priv *il, u32 txq_id) } txq->meta = - kzalloc(sizeof(struct il_cmd_meta) * actual_slots, GFP_KERNEL); + kcalloc(actual_slots, sizeof(struct il_cmd_meta), GFP_KERNEL); txq->cmd = - kzalloc(sizeof(struct il_device_cmd *) * actual_slots, GFP_KERNEL); + kcalloc(actual_slots, sizeof(struct il_device_cmd *), GFP_KERNEL); if (!txq->meta || !txq->cmd) goto out_free_arrays; @@ -3455,7 +3455,7 @@ il_init_geos(struct il_priv *il) } channels = - kzalloc(sizeof(struct ieee80211_channel) * il->channel_count, + kcalloc(il->channel_count, sizeof(struct ieee80211_channel), GFP_KERNEL); if (!channels) return -ENOMEM; @@ -4654,8 +4654,9 @@ il_alloc_txq_mem(struct il_priv *il) { if (!il->txq) il->txq = - kzalloc(sizeof(struct il_tx_queue) * - il->cfg->num_of_queues, GFP_KERNEL); + kcalloc(il->cfg->num_of_queues, + sizeof(struct il_tx_queue), + GFP_KERNEL); if (!il->txq) { IL_ERR("Not enough memory for txq\n"); return -ENOMEM; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 4b3753d78d03..11ecdf63b732 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -564,7 +564,7 @@ iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, else blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; - blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL); + blacklist = kcalloc(blacklist_len, sizeof(*blacklist), GFP_KERNEL); if (!blacklist) return -ENOMEM; diff --git a/drivers/net/wireless/intersil/p54/eeprom.c b/drivers/net/wireless/intersil/p54/eeprom.c index d4c73d39336f..de2ef95c386c 100644 --- a/drivers/net/wireless/intersil/p54/eeprom.c +++ b/drivers/net/wireless/intersil/p54/eeprom.c @@ -161,8 +161,9 @@ static int p54_generate_band(struct ieee80211_hw *dev, if (!tmp) goto err_out; - tmp->channels = kzalloc(sizeof(struct ieee80211_channel) * - list->band_channel_num[band], GFP_KERNEL); + tmp->channels = kcalloc(list->band_channel_num[band], + sizeof(struct ieee80211_channel), + GFP_KERNEL); if (!tmp->channels) goto err_out; @@ -344,7 +345,7 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) goto free; } priv->chan_num = max_channel_num; - priv->survey = kzalloc(sizeof(struct survey_info) * max_channel_num, + priv->survey = kcalloc(max_channel_num, sizeof(struct survey_info), GFP_KERNEL); if (!priv->survey) { ret = -ENOMEM; @@ -352,8 +353,9 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) } list->max_entries = max_channel_num; - list->channels = kzalloc(sizeof(struct p54_channel_entry) * - max_channel_num, GFP_KERNEL); + list->channels = kcalloc(max_channel_num, + sizeof(struct p54_channel_entry), + GFP_KERNEL); if (!list->channels) { ret = -ENOMEM; goto free; diff --git a/drivers/net/wireless/intersil/prism54/oid_mgt.c b/drivers/net/wireless/intersil/prism54/oid_mgt.c index 6528ed5b9b1d..6d57e1cbcc07 100644 --- a/drivers/net/wireless/intersil/prism54/oid_mgt.c +++ b/drivers/net/wireless/intersil/prism54/oid_mgt.c @@ -244,7 +244,7 @@ mgt_init(islpci_private *priv) /* Alloc the cache */ for (i = 0; i < OID_NUM_LAST; i++) { if (isl_oid[i].flags & OID_FLAG_CACHED) { - priv->mib[i] = kzalloc(isl_oid[i].size * + priv->mib[i] = kcalloc(isl_oid[i].size, (isl_oid[i].range + 1), GFP_KERNEL); if (!priv->mib[i]) diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index 1edcddaf7b4b..7ab44cd32a9d 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -399,8 +399,8 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, new_node->win_size = win_size; - new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, - GFP_KERNEL); + new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *), + GFP_KERNEL); if (!new_node->rx_reorder_ptr) { kfree((u8 *) new_node); mwifiex_dbg(priv->adapter, ERROR, diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 47d2dcc3f28f..dfdcbc4f141a 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -2106,15 +2106,16 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) return -ENOMEM; /* Allocate skb pointer buffers */ - card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * - card->mp_agg_pkt_limit, GFP_KERNEL); + card->mpa_rx.skb_arr = kcalloc(card->mp_agg_pkt_limit, sizeof(void *), + GFP_KERNEL); if (!card->mpa_rx.skb_arr) { kfree(card->mp_regs); return -ENOMEM; } - card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * - card->mp_agg_pkt_limit, GFP_KERNEL); + card->mpa_rx.len_arr = kcalloc(card->mp_agg_pkt_limit, + sizeof(*card->mpa_rx.len_arr), + GFP_KERNEL); if (!card->mpa_rx.len_arr) { kfree(card->mp_regs); kfree(card->mpa_rx.skb_arr); diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index 5eb143667539..c5d94a95e21a 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -1216,7 +1216,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, return -EINVAL; } - limits = kzalloc(sizeof(*limits) * rec->n_limits, + limits = kcalloc(rec->n_limits, sizeof(*limits), GFP_KERNEL); if (!limits) return -ENOMEM; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c index 0eee479583b8..acc399b5574e 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c @@ -397,7 +397,7 @@ static ssize_t rt2x00debug_read_crypto_stats(struct file *file, if (*offset) return 0; - data = kzalloc((1 + CIPHER_MAX) * MAX_LINE_LENGTH, GFP_KERNEL); + data = kcalloc(1 + CIPHER_MAX, MAX_LINE_LENGTH, GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/drivers/net/wireless/realtek/rtlwifi/efuse.c b/drivers/net/wireless/realtek/rtlwifi/efuse.c index fd13d4ef53b8..9729e51fce38 100644 --- a/drivers/net/wireless/realtek/rtlwifi/efuse.c +++ b/drivers/net/wireless/realtek/rtlwifi/efuse.c @@ -258,8 +258,8 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf) } /* allocate memory for efuse_tbl and efuse_word */ - efuse_tbl = kzalloc(rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE] * - sizeof(u8), GFP_ATOMIC); + efuse_tbl = kzalloc(rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE], + GFP_ATOMIC); if (!efuse_tbl) return; efuse_word = kcalloc(EFUSE_MAX_WORD_UNIT, sizeof(u16 *), GFP_ATOMIC); diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c index ce3103bb8ebb..f9faffc498bc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.c +++ b/drivers/net/wireless/realtek/rtlwifi/usb.c @@ -1048,7 +1048,7 @@ int rtl_usb_probe(struct usb_interface *intf, } rtlpriv = hw->priv; rtlpriv->hw = hw; - rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32), + rtlpriv->usb_data = kcalloc(RTL_USB_MAX_RX_COUNT, sizeof(u32), GFP_KERNEL); if (!rtlpriv->usb_data) return -ENOMEM; diff --git a/drivers/net/wireless/st/cw1200/queue.c b/drivers/net/wireless/st/cw1200/queue.c index 5153d2cfd991..7c31b63b8258 100644 --- a/drivers/net/wireless/st/cw1200/queue.c +++ b/drivers/net/wireless/st/cw1200/queue.c @@ -154,7 +154,7 @@ int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, spin_lock_init(&stats->lock); init_waitqueue_head(&stats->wait_link_id_empty); - stats->link_map_cache = kzalloc(sizeof(int) * map_capacity, + stats->link_map_cache = kcalloc(map_capacity, sizeof(int), GFP_KERNEL); if (!stats->link_map_cache) return -ENOMEM; @@ -181,13 +181,13 @@ int cw1200_queue_init(struct cw1200_queue *queue, spin_lock_init(&queue->lock); timer_setup(&queue->gc, cw1200_queue_gc, 0); - queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, - GFP_KERNEL); + queue->pool = kcalloc(capacity, sizeof(struct cw1200_queue_item), + GFP_KERNEL); if (!queue->pool) return -ENOMEM; - queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity, - GFP_KERNEL); + queue->link_map_cache = kcalloc(stats->map_capacity, sizeof(int), + GFP_KERNEL); if (!queue->link_map_cache) { kfree(queue->pool); queue->pool = NULL; diff --git a/drivers/net/wireless/st/cw1200/scan.c b/drivers/net/wireless/st/cw1200/scan.c index cc2ce60f4f09..67213f11acbd 100644 --- a/drivers/net/wireless/st/cw1200/scan.c +++ b/drivers/net/wireless/st/cw1200/scan.c @@ -230,9 +230,9 @@ void cw1200_scan_work(struct work_struct *work) scan.type = WSM_SCAN_TYPE_BACKGROUND; scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; } - scan.ch = kzalloc( - sizeof(struct wsm_scan_ch) * (it - priv->scan.curr), - GFP_KERNEL); + scan.ch = kcalloc(it - priv->scan.curr, + sizeof(struct wsm_scan_ch), + GFP_KERNEL); if (!scan.ch) { priv->scan.status = -ENOMEM; goto fail; diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index b3b0b648be62..146de9489339 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c @@ -122,7 +122,8 @@ static int rockchip_rk3328_efuse_read(void *context, unsigned int offset, addr_offset = offset % RK3399_NBYTES; addr_len = addr_end - addr_start; - buf = kzalloc(sizeof(*buf) * addr_len * RK3399_NBYTES, GFP_KERNEL); + buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)), + GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto nomem; @@ -174,7 +175,8 @@ static int rockchip_rk3399_efuse_read(void *context, unsigned int offset, addr_offset = offset % RK3399_NBYTES; addr_len = addr_end - addr_start; - buf = kzalloc(sizeof(*buf) * addr_len * RK3399_NBYTES, GFP_KERNEL); + buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)), + GFP_KERNEL); if (!buf) { clk_disable_unprepare(efuse->clk); return -ENOMEM; diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 26bb637afe92..d020f89248fd 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -185,7 +185,7 @@ static int sunxi_sid_probe(struct platform_device *pdev) if (IS_ERR(nvmem)) return PTR_ERR(nvmem); - randomness = kzalloc(sizeof(u8) * (size), GFP_KERNEL); + randomness = kzalloc(size, GFP_KERNEL); if (!randomness) { ret = -EINVAL; goto err_unreg_nvmem; diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 0b49a62b38a3..14cc962e0eec 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -129,7 +129,7 @@ struct platform_device *of_device_alloc(struct device_node *np, /* Populate the resource table */ if (num_irq || num_reg) { - res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); + res = kcalloc(num_irq + num_reg, sizeof(*res), GFP_KERNEL); if (!res) { platform_device_put(dev); return NULL; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index ecee50d10d14..722537e14848 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -156,7 +156,7 @@ static void __init of_unittest_dynamic(void) } /* Array of 4 properties for the purpose of testing */ - prop = kzalloc(sizeof(*prop) * 4, GFP_KERNEL); + prop = kcalloc(4, sizeof(*prop), GFP_KERNEL); if (!prop) { unittest(0, "kzalloc() failed\n"); return; diff --git a/drivers/opp/ti-opp-supply.c b/drivers/opp/ti-opp-supply.c index 370eff3acd8a..9e5a9a3112c9 100644 --- a/drivers/opp/ti-opp-supply.c +++ b/drivers/opp/ti-opp-supply.c @@ -122,8 +122,8 @@ static int _store_optimized_voltages(struct device *dev, goto out; } - table = kzalloc(sizeof(*data->vdd_table) * - data->num_vdd_table, GFP_KERNEL); + table = kcalloc(data->num_vdd_table, sizeof(*data->vdd_table), + GFP_KERNEL); if (!table) { ret = -ENOMEM; goto out; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index f45b74fcc059..4d88afdfc843 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -474,7 +474,7 @@ static int populate_msi_sysfs(struct pci_dev *pdev) return 0; /* Dynamically create the MSI attributes for the PCI device */ - msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL); + msi_attrs = kcalloc(num_msi + 1, sizeof(void *), GFP_KERNEL); if (!msi_attrs) return -ENOMEM; for_each_pci_msi_entry(entry, pdev) { @@ -501,7 +501,7 @@ static int populate_msi_sysfs(struct pci_dev *pdev) msi_irq_group->name = "msi_irqs"; msi_irq_group->attrs = msi_attrs; - msi_irq_groups = kzalloc(sizeof(void *) * 2, GFP_KERNEL); + msi_irq_groups = kcalloc(2, sizeof(void *), GFP_KERNEL); if (!msi_irq_groups) goto error_irq_group; msi_irq_groups[0] = msi_irq_group; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 788a200fb2dc..0c4653c1d2ce 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1076,7 +1076,7 @@ void pci_create_legacy_files(struct pci_bus *b) { int error; - b->legacy_io = kzalloc(sizeof(struct bin_attribute) * 2, + b->legacy_io = kcalloc(2, sizeof(struct bin_attribute), GFP_ATOMIC); if (!b->legacy_io) goto kzalloc_err; diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index 959ae3e65ef8..f0af9985ca09 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -628,7 +628,7 @@ static int pd6729_pci_probe(struct pci_dev *dev, char configbyte; struct pd6729_socket *socket; - socket = kzalloc(sizeof(struct pd6729_socket) * MAX_SOCKETS, + socket = kcalloc(MAX_SOCKETS, sizeof(struct pd6729_socket), GFP_KERNEL); if (!socket) { dev_warn(&dev->dev, "failed to kzalloc socket.\n"); diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index 136ccaf53df8..fa530913a2c8 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -771,8 +771,8 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, maps_per_pin++; if (num_pulls) maps_per_pin++; - cur_map = maps = kzalloc(num_pins * maps_per_pin * sizeof(*maps), - GFP_KERNEL); + cur_map = maps = kcalloc(num_pins * maps_per_pin, sizeof(*maps), + GFP_KERNEL); if (!maps) return -ENOMEM; diff --git a/drivers/pinctrl/freescale/pinctrl-mxs.c b/drivers/pinctrl/freescale/pinctrl-mxs.c index 594f3e5ce9a9..3a17846aa31f 100644 --- a/drivers/pinctrl/freescale/pinctrl-mxs.c +++ b/drivers/pinctrl/freescale/pinctrl-mxs.c @@ -89,7 +89,7 @@ static int mxs_dt_node_to_map(struct pinctrl_dev *pctldev, if (!purecfg && config) new_num = 2; - new_map = kzalloc(sizeof(*new_map) * new_num, GFP_KERNEL); + new_map = kcalloc(new_num, sizeof(*new_map), GFP_KERNEL); if (!new_map) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-lantiq.c b/drivers/pinctrl/pinctrl-lantiq.c index 41dc39c7a7b1..81632af3a86a 100644 --- a/drivers/pinctrl/pinctrl-lantiq.c +++ b/drivers/pinctrl/pinctrl-lantiq.c @@ -158,7 +158,8 @@ static int ltq_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, for_each_child_of_node(np_config, np) max_maps += ltq_pinctrl_dt_subnode_size(np); - *map = kzalloc(max_maps * sizeof(struct pinctrl_map) * 2, GFP_KERNEL); + *map = kzalloc(array3_size(max_maps, sizeof(struct pinctrl_map), 2), + GFP_KERNEL); if (!*map) return -ENOMEM; tmp = *map; diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index ca2347d0d579..505845c66dd0 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -108,7 +108,7 @@ static int sirfsoc_dt_node_to_map(struct pinctrl_dev *pctldev, return -ENODEV; } - *map = kzalloc(sizeof(**map) * count, GFP_KERNEL); + *map = kcalloc(count, sizeof(**map), GFP_KERNEL); if (!*map) return -ENOMEM; diff --git a/drivers/pinctrl/spear/pinctrl-spear.c b/drivers/pinctrl/spear/pinctrl-spear.c index efe79d3f7659..c4f850345dc4 100644 --- a/drivers/pinctrl/spear/pinctrl-spear.c +++ b/drivers/pinctrl/spear/pinctrl-spear.c @@ -172,7 +172,7 @@ static int spear_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return -ENODEV; } - *map = kzalloc(sizeof(**map) * count, GFP_KERNEL); + *map = kcalloc(count, sizeof(**map), GFP_KERNEL); if (!*map) return -ENOMEM; diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 44459d28efd5..eaace8ec6afc 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -277,7 +277,7 @@ static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node, if (!configlen) return NULL; - pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); + pinconfig = kcalloc(configlen, sizeof(*pinconfig), GFP_KERNEL); if (!pinconfig) return ERR_PTR(-ENOMEM); diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.c b/drivers/pinctrl/vt8500/pinctrl-wmt.c index d73956bdc211..c08318a5a91b 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wmt.c +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c @@ -352,7 +352,7 @@ static int wmt_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, if (num_pulls) maps_per_pin++; - cur_map = maps = kzalloc(num_pins * maps_per_pin * sizeof(*maps), + cur_map = maps = kcalloc(num_pins * maps_per_pin, sizeof(*maps), GFP_KERNEL); if (!maps) return -ENOMEM; diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index 9d7dbd925065..d975462a4c57 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -458,19 +458,19 @@ static int alienware_zone_init(struct platform_device *dev) * - zone_data num_zones is for the distinct zones */ zone_dev_attrs = - kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1), + kcalloc(quirks->num_zones + 1, sizeof(struct device_attribute), GFP_KERNEL); if (!zone_dev_attrs) return -ENOMEM; zone_attrs = - kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2), + kcalloc(quirks->num_zones + 2, sizeof(struct attribute *), GFP_KERNEL); if (!zone_attrs) return -ENOMEM; zone_data = - kzalloc(sizeof(struct platform_zone) * (quirks->num_zones), + kcalloc(quirks->num_zones, sizeof(struct platform_zone), GFP_KERNEL); if (!zone_data) return -ENOMEM; diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index a0c95853fd3f..014fc1634a3d 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -964,12 +964,12 @@ static int ips_monitor(void *data) u16 *mcp_samples, *ctv1_samples, *ctv2_samples, *mch_samples; u8 cur_seqno, last_seqno; - mcp_samples = kzalloc(sizeof(u16) * IPS_SAMPLE_COUNT, GFP_KERNEL); - ctv1_samples = kzalloc(sizeof(u16) * IPS_SAMPLE_COUNT, GFP_KERNEL); - ctv2_samples = kzalloc(sizeof(u16) * IPS_SAMPLE_COUNT, GFP_KERNEL); - mch_samples = kzalloc(sizeof(u16) * IPS_SAMPLE_COUNT, GFP_KERNEL); - cpu_samples = kzalloc(sizeof(u32) * IPS_SAMPLE_COUNT, GFP_KERNEL); - mchp_samples = kzalloc(sizeof(u32) * IPS_SAMPLE_COUNT, GFP_KERNEL); + mcp_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u16), GFP_KERNEL); + ctv1_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u16), GFP_KERNEL); + ctv2_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u16), GFP_KERNEL); + mch_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u16), GFP_KERNEL); + cpu_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u32), GFP_KERNEL); + mchp_samples = kcalloc(IPS_SAMPLE_COUNT, sizeof(u32), GFP_KERNEL); if (!mcp_samples || !ctv1_samples || !ctv2_samples || !mch_samples || !cpu_samples || !mchp_samples) { dev_err(ips->dev, diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 5c39b3211709..8361ad75389a 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -571,7 +571,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) return -ENOMEM; } - pcc->sinf = kzalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL); + pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL); if (!pcc->sinf) { result = -ENOMEM; goto out_hotkey; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index da1ca4856ea1..ab2d28867c52 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6006,7 +6006,7 @@ static int __init led_init(struct ibm_init_struct *iibm) if (led_supported == TPACPI_LED_NONE) return 1; - tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, + tpacpi_leds = kcalloc(TPACPI_LED_NUMLEDS, sizeof(*tpacpi_leds), GFP_KERNEL); if (!tpacpi_leds) { pr_err("Out of memory for LED data\n"); diff --git a/drivers/power/supply/wm97xx_battery.c b/drivers/power/supply/wm97xx_battery.c index bd4f66651513..6754e761778a 100644 --- a/drivers/power/supply/wm97xx_battery.c +++ b/drivers/power/supply/wm97xx_battery.c @@ -201,7 +201,7 @@ static int wm97xx_bat_probe(struct platform_device *dev) if (pdata->min_voltage >= 0) props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ - prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); + prop = kcalloc(props, sizeof(*prop), GFP_KERNEL); if (!prop) { ret = -ENOMEM; goto err3; diff --git a/drivers/power/supply/z2_battery.c b/drivers/power/supply/z2_battery.c index 8a43b49cfd35..bcc2d1a9b0a7 100644 --- a/drivers/power/supply/z2_battery.c +++ b/drivers/power/supply/z2_battery.c @@ -146,7 +146,7 @@ static int z2_batt_ps_init(struct z2_charger *charger, int props) if (info->min_voltage >= 0) props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ - prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); + prop = kcalloc(props, sizeof(*prop), GFP_KERNEL); if (!prop) return -ENOMEM; diff --git a/drivers/powercap/powercap_sys.c b/drivers/powercap/powercap_sys.c index 64b2b2501a79..9e2f274bd44f 100644 --- a/drivers/powercap/powercap_sys.c +++ b/drivers/powercap/powercap_sys.c @@ -545,15 +545,16 @@ struct powercap_zone *powercap_register_zone( dev_set_name(&power_zone->dev, "%s:%x", dev_name(power_zone->dev.parent), power_zone->id); - power_zone->constraints = kzalloc(sizeof(*power_zone->constraints) * - nr_constraints, GFP_KERNEL); + power_zone->constraints = kcalloc(nr_constraints, + sizeof(*power_zone->constraints), + GFP_KERNEL); if (!power_zone->constraints) goto err_const_alloc; nr_attrs = nr_constraints * POWERCAP_CONSTRAINTS_ATTRS + POWERCAP_ZONE_MAX_ATTRS + 1; - power_zone->zone_dev_attrs = kzalloc(sizeof(void *) * - nr_attrs, GFP_KERNEL); + power_zone->zone_dev_attrs = kcalloc(nr_attrs, sizeof(void *), + GFP_KERNEL); if (!power_zone->zone_dev_attrs) goto err_attr_alloc; create_power_zone_common_attributes(power_zone); diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 161b927d9de1..fd7b517132ac 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -425,9 +425,9 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, rswitch = rdev->rswitch; rswitch->port_ok = 0; spin_lock_init(&rswitch->lock); - rswitch->route_table = kzalloc(sizeof(u8)* - RIO_MAX_ROUTE_ENTRIES(port->sys_size), - GFP_KERNEL); + rswitch->route_table = + kzalloc(RIO_MAX_ROUTE_ENTRIES(port->sys_size), + GFP_KERNEL); if (!rswitch->route_table) goto cleanup; /* Initialize switch route table */ diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 7726b874e539..b4e588cce03d 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -1162,7 +1162,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) } } - rdata = kzalloc(sizeof(*rdata) * rdev_num, GFP_KERNEL); + rdata = kcalloc(rdev_num, sizeof(*rdata), GFP_KERNEL); if (!rdata) return -ENOMEM; diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 29024492b8ed..ed607288e696 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -238,9 +238,9 @@ dcssblk_is_continuous(struct dcssblk_dev_info *dev_info) if (dev_info->num_of_segments <= 1) return 0; - sort_list = kzalloc( - sizeof(struct segment_info) * dev_info->num_of_segments, - GFP_KERNEL); + sort_list = kcalloc(dev_info->num_of_segments, + sizeof(struct segment_info), + GFP_KERNEL); if (sort_list == NULL) return -ENOMEM; i = 0; diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c index db1fbf9b00b5..79eb60958015 100644 --- a/drivers/s390/char/keyboard.c +++ b/drivers/s390/char/keyboard.c @@ -78,7 +78,7 @@ kbd_alloc(void) { } } kbd->fn_handler = - kzalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL); + kcalloc(NR_FN_HANDLER, sizeof(fn_handler_fn *), GFP_KERNEL); if (!kbd->fn_handler) goto out_func; kbd->accent_table = kmemdup(ebc_accent_table, diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 52aa89424318..cbde65ab2170 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -242,7 +242,7 @@ static struct ccw1 *alloc_chan_prog(const char __user *ubuf, int rec_count, * That means we allocate room for CCWs to cover count/reclen * records plus a NOP. */ - cpa = kzalloc((rec_count + 1) * sizeof(struct ccw1), + cpa = kcalloc(rec_count + 1, sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); if (!cpa) return ERR_PTR(-ENOMEM); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 4369662cfff5..76d3c50bf078 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -152,7 +152,7 @@ static int zcore_memmap_open(struct inode *inode, struct file *filp) char *buf; int i = 0; - buf = kzalloc(memblock.memory.cnt * CHUNK_INFO_SIZE, GFP_KERNEL); + buf = kcalloc(memblock.memory.cnt, CHUNK_INFO_SIZE, GFP_KERNEL); if (!buf) { return -ENOMEM; } diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 4c14ce428e92..78f1be41b05e 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -536,7 +536,7 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, int qdio_enable_async_operation(struct qdio_output_q *outq) { - outq->aobs = kzalloc(sizeof(struct qaob *) * QDIO_MAX_BUFFERS_PER_Q, + outq->aobs = kcalloc(QDIO_MAX_BUFFERS_PER_Q, sizeof(struct qaob *), GFP_ATOMIC); if (!outq->aobs) { outq->use_cq = 0; diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 0787b587e4b8..07dea602205b 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -241,8 +241,9 @@ out: /* allocate non-shared indicators and shared indicator */ int __init tiqdio_allocate_memory(void) { - q_indicators = kzalloc(sizeof(struct indicator_t) * TIQDIO_NR_INDICATORS, - GFP_KERNEL); + q_indicators = kcalloc(TIQDIO_NR_INDICATORS, + sizeof(struct indicator_t), + GFP_KERNEL); if (!q_indicators) return -ENOMEM; return 0; diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index a9ae827cc1ce..3929c8be8098 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -121,7 +121,7 @@ static int alloc_and_prep_cprbmem(size_t paramblen, * allocate consecutive memory for request CPRB, request param * block, reply CPRB and reply param block */ - cprbmem = kzalloc(2 * cprbplusparamblen, GFP_KERNEL); + cprbmem = kcalloc(2, cprbplusparamblen, GFP_KERNEL); if (!cprbmem) return -ENOMEM; diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 7ce98b70cad3..7617d21cb296 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1379,7 +1379,7 @@ static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type, } else ccw_num = 8; - ch->ccw = kzalloc(ccw_num * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); + ch->ccw = kcalloc(ccw_num, sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); if (ch->ccw == NULL) goto nomem_return; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 9f28b6f2efc4..8e1474f1ffac 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -374,9 +374,10 @@ static int qeth_alloc_cq(struct qeth_card *card) } card->qdio.no_in_queues = 2; card->qdio.out_bufstates = - kzalloc(card->qdio.no_out_queues * - QDIO_MAX_BUFFERS_PER_Q * - sizeof(struct qdio_outbuf_state), GFP_KERNEL); + kcalloc(card->qdio.no_out_queues * + QDIO_MAX_BUFFERS_PER_Q, + sizeof(struct qdio_outbuf_state), + GFP_KERNEL); outbuf_states = card->qdio.out_bufstates; if (outbuf_states == NULL) { rc = -1; @@ -2538,8 +2539,9 @@ static int qeth_alloc_qdio_buffers(struct qeth_card *card) /* outbound */ card->qdio.out_qs = - kzalloc(card->qdio.no_out_queues * - sizeof(struct qeth_qdio_out_q *), GFP_KERNEL); + kcalloc(card->qdio.no_out_queues, + sizeof(struct qeth_qdio_out_q *), + GFP_KERNEL); if (!card->qdio.out_qs) goto out_freepool; for (i = 0; i < card->qdio.no_out_queues; ++i) { @@ -4963,8 +4965,8 @@ static int qeth_qdio_establish(struct qeth_card *card) QETH_DBF_TEXT(SETUP, 2, "qdioest"); - qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char), - GFP_KERNEL); + qib_param_field = kzalloc(QDIO_MAX_BUFFERS_PER_Q, + GFP_KERNEL); if (!qib_param_field) { rc = -ENOMEM; goto out_free_nothing; @@ -4973,8 +4975,8 @@ static int qeth_qdio_establish(struct qeth_card *card) qeth_create_qib_param_field(card, qib_param_field); qeth_create_qib_param_field_blkt(card, qib_param_field); - in_sbal_ptrs = kzalloc(card->qdio.no_in_queues * - QDIO_MAX_BUFFERS_PER_Q * sizeof(void *), + in_sbal_ptrs = kcalloc(card->qdio.no_in_queues * QDIO_MAX_BUFFERS_PER_Q, + sizeof(void *), GFP_KERNEL); if (!in_sbal_ptrs) { rc = -ENOMEM; @@ -4985,7 +4987,7 @@ static int qeth_qdio_establish(struct qeth_card *card) virt_to_phys(card->qdio.in_q->bufs[i].buffer); } - queue_start_poll = kzalloc(sizeof(void *) * card->qdio.no_in_queues, + queue_start_poll = kcalloc(card->qdio.no_in_queues, sizeof(void *), GFP_KERNEL); if (!queue_start_poll) { rc = -ENOMEM; @@ -4997,8 +4999,9 @@ static int qeth_qdio_establish(struct qeth_card *card) qeth_qdio_establish_cq(card, in_sbal_ptrs, queue_start_poll); out_sbal_ptrs = - kzalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q * - sizeof(void *), GFP_KERNEL); + kcalloc(card->qdio.no_out_queues * QDIO_MAX_BUFFERS_PER_Q, + sizeof(void *), + GFP_KERNEL); if (!out_sbal_ptrs) { rc = -ENOMEM; goto out_free_queue_start_poll; diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index 35380a58d3f0..0d4ffe0ae306 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -2366,7 +2366,7 @@ static int __init blogic_init(void) if (blogic_probe_options.noprobe) return -ENODEV; blogic_probeinfo_list = - kzalloc(BLOGIC_MAX_ADAPTERS * sizeof(struct blogic_probeinfo), + kcalloc(BLOGIC_MAX_ADAPTERS, sizeof(struct blogic_probeinfo), GFP_KERNEL); if (blogic_probeinfo_list == NULL) { blogic_err("BusLogic: Unable to allocate Probe Info List\n", diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index f24fb942065d..04443577d48b 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -1681,7 +1681,9 @@ static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (aac_reset_devices || reset_devices) aac->init_reset = true; - aac->fibs = kzalloc(sizeof(struct fib) * (shost->can_queue + AAC_NUM_MGT_FIB), GFP_KERNEL); + aac->fibs = kcalloc(shost->can_queue + AAC_NUM_MGT_FIB, + sizeof(struct fib), + GFP_KERNEL); if (!aac->fibs) goto out_free_host; spin_lock_init(&aac->fib_lock); diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c index e97eceacf522..915a34f141e4 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_core.c +++ b/drivers/scsi/aic7xxx/aic7xxx_core.c @@ -4779,8 +4779,8 @@ ahc_init_scbdata(struct ahc_softc *ahc) SLIST_INIT(&scb_data->sg_maps); /* Allocate SCB resources */ - scb_data->scbarray = kzalloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC, - GFP_ATOMIC); + scb_data->scbarray = kcalloc(AHC_SCB_MAX_ALLOC, sizeof(struct scb), + GFP_ATOMIC); if (scb_data->scbarray == NULL) return (ENOMEM); diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index 35e0b5b64e8f..3b8ad55e59de 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -220,8 +220,9 @@ static int asd_init_scbs(struct asd_ha_struct *asd_ha) /* allocate the index array and bitmap */ asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs; - asd_ha->seq.tc_index_array = kzalloc(asd_ha->seq.tc_index_bitmap_bits* - sizeof(void *), GFP_KERNEL); + asd_ha->seq.tc_index_array = kcalloc(asd_ha->seq.tc_index_bitmap_bits, + sizeof(void *), + GFP_KERNEL); if (!asd_ha->seq.tc_index_array) return -ENOMEM; diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 6c838865ac5a..80e5b283fd81 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -350,7 +350,7 @@ static ssize_t asd_store_update_bios(struct device *dev, int flash_command = FLASH_CMD_NONE; int err = 0; - cmd_ptr = kzalloc(count*2, GFP_KERNEL); + cmd_ptr = kcalloc(count, 2, GFP_KERNEL); if (!cmd_ptr) { err = FAIL_OUT_MEMORY; diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index d981c16cd611..818d185d63f0 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -2467,8 +2467,8 @@ static int beiscsi_alloc_mem(struct beiscsi_hba *phba) /* Allocate memory for wrb_context */ phwi_ctrlr = phba->phwi_ctrlr; - phwi_ctrlr->wrb_context = kzalloc(sizeof(struct hwi_wrb_context) * - phba->params.cxns_per_ctrl, + phwi_ctrlr->wrb_context = kcalloc(phba->params.cxns_per_ctrl, + sizeof(struct hwi_wrb_context), GFP_KERNEL); if (!phwi_ctrlr->wrb_context) { kfree(phba->phwi_ctrlr); @@ -2621,8 +2621,8 @@ static int beiscsi_init_wrb_handle(struct beiscsi_hba *phba) /* Allocate memory for WRBQ */ phwi_ctxt = phwi_ctrlr->phwi_ctxt; - phwi_ctxt->be_wrbq = kzalloc(sizeof(struct be_queue_info) * - phba->params.cxns_per_ctrl, + phwi_ctxt->be_wrbq = kcalloc(phba->params.cxns_per_ctrl, + sizeof(struct be_queue_info), GFP_KERNEL); if (!phwi_ctxt->be_wrbq) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, @@ -2633,16 +2633,18 @@ static int beiscsi_init_wrb_handle(struct beiscsi_hba *phba) for (index = 0; index < phba->params.cxns_per_ctrl; index++) { pwrb_context = &phwi_ctrlr->wrb_context[index]; pwrb_context->pwrb_handle_base = - kzalloc(sizeof(struct wrb_handle *) * - phba->params.wrbs_per_cxn, GFP_KERNEL); + kcalloc(phba->params.wrbs_per_cxn, + sizeof(struct wrb_handle *), + GFP_KERNEL); if (!pwrb_context->pwrb_handle_base) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Mem Alloc Failed. Failing to load\n"); goto init_wrb_hndl_failed; } pwrb_context->pwrb_handle_basestd = - kzalloc(sizeof(struct wrb_handle *) * - phba->params.wrbs_per_cxn, GFP_KERNEL); + kcalloc(phba->params.wrbs_per_cxn, + sizeof(struct wrb_handle *), + GFP_KERNEL); if (!pwrb_context->pwrb_handle_basestd) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Mem Alloc Failed. Failing to load\n"); @@ -3896,18 +3898,18 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba) mem_descr_sglh = phba->init_mem; mem_descr_sglh += HWI_MEM_SGLH; if (1 == mem_descr_sglh->num_elements) { - phba->io_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) * - phba->params.ios_per_ctrl, + phba->io_sgl_hndl_base = kcalloc(phba->params.ios_per_ctrl, + sizeof(struct sgl_handle *), GFP_KERNEL); if (!phba->io_sgl_hndl_base) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Mem Alloc Failed. Failing to load\n"); return -ENOMEM; } - phba->eh_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) * - (phba->params.icds_per_ctrl - - phba->params.ios_per_ctrl), - GFP_KERNEL); + phba->eh_sgl_hndl_base = + kcalloc(phba->params.icds_per_ctrl - + phba->params.ios_per_ctrl, + sizeof(struct sgl_handle *), GFP_KERNEL); if (!phba->eh_sgl_hndl_base) { kfree(phba->io_sgl_hndl_base); beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, @@ -4034,8 +4036,9 @@ static int hba_setup_cid_tbls(struct beiscsi_hba *phba) phba->cid_array_info[ulp_num] = ptr_cid_info; } } - phba->ep_array = kzalloc(sizeof(struct iscsi_endpoint *) * - phba->params.cxns_per_ctrl, GFP_KERNEL); + phba->ep_array = kcalloc(phba->params.cxns_per_ctrl, + sizeof(struct iscsi_endpoint *), + GFP_KERNEL); if (!phba->ep_array) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Failed to allocate memory in " @@ -4045,8 +4048,9 @@ static int hba_setup_cid_tbls(struct beiscsi_hba *phba) goto free_memory; } - phba->conn_table = kzalloc(sizeof(struct beiscsi_conn *) * - phba->params.cxns_per_ctrl, GFP_KERNEL); + phba->conn_table = kcalloc(phba->params.cxns_per_ctrl, + sizeof(struct beiscsi_conn *), + GFP_KERNEL); if (!phba->conn_table) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : Failed to allocate memory in" diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c index d4d276c757ea..26b0fa4e90b5 100644 --- a/drivers/scsi/bfa/bfad_attr.c +++ b/drivers/scsi/bfa/bfad_attr.c @@ -927,7 +927,7 @@ bfad_im_num_of_discovered_ports_show(struct device *dev, struct bfa_rport_qualifier_s *rports = NULL; unsigned long flags; - rports = kzalloc(sizeof(struct bfa_rport_qualifier_s) * nrports, + rports = kcalloc(nrports, sizeof(struct bfa_rport_qualifier_s), GFP_ATOMIC); if (rports == NULL) return snprintf(buf, PAGE_SIZE, "Failed\n"); diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c index 7c884f881180..5d163ca1b366 100644 --- a/drivers/scsi/bfa/bfad_bsg.c +++ b/drivers/scsi/bfa/bfad_bsg.c @@ -3252,8 +3252,9 @@ bfad_fcxp_map_sg(struct bfad_s *bfad, void *payload_kbuf, struct bfa_sge_s *sg_table; int sge_num = 1; - buf_base = kzalloc((sizeof(struct bfad_buf_info) + - sizeof(struct bfa_sge_s)) * sge_num, GFP_KERNEL); + buf_base = kcalloc(sizeof(struct bfad_buf_info) + + sizeof(struct bfa_sge_s), + sge_num, GFP_KERNEL); if (!buf_base) return NULL; diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 65de1d0578a1..f00045813378 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -1397,7 +1397,7 @@ static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic) hba->next_conn_id = 0; hba->tgt_ofld_list = - kzalloc(sizeof(struct bnx2fc_rport *) * BNX2FC_NUM_MAX_SESS, + kcalloc(BNX2FC_NUM_MAX_SESS, sizeof(struct bnx2fc_rport *), GFP_KERNEL); if (!hba->tgt_ofld_list) { printk(KERN_ERR PFX "Unable to allocate tgt offload list\n"); diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 5a645b8b9af1..350257c13a5b 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -240,15 +240,15 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba) return NULL; } - cmgr->free_list = kzalloc(sizeof(*cmgr->free_list) * - arr_sz, GFP_KERNEL); + cmgr->free_list = kcalloc(arr_sz, sizeof(*cmgr->free_list), + GFP_KERNEL); if (!cmgr->free_list) { printk(KERN_ERR PFX "failed to alloc free_list\n"); goto mem_err; } - cmgr->free_list_lock = kzalloc(sizeof(*cmgr->free_list_lock) * - arr_sz, GFP_KERNEL); + cmgr->free_list_lock = kcalloc(arr_sz, sizeof(*cmgr->free_list_lock), + GFP_KERNEL); if (!cmgr->free_list_lock) { printk(KERN_ERR PFX "failed to alloc free_list_lock\n"); kfree(cmgr->free_list); diff --git a/drivers/scsi/csiostor/csio_wr.c b/drivers/scsi/csiostor/csio_wr.c index c0a17789752f..faa357b62c61 100644 --- a/drivers/scsi/csiostor/csio_wr.c +++ b/drivers/scsi/csiostor/csio_wr.c @@ -276,7 +276,7 @@ csio_wr_alloc_q(struct csio_hw *hw, uint32_t qsize, uint32_t wrsize, q->un.iq.flq_idx = flq_idx; flq = wrm->q_arr[q->un.iq.flq_idx]; - flq->un.fl.bufs = kzalloc(flq->credits * + flq->un.fl.bufs = kcalloc(flq->credits, sizeof(struct csio_dma_buf), GFP_KERNEL); if (!flq->un.fl.bufs) { @@ -1579,7 +1579,7 @@ csio_wrm_init(struct csio_wrm *wrm, struct csio_hw *hw) return -EINVAL; } - wrm->q_arr = kzalloc(sizeof(struct csio_q *) * wrm->num_q, GFP_KERNEL); + wrm->q_arr = kcalloc(wrm->num_q, sizeof(struct csio_q *), GFP_KERNEL); if (!wrm->q_arr) goto err; diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c index 9db645dde35e..bbe77db8938d 100644 --- a/drivers/scsi/esas2r/esas2r_init.c +++ b/drivers/scsi/esas2r/esas2r_init.c @@ -833,7 +833,7 @@ bool esas2r_init_adapter_struct(struct esas2r_adapter *a, /* allocate requests for asynchronous events */ a->first_ae_req = - kzalloc(num_ae_requests * sizeof(struct esas2r_request), + kcalloc(num_ae_requests, sizeof(struct esas2r_request), GFP_KERNEL); if (a->first_ae_req == NULL) { @@ -843,8 +843,8 @@ bool esas2r_init_adapter_struct(struct esas2r_adapter *a, } /* allocate the S/G list memory descriptors */ - a->sg_list_mds = kzalloc( - num_sg_lists * sizeof(struct esas2r_mem_desc), GFP_KERNEL); + a->sg_list_mds = kcalloc(num_sg_lists, sizeof(struct esas2r_mem_desc), + GFP_KERNEL); if (a->sg_list_mds == NULL) { esas2r_log(ESAS2R_LOG_CRIT, @@ -854,8 +854,9 @@ bool esas2r_init_adapter_struct(struct esas2r_adapter *a, /* allocate the request table */ a->req_table = - kzalloc((num_requests + num_ae_requests + - 1) * sizeof(struct esas2r_request *), GFP_KERNEL); + kcalloc(num_requests + num_ae_requests + 1, + sizeof(struct esas2r_request *), + GFP_KERNEL); if (a->req_table == NULL) { esas2r_log(ESAS2R_LOG_CRIT, diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index e6f31fa9ec65..af0e628ff396 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -1923,8 +1923,8 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, } spin_unlock_irqrestore(&h->reset_lock, flags); - added = kzalloc(sizeof(*added) * HPSA_MAX_DEVICES, GFP_KERNEL); - removed = kzalloc(sizeof(*removed) * HPSA_MAX_DEVICES, GFP_KERNEL); + added = kcalloc(HPSA_MAX_DEVICES, sizeof(*added), GFP_KERNEL); + removed = kcalloc(HPSA_MAX_DEVICES, sizeof(*removed), GFP_KERNEL); if (!added || !removed) { dev_warn(&h->pdev->dev, "out of memory in " @@ -2171,7 +2171,7 @@ static int hpsa_allocate_ioaccel2_sg_chain_blocks(struct ctlr_info *h) return 0; h->ioaccel2_cmd_sg_list = - kzalloc(sizeof(*h->ioaccel2_cmd_sg_list) * h->nr_cmds, + kcalloc(h->nr_cmds, sizeof(*h->ioaccel2_cmd_sg_list), GFP_KERNEL); if (!h->ioaccel2_cmd_sg_list) return -ENOMEM; @@ -2211,8 +2211,8 @@ static int hpsa_alloc_sg_chain_blocks(struct ctlr_info *h) if (h->chainsize <= 0) return 0; - h->cmd_sg_list = kzalloc(sizeof(*h->cmd_sg_list) * h->nr_cmds, - GFP_KERNEL); + h->cmd_sg_list = kcalloc(h->nr_cmds, sizeof(*h->cmd_sg_list), + GFP_KERNEL); if (!h->cmd_sg_list) return -ENOMEM; @@ -4321,7 +4321,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h) bool physical_device; DECLARE_BITMAP(lunzerobits, MAX_EXT_TARGETS); - currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_DEVICES, GFP_KERNEL); + currentsd = kcalloc(HPSA_MAX_DEVICES, sizeof(*currentsd), GFP_KERNEL); physdev_list = kzalloc(sizeof(*physdev_list), GFP_KERNEL); logdev_list = kzalloc(sizeof(*logdev_list), GFP_KERNEL); tmpdevice = kzalloc(sizeof(*tmpdevice), GFP_KERNEL); @@ -6404,7 +6404,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) status = -EINVAL; goto cleanup1; } - buff = kzalloc(SG_ENTRIES_IN_CMD * sizeof(char *), GFP_KERNEL); + buff = kcalloc(SG_ENTRIES_IN_CMD, sizeof(char *), GFP_KERNEL); if (!buff) { status = -ENOMEM; goto cleanup1; @@ -7933,9 +7933,9 @@ static void hpsa_free_cmd_pool(struct ctlr_info *h) static int hpsa_alloc_cmd_pool(struct ctlr_info *h) { - h->cmd_pool_bits = kzalloc( - DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) * - sizeof(unsigned long), GFP_KERNEL); + h->cmd_pool_bits = kcalloc(DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG), + sizeof(unsigned long), + GFP_KERNEL); h->cmd_pool = pci_alloc_consistent(h->pdev, h->nr_cmds * sizeof(*h->cmd_pool), &(h->cmd_pool_dhandle)); @@ -8509,7 +8509,7 @@ static struct ctlr_info *hpda_alloc_ctlr_info(void) if (!h) return NULL; - h->reply_map = kzalloc(sizeof(*h->reply_map) * nr_cpu_ids, GFP_KERNEL); + h->reply_map = kcalloc(nr_cpu_ids, sizeof(*h->reply_map), GFP_KERNEL); if (!h->reply_map) { kfree(h); return NULL; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 6615ad8754b8..e63785d5df32 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -9713,8 +9713,9 @@ static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) int i, rc = -ENOMEM; ENTER; - ioa_cfg->res_entries = kzalloc(sizeof(struct ipr_resource_entry) * - ioa_cfg->max_devs_supported, GFP_KERNEL); + ioa_cfg->res_entries = kcalloc(ioa_cfg->max_devs_supported, + sizeof(struct ipr_resource_entry), + GFP_KERNEL); if (!ioa_cfg->res_entries) goto out; @@ -9775,8 +9776,9 @@ static int ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q); } - ioa_cfg->trace = kzalloc(sizeof(struct ipr_trace_entry) * - IPR_NUM_TRACE_ENTRIES, GFP_KERNEL); + ioa_cfg->trace = kcalloc(IPR_NUM_TRACE_ENTRIES, + sizeof(struct ipr_trace_entry), + GFP_KERNEL); if (!ioa_cfg->trace) goto out_free_hostrcb_dma; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 8b7114348def..fadc99cb60df 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -443,7 +443,7 @@ static int sas_expander_discover(struct domain_device *dev) struct expander_device *ex = &dev->ex_dev; int res = -ENOMEM; - ex->ex_phy = kzalloc(sizeof(*ex->ex_phy)*ex->num_phys, GFP_KERNEL); + ex->ex_phy = kcalloc(ex->num_phys, sizeof(*ex->ex_phy), GFP_KERNEL); if (!ex->ex_phy) return -ENOMEM; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 7ae343b14630..52cae87da0d2 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -5723,8 +5723,9 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) } if (!phba->sli.sli3_ring) - phba->sli.sli3_ring = kzalloc(LPFC_SLI3_MAX_RING * - sizeof(struct lpfc_sli_ring), GFP_KERNEL); + phba->sli.sli3_ring = kcalloc(LPFC_SLI3_MAX_RING, + sizeof(struct lpfc_sli_ring), + GFP_KERNEL); if (!phba->sli.sli3_ring) return -ENOMEM; @@ -6233,7 +6234,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) /* Allocate eligible FCF bmask memory for FCF roundrobin failover */ longs = (LPFC_SLI4_FCF_TBL_INDX_MAX + BITS_PER_LONG - 1)/BITS_PER_LONG; - phba->fcf.fcf_rr_bmask = kzalloc(longs * sizeof(unsigned long), + phba->fcf.fcf_rr_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (!phba->fcf.fcf_rr_bmask) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 4b70d53acb72..6f3c00a233ec 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1720,7 +1720,7 @@ lpfc_sli_next_iotag(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) - LPFC_IOCBQ_LOOKUP_INCREMENT)) { new_len = psli->iocbq_lookup_len + LPFC_IOCBQ_LOOKUP_INCREMENT; spin_unlock_irq(&phba->hbalock); - new_arr = kzalloc(new_len * sizeof (struct lpfc_iocbq *), + new_arr = kcalloc(new_len, sizeof(struct lpfc_iocbq *), GFP_KERNEL); if (new_arr) { spin_lock_irq(&phba->hbalock); @@ -5142,16 +5142,17 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba) */ if ((phba->vpi_bmask == NULL) && (phba->vpi_ids == NULL)) { longs = (phba->max_vpi + BITS_PER_LONG) / BITS_PER_LONG; - phba->vpi_bmask = kzalloc(longs * sizeof(unsigned long), + phba->vpi_bmask = kcalloc(longs, + sizeof(unsigned long), GFP_KERNEL); if (!phba->vpi_bmask) { rc = -ENOMEM; goto lpfc_sli_hba_setup_error; } - phba->vpi_ids = kzalloc( - (phba->max_vpi+1) * sizeof(uint16_t), - GFP_KERNEL); + phba->vpi_ids = kcalloc(phba->max_vpi + 1, + sizeof(uint16_t), + GFP_KERNEL); if (!phba->vpi_ids) { kfree(phba->vpi_bmask); rc = -ENOMEM; @@ -5836,14 +5837,14 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) length = sizeof(struct lpfc_rsrc_blks); switch (type) { case LPFC_RSC_TYPE_FCOE_RPI: - phba->sli4_hba.rpi_bmask = kzalloc(longs * + phba->sli4_hba.rpi_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (unlikely(!phba->sli4_hba.rpi_bmask)) { rc = -ENOMEM; goto err_exit; } - phba->sli4_hba.rpi_ids = kzalloc(rsrc_id_cnt * + phba->sli4_hba.rpi_ids = kcalloc(rsrc_id_cnt, sizeof(uint16_t), GFP_KERNEL); if (unlikely(!phba->sli4_hba.rpi_ids)) { @@ -5865,15 +5866,13 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) ext_blk_list = &phba->sli4_hba.lpfc_rpi_blk_list; break; case LPFC_RSC_TYPE_FCOE_VPI: - phba->vpi_bmask = kzalloc(longs * - sizeof(unsigned long), + phba->vpi_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (unlikely(!phba->vpi_bmask)) { rc = -ENOMEM; goto err_exit; } - phba->vpi_ids = kzalloc(rsrc_id_cnt * - sizeof(uint16_t), + phba->vpi_ids = kcalloc(rsrc_id_cnt, sizeof(uint16_t), GFP_KERNEL); if (unlikely(!phba->vpi_ids)) { kfree(phba->vpi_bmask); @@ -5887,7 +5886,7 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) ext_blk_list = &phba->lpfc_vpi_blk_list; break; case LPFC_RSC_TYPE_FCOE_XRI: - phba->sli4_hba.xri_bmask = kzalloc(longs * + phba->sli4_hba.xri_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (unlikely(!phba->sli4_hba.xri_bmask)) { @@ -5895,7 +5894,7 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) goto err_exit; } phba->sli4_hba.max_cfg_param.xri_used = 0; - phba->sli4_hba.xri_ids = kzalloc(rsrc_id_cnt * + phba->sli4_hba.xri_ids = kcalloc(rsrc_id_cnt, sizeof(uint16_t), GFP_KERNEL); if (unlikely(!phba->sli4_hba.xri_ids)) { @@ -5910,14 +5909,14 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) ext_blk_list = &phba->sli4_hba.lpfc_xri_blk_list; break; case LPFC_RSC_TYPE_FCOE_VFI: - phba->sli4_hba.vfi_bmask = kzalloc(longs * + phba->sli4_hba.vfi_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (unlikely(!phba->sli4_hba.vfi_bmask)) { rc = -ENOMEM; goto err_exit; } - phba->sli4_hba.vfi_ids = kzalloc(rsrc_id_cnt * + phba->sli4_hba.vfi_ids = kcalloc(rsrc_id_cnt, sizeof(uint16_t), GFP_KERNEL); if (unlikely(!phba->sli4_hba.vfi_ids)) { @@ -6250,15 +6249,14 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba) } base = phba->sli4_hba.max_cfg_param.rpi_base; longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG; - phba->sli4_hba.rpi_bmask = kzalloc(longs * + phba->sli4_hba.rpi_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (unlikely(!phba->sli4_hba.rpi_bmask)) { rc = -ENOMEM; goto err_exit; } - phba->sli4_hba.rpi_ids = kzalloc(count * - sizeof(uint16_t), + phba->sli4_hba.rpi_ids = kcalloc(count, sizeof(uint16_t), GFP_KERNEL); if (unlikely(!phba->sli4_hba.rpi_ids)) { rc = -ENOMEM; @@ -6279,15 +6277,13 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba) } base = phba->sli4_hba.max_cfg_param.vpi_base; longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG; - phba->vpi_bmask = kzalloc(longs * - sizeof(unsigned long), + phba->vpi_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (unlikely(!phba->vpi_bmask)) { rc = -ENOMEM; goto free_rpi_ids; } - phba->vpi_ids = kzalloc(count * - sizeof(uint16_t), + phba->vpi_ids = kcalloc(count, sizeof(uint16_t), GFP_KERNEL); if (unlikely(!phba->vpi_ids)) { rc = -ENOMEM; @@ -6308,7 +6304,7 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba) } base = phba->sli4_hba.max_cfg_param.xri_base; longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG; - phba->sli4_hba.xri_bmask = kzalloc(longs * + phba->sli4_hba.xri_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (unlikely(!phba->sli4_hba.xri_bmask)) { @@ -6316,8 +6312,7 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba) goto free_vpi_ids; } phba->sli4_hba.max_cfg_param.xri_used = 0; - phba->sli4_hba.xri_ids = kzalloc(count * - sizeof(uint16_t), + phba->sli4_hba.xri_ids = kcalloc(count, sizeof(uint16_t), GFP_KERNEL); if (unlikely(!phba->sli4_hba.xri_ids)) { rc = -ENOMEM; @@ -6338,15 +6333,14 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba) } base = phba->sli4_hba.max_cfg_param.vfi_base; longs = (count + BITS_PER_LONG - 1) / BITS_PER_LONG; - phba->sli4_hba.vfi_bmask = kzalloc(longs * + phba->sli4_hba.vfi_bmask = kcalloc(longs, sizeof(unsigned long), GFP_KERNEL); if (unlikely(!phba->sli4_hba.vfi_bmask)) { rc = -ENOMEM; goto free_xri_ids; } - phba->sli4_hba.vfi_ids = kzalloc(count * - sizeof(uint16_t), + phba->sli4_hba.vfi_ids = kcalloc(count, sizeof(uint16_t), GFP_KERNEL); if (unlikely(!phba->sli4_hba.vfi_ids)) { rc = -ENOMEM; diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index c9d33b1268cb..81bc12dedf41 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -840,7 +840,7 @@ lpfc_create_vport_work_array(struct lpfc_hba *phba) struct lpfc_vport *port_iterator; struct lpfc_vport **vports; int index = 0; - vports = kzalloc((phba->max_vports + 1) * sizeof(struct lpfc_vport *), + vports = kcalloc(phba->max_vports + 1, sizeof(struct lpfc_vport *), GFP_KERNEL); if (vports == NULL) return NULL; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index c5d0c4bd71d2..71d97573a667 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -5419,9 +5419,9 @@ static int megasas_init_fw(struct megasas_instance *instance) /* stream detection initialization */ if (instance->adapter_type == VENTURA_SERIES) { fusion->stream_detect_by_ld = - kzalloc(sizeof(struct LD_STREAM_DETECT *) - * MAX_LOGICAL_DRIVES_EXT, - GFP_KERNEL); + kcalloc(MAX_LOGICAL_DRIVES_EXT, + sizeof(struct LD_STREAM_DETECT *), + GFP_KERNEL); if (!fusion->stream_detect_by_ld) { dev_err(&instance->pdev->dev, "unable to allocate stream detection for pool of LDs\n"); @@ -6139,7 +6139,7 @@ static inline int megasas_alloc_mfi_ctrl_mem(struct megasas_instance *instance) */ static int megasas_alloc_ctrl_mem(struct megasas_instance *instance) { - instance->reply_map = kzalloc(sizeof(unsigned int) * nr_cpu_ids, + instance->reply_map = kcalloc(nr_cpu_ids, sizeof(unsigned int), GFP_KERNEL); if (!instance->reply_map) return -ENOMEM; diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 98a7a090b75e..b965d4fe18ef 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -487,7 +487,7 @@ megasas_alloc_cmdlist_fusion(struct megasas_instance *instance) * commands. */ fusion->cmd_list = - kzalloc(sizeof(struct megasas_cmd_fusion *) * max_mpt_cmd, + kcalloc(max_mpt_cmd, sizeof(struct megasas_cmd_fusion *), GFP_KERNEL); if (!fusion->cmd_list) { dev_err(&instance->pdev->dev, diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 773c4bfeb0f8..928ee4e89813 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -381,7 +381,7 @@ static int osst_execute(struct osst_request *SRpnt, const unsigned char *cmd, struct scatterlist *sg, *sgl = (struct scatterlist *)buffer; int i; - pages = kzalloc(use_sg * sizeof(struct page *), GFP_KERNEL); + pages = kcalloc(use_sg, sizeof(struct page *), GFP_KERNEL); if (!pages) goto free_req; diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index 596f3ff965f5..d193961ea82f 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -705,7 +705,7 @@ static ssize_t pm8001_store_update_fw(struct device *cdev, return -EINPROGRESS; pm8001_ha->fw_status = FLASH_IN_PROGRESS; - cmd_ptr = kzalloc(count*2, GFP_KERNEL); + cmd_ptr = kcalloc(count, 2, GFP_KERNEL); if (!cmd_ptr) { pm8001_ha->fw_status = FAIL_OUT_MEMORY; return -ENOMEM; diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 95530393872d..4e86994e10e8 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -4873,8 +4873,9 @@ static int pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance) int i; pinstance->res_entries = - kzalloc(sizeof(struct pmcraid_resource_entry) * - PMCRAID_MAX_RESOURCES, GFP_KERNEL); + kcalloc(PMCRAID_MAX_RESOURCES, + sizeof(struct pmcraid_resource_entry), + GFP_KERNEL); if (NULL == pinstance->res_entries) { pmcraid_err("failed to allocate memory for resource table\n"); diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c index 32ee7f62fef9..cf274a79e77a 100644 --- a/drivers/scsi/qedi/qedi_main.c +++ b/drivers/scsi/qedi/qedi_main.c @@ -524,7 +524,7 @@ static int qedi_init_id_tbl(struct qedi_portid_tbl *id_tbl, u16 size, id_tbl->max = size; id_tbl->next = next; spin_lock_init(&id_tbl->lock); - id_tbl->table = kzalloc(DIV_ROUND_UP(size, 32) * 4, GFP_KERNEL); + id_tbl->table = kcalloc(DIV_ROUND_UP(size, 32), 4, GFP_KERNEL); if (!id_tbl->table) return -ENOMEM; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 1aa3720ea2ed..fbbb328c64d5 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -3089,8 +3089,9 @@ qla2x00_alloc_outstanding_cmds(struct qla_hw_data *ha, struct req_que *req) req->num_outstanding_cmds = ha->cur_fw_iocb_count; } - req->outstanding_cmds = kzalloc(sizeof(srb_t *) * - req->num_outstanding_cmds, GFP_KERNEL); + req->outstanding_cmds = kcalloc(req->num_outstanding_cmds, + sizeof(srb_t *), + GFP_KERNEL); if (!req->outstanding_cmds) { /* @@ -3098,8 +3099,9 @@ qla2x00_alloc_outstanding_cmds(struct qla_hw_data *ha, struct req_que *req) * initialization. */ req->num_outstanding_cmds = MIN_OUTSTANDING_COMMANDS; - req->outstanding_cmds = kzalloc(sizeof(srb_t *) * - req->num_outstanding_cmds, GFP_KERNEL); + req->outstanding_cmds = kcalloc(req->num_outstanding_cmds, + sizeof(srb_t *), + GFP_KERNEL); if (!req->outstanding_cmds) { ql_log(ql_log_fatal, NULL, 0x0126, diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index a3dc83f9444d..d14d3911516d 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -3434,8 +3434,9 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) "Adjusted Max no of queues pairs: %d.\n", ha->max_qpairs); } } - ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) * - ha->msix_count, GFP_KERNEL); + ha->msix_entries = kcalloc(ha->msix_count, + sizeof(struct qla_msix_entry), + GFP_KERNEL); if (!ha->msix_entries) { ql_log(ql_log_fatal, vha, 0x00c8, "Failed to allocate memory for ha->msix_entries.\n"); diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 817c18a8e84d..e881fce7477a 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -410,7 +410,7 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req, struct rsp_que *rsp) { scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); - ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues, + ha->req_q_map = kcalloc(ha->max_req_queues, sizeof(struct req_que *), GFP_KERNEL); if (!ha->req_q_map) { ql_log(ql_log_fatal, vha, 0x003b, @@ -418,7 +418,7 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req, goto fail_req_map; } - ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues, + ha->rsp_q_map = kcalloc(ha->max_rsp_queues, sizeof(struct rsp_que *), GFP_KERNEL); if (!ha->rsp_q_map) { ql_log(ql_log_fatal, vha, 0x003c, @@ -4045,8 +4045,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, (*rsp)->ring); /* Allocate memory for NVRAM data for vports */ if (ha->nvram_npiv_size) { - ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) * - ha->nvram_npiv_size, GFP_KERNEL); + ha->npiv_info = kcalloc(ha->nvram_npiv_size, + sizeof(struct qla_npiv_entry), + GFP_KERNEL); if (!ha->npiv_info) { ql_log_pci(ql_log_fatal, ha->pdev, 0x002d, "Failed to allocate memory for npiv_info.\n"); @@ -4080,8 +4081,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, INIT_LIST_HEAD(&ha->vp_list); /* Allocate memory for our loop_id bitmap */ - ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long), - GFP_KERNEL); + ha->loop_id_map = kcalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE), + sizeof(long), + GFP_KERNEL); if (!ha->loop_id_map) goto fail_loop_id_map; else { diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index b85c833099ff..0fea2e2326be 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -6248,8 +6248,9 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) return -ENOMEM; } - tgt->qphints = kzalloc((ha->max_qpairs + 1) * - sizeof(struct qla_qpair_hint), GFP_KERNEL); + tgt->qphints = kcalloc(ha->max_qpairs + 1, + sizeof(struct qla_qpair_hint), + GFP_KERNEL); if (!tgt->qphints) { kfree(tgt); ql_log(ql_log_warn, base_vha, 0x0197, @@ -7089,8 +7090,9 @@ qlt_mem_alloc(struct qla_hw_data *ha) if (!QLA_TGT_MODE_ENABLED()) return 0; - ha->tgt.tgt_vp_map = kzalloc(sizeof(struct qla_tgt_vp_map) * - MAX_MULTI_ID_FABRIC, GFP_KERNEL); + ha->tgt.tgt_vp_map = kcalloc(MAX_MULTI_ID_FABRIC, + sizeof(struct qla_tgt_vp_map), + GFP_KERNEL); if (!ha->tgt.tgt_vp_map) return -ENOMEM; diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 656c98e116a9..798a6afa4cbf 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -3450,7 +3450,7 @@ static int resp_comp_write(struct scsi_cmnd *scp, return check_condition_result; } dnum = 2 * num; - arr = kzalloc(dnum * lb_size, GFP_ATOMIC); + arr = kcalloc(lb_size, dnum, GFP_ATOMIC); if (NULL == arr) { mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 62f04c0511cf..0fc39224ce1e 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -747,7 +747,7 @@ static int ses_intf_add(struct device *cdev, buf = NULL; } page2_not_supported: - scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL); + scomp = kcalloc(components, sizeof(struct ses_component), GFP_KERNEL); if (!scomp) goto err_free; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 573763908562..53ae52dbff84 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1045,7 +1045,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) else { sg_req_info_t *rinfo; - rinfo = kzalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE, + rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, GFP_KERNEL); if (!rinfo) return -ENOMEM; diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 8332f958cc42..b78d20b74ed8 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -4252,8 +4252,9 @@ static int pqi_alloc_io_resources(struct pqi_ctrl_info *ctrl_info) struct device *dev; struct pqi_io_request *io_request; - ctrl_info->io_request_pool = kzalloc(ctrl_info->max_io_slots * - sizeof(ctrl_info->io_request_pool[0]), GFP_KERNEL); + ctrl_info->io_request_pool = + kcalloc(ctrl_info->max_io_slots, + sizeof(ctrl_info->io_request_pool[0]), GFP_KERNEL); if (!ctrl_info->io_request_pool) { dev_err(&ctrl_info->pci_dev->dev, diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index c16e4de3a03f..50c66ccc4b41 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3888,7 +3888,7 @@ static struct st_buffer *new_tape_buffer(int need_dma, int max_sg) tb->dma = need_dma; tb->buffer_size = 0; - tb->reserved_pages = kzalloc(max_sg * sizeof(struct page *), + tb->reserved_pages = kcalloc(max_sg, sizeof(struct page *), GFP_KERNEL); if (!tb->reserved_pages) { kfree(tb); diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 7442bc130055..eeb028b9cdb3 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -249,7 +249,7 @@ static int __init sh_clk_div_register_ops(struct clk *clks, int nr, int k; freq_table_size *= (nr_divs + 1); - freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); + freq_table = kcalloc(nr, freq_table_size, GFP_KERNEL); if (!freq_table) { pr_err("%s: unable to alloc memory\n", __func__); return -ENOMEM; diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index 8e72bcbd3d6d..46f0f322d4d8 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -203,7 +203,7 @@ int __init register_intc_controller(struct intc_desc *desc) if (desc->num_resources) { d->nr_windows = desc->num_resources; - d->window = kzalloc(d->nr_windows * sizeof(*d->window), + d->window = kcalloc(d->nr_windows, sizeof(*d->window), GFP_NOWAIT); if (!d->window) goto err1; @@ -230,12 +230,12 @@ int __init register_intc_controller(struct intc_desc *desc) d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; d->nr_reg += hw->subgroups ? hw->nr_subgroups : 0; - d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); + d->reg = kcalloc(d->nr_reg, sizeof(*d->reg), GFP_NOWAIT); if (!d->reg) goto err2; #ifdef CONFIG_SMP - d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); + d->smp = kcalloc(d->nr_reg, sizeof(*d->smp), GFP_NOWAIT); if (!d->smp) goto err3; #endif @@ -253,7 +253,7 @@ int __init register_intc_controller(struct intc_desc *desc) } if (hw->prio_regs) { - d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), + d->prio = kcalloc(hw->nr_vectors, sizeof(*d->prio), GFP_NOWAIT); if (!d->prio) goto err4; @@ -269,7 +269,7 @@ int __init register_intc_controller(struct intc_desc *desc) } if (hw->sense_regs) { - d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), + d->sense = kcalloc(hw->nr_vectors, sizeof(*d->sense), GFP_NOWAIT); if (!d->sense) goto err5; diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index 7525039d812c..2e45988d1259 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c @@ -161,7 +161,7 @@ int maple_add_packet(struct maple_device *mdev, u32 function, u32 command, void *sendbuf = NULL; if (length) { - sendbuf = kzalloc(length * 4, GFP_KERNEL); + sendbuf = kcalloc(length, 4, GFP_KERNEL); if (!sendbuf) { ret = -ENOMEM; goto out; diff --git a/drivers/slimbus/qcom-ctrl.c b/drivers/slimbus/qcom-ctrl.c index bb36a8fbc9b1..db1f5135846a 100644 --- a/drivers/slimbus/qcom-ctrl.c +++ b/drivers/slimbus/qcom-ctrl.c @@ -540,7 +540,7 @@ static int qcom_slim_probe(struct platform_device *pdev) ctrl->tx.sl_sz = SLIM_MSGQ_BUF_LEN; ctrl->rx.n = QCOM_RX_MSGS; ctrl->rx.sl_sz = SLIM_MSGQ_BUF_LEN; - ctrl->wr_comp = kzalloc(sizeof(struct completion *) * QCOM_TX_MSGS, + ctrl->wr_comp = kcalloc(QCOM_TX_MSGS, sizeof(struct completion *), GFP_KERNEL); if (!ctrl->wr_comp) return -ENOMEM; diff --git a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c index 2d9ab2620b82..04b1a0950387 100644 --- a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c +++ b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c @@ -143,7 +143,7 @@ static int rt2880_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrldev, if (!max_maps) return max_maps; - *map = kzalloc(max_maps * sizeof(struct pinctrl_map), GFP_KERNEL); + *map = kcalloc(max_maps, sizeof(struct pinctrl_map), GFP_KERNEL); if (!*map) return -ENOMEM; diff --git a/drivers/staging/rtlwifi/efuse.c b/drivers/staging/rtlwifi/efuse.c index d7c7d146a84d..1dc71455f270 100644 --- a/drivers/staging/rtlwifi/efuse.c +++ b/drivers/staging/rtlwifi/efuse.c @@ -237,8 +237,8 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf) } /* allocate memory for efuse_tbl and efuse_word */ - efuse_tbl = kzalloc(rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE] * - sizeof(u8), GFP_ATOMIC); + efuse_tbl = kzalloc(rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE], + GFP_ATOMIC); if (!efuse_tbl) return; efuse_word = kcalloc(EFUSE_MAX_WORD_UNIT, sizeof(u16 *), GFP_ATOMIC); diff --git a/drivers/staging/unisys/visorhba/visorhba_main.c b/drivers/staging/unisys/visorhba/visorhba_main.c index 167e98f8688e..4fc521c51c0e 100644 --- a/drivers/staging/unisys/visorhba/visorhba_main.c +++ b/drivers/staging/unisys/visorhba/visorhba_main.c @@ -865,7 +865,7 @@ static void do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, if (cmdrsp->scsi.no_disk_result == 0) return; - buf = kzalloc(sizeof(char) * 36, GFP_KERNEL); + buf = kzalloc(36, GFP_KERNEL); if (!buf) return; diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index f0e8f0f4ccb4..efe8214f2df3 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -250,7 +250,7 @@ int transport_alloc_session_tags(struct se_session *se_sess, { int rc; - se_sess->sess_cmd_map = kzalloc(tag_num * tag_size, + se_sess->sess_cmd_map = kcalloc(tag_size, tag_num, GFP_KERNEL | __GFP_NOWARN | __GFP_RETRY_MAYFAIL); if (!se_sess->sess_cmd_map) { se_sess->sess_cmd_map = vzalloc(tag_num * tag_size); diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 94b183efd236..7f96dfa32b9c 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1717,8 +1717,9 @@ static int tcmu_configure_device(struct se_device *dev) info = &udev->uio_info; - udev->data_bitmap = kzalloc(BITS_TO_LONGS(udev->max_blocks) * - sizeof(unsigned long), GFP_KERNEL); + udev->data_bitmap = kcalloc(BITS_TO_LONGS(udev->max_blocks), + sizeof(unsigned long), + GFP_KERNEL); if (!udev->data_bitmap) { ret = -ENOMEM; goto err_bitmap_alloc; diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c index c719167e9f28..45e7e5cbdffb 100644 --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c @@ -96,7 +96,7 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, } *trt_count = p->package.count; - trts = kzalloc(*trt_count * sizeof(struct trt), GFP_KERNEL); + trts = kcalloc(*trt_count, sizeof(struct trt), GFP_KERNEL); if (!trts) { result = -ENOMEM; goto end; @@ -178,7 +178,7 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, /* ignore p->package.elements[0], as this is _ART Revision field */ *art_count = p->package.count - 1; - arts = kzalloc(*art_count * sizeof(struct art), GFP_KERNEL); + arts = kcalloc(*art_count, sizeof(struct art), GFP_KERNEL); if (!arts) { result = -ENOMEM; goto end; diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c index 145a5c53ff5c..953c83967ceb 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c @@ -239,9 +239,10 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, if (ACPI_FAILURE(status)) trip_cnt = 0; else { - int34x_thermal_zone->aux_trips = kzalloc( - sizeof(*int34x_thermal_zone->aux_trips) * - trip_cnt, GFP_KERNEL); + int34x_thermal_zone->aux_trips = + kcalloc(trip_cnt, + sizeof(*int34x_thermal_zone->aux_trips), + GFP_KERNEL); if (!int34x_thermal_zone->aux_trips) { ret = -ENOMEM; goto err_trip_alloc; diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index e09f0354a4bc..5798420ac29c 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -870,7 +870,7 @@ __init *thermal_of_build_thermal_zone(struct device_node *np) if (tz->ntrips == 0) /* must have at least one child */ goto finish; - tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL); + tz->trips = kcalloc(tz->ntrips, sizeof(*tz->trips), GFP_KERNEL); if (!tz->trips) { ret = -ENOMEM; goto free_tz; @@ -896,7 +896,7 @@ __init *thermal_of_build_thermal_zone(struct device_node *np) if (tz->num_tbps == 0) goto finish; - tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL); + tz->tbps = kcalloc(tz->num_tbps, sizeof(*tz->tbps), GFP_KERNEL); if (!tz->tbps) { ret = -ENOMEM; goto free_trips; diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 1a6c88b10a39..1ef937d799e4 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -516,7 +516,8 @@ static int __init pkg_temp_thermal_init(void) return -ENODEV; max_packages = topology_max_packages(); - packages = kzalloc(max_packages * sizeof(struct pkg_device *), GFP_KERNEL); + packages = kcalloc(max_packages, sizeof(struct pkg_device *), + GFP_KERNEL); if (!packages) return -ENOMEM; diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 47ac56817c43..eea4049b5dcc 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -754,7 +754,7 @@ static int __init ehv_bc_init(void) * array, then you can use pointer math (e.g. "bc - bcs") to get its * tty index. */ - bcs = kzalloc(count * sizeof(struct ehv_bc_data), GFP_KERNEL); + bcs = kcalloc(count, sizeof(struct ehv_bc_data), GFP_KERNEL); if (!bcs) return -ENOMEM; diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 1c1bd0afcd48..37caba7c3aff 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -245,8 +245,9 @@ static int goldfish_tty_create_driver(void) int ret; struct tty_driver *tty; - goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * - goldfish_tty_line_count, GFP_KERNEL); + goldfish_ttys = kcalloc(goldfish_tty_line_count, + sizeof(*goldfish_ttys), + GFP_KERNEL); if (goldfish_ttys == NULL) { ret = -ENOMEM; goto err_alloc_goldfish_ttys_failed; diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index a74680729825..2af1e5751bd6 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -1252,7 +1252,7 @@ static int hvc_iucv_setup_filter(const char *val) if (size > MAX_VMID_FILTER) return -ENOSPC; - array = kzalloc(size * 8, GFP_KERNEL); + array = kcalloc(size, 8, GFP_KERNEL); if (!array) return -ENOMEM; diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 760d5dd0aada..cb85002a10d8 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -991,7 +991,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) priv->tx_dma_use = 1; - priv->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC); + priv->sg_tx_p = kcalloc(num, sizeof(struct scatterlist), GFP_ATOMIC); if (!priv->sg_tx_p) { dev_err(priv->port.dev, "%s:kzalloc Failed\n", __func__); return 0; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 890b8832aff2..9c14a453f73c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2445,7 +2445,7 @@ int uart_register_driver(struct uart_driver *drv) * Maybe we should be using a slab cache for this, especially if * we have a large number of ports to handle. */ - drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); + drv->state = kcalloc(drv->nr, sizeof(struct uart_state), GFP_KERNEL); if (!drv->state) goto out; diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index b93d0225f8c9..72131b5e132e 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -1125,8 +1125,9 @@ static int __init sunsab_init(void) } if (num_channels) { - sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) * - num_channels, GFP_KERNEL); + sunsab_ports = kcalloc(num_channels, + sizeof(struct uart_sunsab_port), + GFP_KERNEL); if (!sunsab_ports) return -ENOMEM; diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index 31d5b1d3b5af..91aea8823af5 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -129,7 +129,7 @@ static int pruss_probe(struct platform_device *pdev) if (!gdev) return -ENOMEM; - gdev->info = kzalloc(sizeof(*p) * MAX_PRUSS_EVT, GFP_KERNEL); + gdev->info = kcalloc(MAX_PRUSS_EVT, sizeof(*p), GFP_KERNEL); if (!gdev->info) { kfree(gdev); return -ENOMEM; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 26c2438d2889..fcae521df29b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1376,7 +1376,7 @@ static int hub_configure(struct usb_hub *hub, dev_info(hub_dev, "%d port%s detected\n", maxchild, (maxchild == 1) ? "" : "s"); - hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL); + hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL); if (!hub->ports) { ret = -ENOMEM; goto fail; diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 1faefea16cec..edaf0b6af4f0 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5079,13 +5079,14 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg); #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS - hsotg->frame_num_array = kzalloc(sizeof(*hsotg->frame_num_array) * - FRAME_NUM_ARRAY_SIZE, GFP_KERNEL); + hsotg->frame_num_array = kcalloc(FRAME_NUM_ARRAY_SIZE, + sizeof(*hsotg->frame_num_array), + GFP_KERNEL); if (!hsotg->frame_num_array) goto error1; - hsotg->last_frame_num_array = kzalloc( - sizeof(*hsotg->last_frame_num_array) * - FRAME_NUM_ARRAY_SIZE, GFP_KERNEL); + hsotg->last_frame_num_array = + kcalloc(FRAME_NUM_ARRAY_SIZE, + sizeof(*hsotg->last_frame_num_array), GFP_KERNEL); if (!hsotg->last_frame_num_array) goto error1; #endif diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index 03149b9d7ea7..a4d9b5e1e50e 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -138,9 +138,9 @@ static int ep_bd_list_alloc(struct bdc_ep *ep) __func__, ep, num_tabs); /* Allocate memory for table array */ - ep->bd_list.bd_table_array = kzalloc( - num_tabs * sizeof(struct bd_table *), - GFP_ATOMIC); + ep->bd_list.bd_table_array = kcalloc(num_tabs, + sizeof(struct bd_table *), + GFP_ATOMIC); if (!ep->bd_list.bd_table_array) return -ENOMEM; diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 9a3f7db26a5e..be59309e848c 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2246,7 +2246,7 @@ static int struct_udc_setup(struct fsl_udc *udc, pdata = dev_get_platdata(&pdev->dev); udc->phy_mode = pdata->phy_mode; - udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL); + udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL); if (!udc->eps) return -1; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index e56db44708bc..1d87295682b8 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -117,8 +117,9 @@ static struct ehci_tt *find_tt(struct usb_device *udev) if (utt->multi) { tt_index = utt->hcpriv; if (!tt_index) { /* Create the index array */ - tt_index = kzalloc(utt->hub->maxchild * - sizeof(*tt_index), GFP_ATOMIC); + tt_index = kcalloc(utt->hub->maxchild, + sizeof(*tt_index), + GFP_ATOMIC); if (!tt_index) return ERR_PTR(-ENOMEM); utt->hcpriv = tt_index; diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index 3a8bbfe43a8e..6e3dad19d369 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -741,8 +741,8 @@ static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd, if (urb_priv == NULL) return -ENOMEM; - urb_priv->isoc_td = kzalloc( - sizeof(struct td) * urb->number_of_packets, mem_flags); + urb_priv->isoc_td = kcalloc(urb->number_of_packets, sizeof(struct td), + mem_flags); if (urb_priv->isoc_td == NULL) { ret = -ENOMEM; goto alloc_td_failed; diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 34e866ad4a81..ad2c082bd0fb 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1024,7 +1024,8 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg return -EINVAL; size = CHUNK_ALIGN(arg); - vec = kzalloc(sizeof(struct mon_pgmap) * (size / CHUNK_SIZE), GFP_KERNEL); + vec = kcalloc(size / CHUNK_SIZE, sizeof(struct mon_pgmap), + GFP_KERNEL); if (vec == NULL) { ret = -ENOMEM; break; diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 34ee9ebe12a3..33d059c40616 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -1068,7 +1068,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) if (!gpriv) return -ENOMEM; - uep = kzalloc(sizeof(struct usbhsg_uep) * pipe_size, GFP_KERNEL); + uep = kcalloc(pipe_size, sizeof(struct usbhsg_uep), GFP_KERNEL); if (!uep) { ret = -ENOMEM; goto usbhs_mod_gadget_probe_err_gpriv; diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 9677e0e31475..c4922b96c93b 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -803,7 +803,8 @@ int usbhs_pipe_probe(struct usbhs_priv *priv) return -EINVAL; } - info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL); + info->pipe = kcalloc(pipe_size, sizeof(struct usbhs_pipe), + GFP_KERNEL); if (!info->pipe) return -ENOMEM; diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index d0f1a6698460..38884aac862b 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -470,7 +470,8 @@ error: int wa_rpipes_create(struct wahc *wa) { wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes); - wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long), + wa->rpipe_bm = kcalloc(BITS_TO_LONGS(wa->rpipes), + sizeof(unsigned long), GFP_KERNEL); if (wa->rpipe_bm == NULL) return -ENOMEM; diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index ce10eb75b042..17fcd3b2e686 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -1685,22 +1685,25 @@ static int vhost_scsi_nexus_cb(struct se_portal_group *se_tpg, for (i = 0; i < VHOST_SCSI_DEFAULT_TAGS; i++) { tv_cmd = &((struct vhost_scsi_cmd *)se_sess->sess_cmd_map)[i]; - tv_cmd->tvc_sgl = kzalloc(sizeof(struct scatterlist) * - VHOST_SCSI_PREALLOC_SGLS, GFP_KERNEL); + tv_cmd->tvc_sgl = kcalloc(VHOST_SCSI_PREALLOC_SGLS, + sizeof(struct scatterlist), + GFP_KERNEL); if (!tv_cmd->tvc_sgl) { pr_err("Unable to allocate tv_cmd->tvc_sgl\n"); goto out; } - tv_cmd->tvc_upages = kzalloc(sizeof(struct page *) * - VHOST_SCSI_PREALLOC_UPAGES, GFP_KERNEL); + tv_cmd->tvc_upages = kcalloc(VHOST_SCSI_PREALLOC_UPAGES, + sizeof(struct page *), + GFP_KERNEL); if (!tv_cmd->tvc_upages) { pr_err("Unable to allocate tv_cmd->tvc_upages\n"); goto out; } - tv_cmd->tvc_prot_sgl = kzalloc(sizeof(struct scatterlist) * - VHOST_SCSI_PREALLOC_PROT_SGLS, GFP_KERNEL); + tv_cmd->tvc_prot_sgl = kcalloc(VHOST_SCSI_PREALLOC_PROT_SGLS, + sizeof(struct scatterlist), + GFP_KERNEL); if (!tv_cmd->tvc_prot_sgl) { pr_err("Unable to allocate tv_cmd->tvc_prot_sgl\n"); goto out; diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c index 08b822656846..ff45dca3ee46 100644 --- a/drivers/video/console/sticore.c +++ b/drivers/video/console/sticore.c @@ -649,7 +649,7 @@ static void *sti_bmode_font_raw(struct sti_cooked_font *f) unsigned char *n, *p, *q; int size = f->raw->bytes_per_char*256+sizeof(struct sti_rom_font); - n = kzalloc(4*size, STI_LOWMEM); + n = kcalloc(4, size, STI_LOWMEM); if (!n) return NULL; p = n + 3; diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c index 9f9a7bef1ff6..d6ba348deb9f 100644 --- a/drivers/video/fbdev/broadsheetfb.c +++ b/drivers/video/fbdev/broadsheetfb.c @@ -617,7 +617,7 @@ static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, int tail_start_addr; int start_sector_addr; - sector_buffer = kzalloc(sizeof(char)*sector_size, GFP_KERNEL); + sector_buffer = kzalloc(sector_size, GFP_KERNEL); if (!sector_buffer) return -ENOMEM; diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index 522cf441842c..852d86c1c527 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -620,7 +620,7 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize, int num = 0, i, first = 1; int ver, rev; - mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); + mode = kcalloc(50, sizeof(struct fb_videomode), GFP_KERNEL); if (mode == NULL) return NULL; @@ -1055,8 +1055,9 @@ void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) if (!(num + svd_n)) return; - m = kzalloc((specs->modedb_len + num + svd_n) * - sizeof(struct fb_videomode), GFP_KERNEL); + m = kcalloc(specs->modedb_len + num + svd_n, + sizeof(struct fb_videomode), + GFP_KERNEL); if (!m) return; diff --git a/drivers/video/fbdev/mmp/fb/mmpfb.c b/drivers/video/fbdev/mmp/fb/mmpfb.c index 92279e02dd94..f27697e07c55 100644 --- a/drivers/video/fbdev/mmp/fb/mmpfb.c +++ b/drivers/video/fbdev/mmp/fb/mmpfb.c @@ -493,8 +493,8 @@ static int modes_setup(struct mmpfb_info *fbi) return 0; } /* put videomode list to info structure */ - videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num, - GFP_KERNEL); + videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode), + GFP_KERNEL); if (!videomodes) { dev_err(fbi->dev, "can't malloc video modes\n"); return -ENOMEM; diff --git a/drivers/video/fbdev/omap2/omapfb/dss/manager.c b/drivers/video/fbdev/omap2/omapfb/dss/manager.c index 69f86d2cc274..d21c641e1f3c 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/manager.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/manager.c @@ -42,8 +42,8 @@ int dss_init_overlay_managers(void) num_managers = dss_feat_get_num_mgrs(); - managers = kzalloc(sizeof(struct omap_overlay_manager) * num_managers, - GFP_KERNEL); + managers = kcalloc(num_managers, sizeof(struct omap_overlay_manager), + GFP_KERNEL); BUG_ON(managers == NULL); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/overlay.c b/drivers/video/fbdev/omap2/omapfb/dss/overlay.c index d6c5d75d2ef8..be17a4785a5e 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/overlay.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/overlay.c @@ -59,8 +59,8 @@ void dss_init_overlays(struct platform_device *pdev) num_overlays = dss_feat_get_num_ovls(); - overlays = kzalloc(sizeof(struct omap_overlay) * num_overlays, - GFP_KERNEL); + overlays = kcalloc(num_overlays, sizeof(struct omap_overlay), + GFP_KERNEL); BUG_ON(overlays == NULL); diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index c592ca513115..440a6636d8f0 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -486,8 +486,9 @@ static int uvesafb_vbe_getmodes(struct uvesafb_ktask *task, mode++; } - par->vbe_modes = kzalloc(sizeof(struct vbe_mode_ib) * - par->vbe_modes_cnt, GFP_KERNEL); + par->vbe_modes = kcalloc(par->vbe_modes_cnt, + sizeof(struct vbe_mode_ib), + GFP_KERNEL); if (!par->vbe_modes) return -ENOMEM; @@ -858,7 +859,7 @@ static int uvesafb_vbe_init_mode(struct fb_info *info) * Convert the modelist into a modedb so that we can use it with * fb_find_mode(). */ - mode = kzalloc(i * sizeof(*mode), GFP_KERNEL); + mode = kcalloc(i, sizeof(*mode), GFP_KERNEL); if (mode) { i = 0; list_for_each(pos, &info->modelist) { diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index 83b8963c9657..5244e93ceafc 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c @@ -181,8 +181,9 @@ struct display_timings *of_get_display_timings(const struct device_node *np) goto entryfail; } - disp->timings = kzalloc(sizeof(struct display_timing *) * - disp->num_timings, GFP_KERNEL); + disp->timings = kcalloc(disp->num_timings, + sizeof(struct display_timing *), + GFP_KERNEL); if (!disp->timings) { pr_err("%pOF: could not allocate timings array\n", np); goto entryfail; diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c index 4e05d7f711fe..8ba726e600e9 100644 --- a/drivers/virt/fsl_hypervisor.c +++ b/drivers/virt/fsl_hypervisor.c @@ -223,7 +223,7 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p) * 'pages' is an array of struct page pointers that's initialized by * get_user_pages(). */ - pages = kzalloc(num_pages * sizeof(struct page *), GFP_KERNEL); + pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); if (!pages) { pr_debug("fsl-hv: could not allocate page list\n"); return -ENOMEM; diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index a491d0ed3f16..b563a4499cc8 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -119,7 +119,7 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, if (!vp_dev->msix_names) goto error; vp_dev->msix_affinity_masks - = kzalloc(nvectors * sizeof *vp_dev->msix_affinity_masks, + = kcalloc(nvectors, sizeof(*vp_dev->msix_affinity_masks), GFP_KERNEL); if (!vp_dev->msix_affinity_masks) goto error; diff --git a/drivers/xen/arm-device.c b/drivers/xen/arm-device.c index 85dd20e05726..3e789c77f568 100644 --- a/drivers/xen/arm-device.c +++ b/drivers/xen/arm-device.c @@ -70,9 +70,9 @@ static int xen_map_device_mmio(const struct resource *resources, if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) continue; - gpfns = kzalloc(sizeof(xen_pfn_t) * nr, GFP_KERNEL); - idxs = kzalloc(sizeof(xen_ulong_t) * nr, GFP_KERNEL); - errs = kzalloc(sizeof(int) * nr, GFP_KERNEL); + gpfns = kcalloc(nr, sizeof(xen_pfn_t), GFP_KERNEL); + idxs = kcalloc(nr, sizeof(xen_ulong_t), GFP_KERNEL); + errs = kcalloc(nr, sizeof(int), GFP_KERNEL); if (!gpfns || !idxs || !errs) { kfree(gpfns); kfree(idxs); diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c index dc062b195c46..a3fdb4fe967d 100644 --- a/fs/btrfs/check-integrity.c +++ b/fs/btrfs/check-integrity.c @@ -1603,8 +1603,8 @@ static int btrfsic_read_block(struct btrfsic_state *state, num_pages = (block_ctx->len + (u64)PAGE_SIZE - 1) >> PAGE_SHIFT; - block_ctx->mem_to_free = kzalloc((sizeof(*block_ctx->datav) + - sizeof(*block_ctx->pagev)) * + block_ctx->mem_to_free = kcalloc(sizeof(*block_ctx->datav) + + sizeof(*block_ctx->pagev), num_pages, GFP_NOFS); if (!block_ctx->mem_to_free) return -ENOMEM; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 5aca336642c0..42329b25877d 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2077,7 +2077,7 @@ struct cifs_writedata * cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) { struct page **pages = - kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); + kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS); if (pages) return cifs_writedata_direct_alloc(pages, complete); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 87eece6fbd48..8d41ca7bfcf1 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2900,7 +2900,7 @@ static struct cifs_readdata * cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete) { struct page **pages = - kzalloc(sizeof(struct page *) * nr_pages, GFP_KERNEL); + kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); struct cifs_readdata *ret = NULL; if (pages) { diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index c969275ce3ee..0057fe3f248d 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -577,7 +577,7 @@ int ext4_ext_precache(struct inode *inode) down_read(&ei->i_data_sem); depth = ext_depth(inode); - path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), + path = kcalloc(depth + 1, sizeof(struct ext4_ext_path), GFP_NOFS); if (path == NULL) { up_read(&ei->i_data_sem); @@ -879,7 +879,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block, } if (!path) { /* account possible depth increase */ - path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 2), + path = kcalloc(depth + 2, sizeof(struct ext4_ext_path), GFP_NOFS); if (unlikely(!path)) return ERR_PTR(-ENOMEM); @@ -1063,7 +1063,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, * We need this to handle errors and free blocks * upon them. */ - ablocks = kzalloc(sizeof(ext4_fsblk_t) * depth, GFP_NOFS); + ablocks = kcalloc(depth, sizeof(ext4_fsblk_t), GFP_NOFS); if (!ablocks) return -ENOMEM; @@ -2921,7 +2921,7 @@ again: path[k].p_block = le16_to_cpu(path[k].p_hdr->eh_entries)+1; } else { - path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), + path = kcalloc(depth + 1, sizeof(struct ext4_ext_path), GFP_NOFS); if (path == NULL) { ext4_journal_stop(handle); diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index c75ad982bcfc..956f27826026 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -461,7 +461,7 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, fh_count = be32_to_cpup(p); fls->mirror_array[i]->fh_versions = - kzalloc(fh_count * sizeof(struct nfs_fh), + kcalloc(fh_count, sizeof(struct nfs_fh), gfp_flags); if (fls->mirror_array[i]->fh_versions == NULL) { rc = -ENOMEM; diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c index d62279d3fc5d..59aa04976331 100644 --- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c +++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c @@ -99,7 +99,8 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev, version_count = be32_to_cpup(p); dprintk("%s: version count %d\n", __func__, version_count); - ds_versions = kzalloc(version_count * sizeof(struct nfs4_ff_ds_version), + ds_versions = kcalloc(version_count, + sizeof(struct nfs4_ff_ds_version), gfp_flags); if (!ds_versions) goto out_scratch; diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 8ceb25a10ea0..a1143f7c2201 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -404,8 +404,9 @@ fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) if (fsloc->locations_count == 0) return 0; - fsloc->locations = kzalloc(fsloc->locations_count - * sizeof(struct nfsd4_fs_location), GFP_KERNEL); + fsloc->locations = kcalloc(fsloc->locations_count, + sizeof(struct nfsd4_fs_location), + GFP_KERNEL); if (!fsloc->locations) return -ENOMEM; for (i=0; i < fsloc->locations_count; i++) { diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index e5dcea6cee5f..bd3475694e83 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -1383,7 +1383,7 @@ static int __ocfs2_recovery_thread(void *arg) goto bail; } - rm_quota = kzalloc(osb->max_slots * sizeof(int), GFP_NOFS); + rm_quota = kcalloc(osb->max_slots, sizeof(int), GFP_NOFS); if (!rm_quota) { status = -ENOMEM; goto bail; diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c index af155c183123..5965f3878d49 100644 --- a/fs/ocfs2/sysfile.c +++ b/fs/ocfs2/sysfile.c @@ -69,10 +69,11 @@ static struct inode **get_local_system_inode(struct ocfs2_super *osb, spin_unlock(&osb->osb_lock); if (unlikely(!local_system_inodes)) { - local_system_inodes = kzalloc(sizeof(struct inode *) * - NUM_LOCAL_SYSTEM_INODES * - osb->max_slots, - GFP_NOFS); + local_system_inodes = + kzalloc(array3_size(sizeof(struct inode *), + NUM_LOCAL_SYSTEM_INODES, + osb->max_slots), + GFP_NOFS); if (!local_system_inodes) { mlog_errno(-ENOMEM); /* diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 08801b45df00..c993dd8db739 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -612,7 +612,7 @@ static int ovl_get_index_name_fh(struct ovl_fh *fh, struct qstr *name) { char *n, *s; - n = kzalloc(fh->len * 2, GFP_KERNEL); + n = kcalloc(fh->len, 2, GFP_KERNEL); if (!n) return -ENOMEM; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 4d765e5e91ed..89921a0d2ebb 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1426,7 +1426,7 @@ static int register_leaf_sysctl_tables(const char *path, char *pos, /* If there are mixed files and directories we need a new table */ if (nr_dirs && nr_files) { struct ctl_table *new; - files = kzalloc(sizeof(struct ctl_table) * (nr_files + 1), + files = kcalloc(nr_files + 1, sizeof(struct ctl_table), GFP_KERNEL); if (!files) goto out; diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index b13fc024d2ee..132ec4406ed0 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1044,7 +1044,8 @@ research: if (blocks_needed == 1) { un = &unf_single; } else { - un = kzalloc(min(blocks_needed, max_to_insert) * UNFM_P_SIZE, GFP_NOFS); + un = kcalloc(min(blocks_needed, max_to_insert), + UNFM_P_SIZE, GFP_NOFS); if (!un) { un = &unf_single; blocks_needed = 1; diff --git a/fs/udf/super.c b/fs/udf/super.c index 0d27d41f5c6e..fc77ea736da7 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1585,7 +1585,7 @@ static struct udf_vds_record *handle_partition_descriptor( struct udf_vds_record *new_loc; unsigned int new_size = ALIGN(partnum, PART_DESC_ALLOC_STEP); - new_loc = kzalloc(sizeof(*new_loc) * new_size, GFP_KERNEL); + new_loc = kcalloc(new_size, sizeof(*new_loc), GFP_KERNEL); if (!new_loc) return ERR_PTR(-ENOMEM); memcpy(new_loc, data->part_descs_loc, @@ -1644,8 +1644,9 @@ static noinline int udf_process_sequence( memset(data.vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH); data.size_part_descs = PART_DESC_ALLOC_STEP; - data.part_descs_loc = kzalloc(sizeof(*data.part_descs_loc) * - data.size_part_descs, GFP_KERNEL); + data.part_descs_loc = kcalloc(data.size_part_descs, + sizeof(*data.part_descs_loc), + GFP_KERNEL); if (!data.part_descs_loc) return -ENOMEM; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index cced0c1e63e2..1494e087890e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5447,7 +5447,7 @@ static int jit_subprogs(struct bpf_verifier_env *env) insn->imm = 1; } - func = kzalloc(sizeof(prog) * env->subprog_cnt, GFP_KERNEL); + func = kcalloc(env->subprog_cnt, sizeof(prog), GFP_KERNEL); if (!func) return -ENOMEM; diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index aaa69531fae2..2ddfce8f1e8f 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -691,7 +691,7 @@ static int kdb_defcmd2(const char *cmdstr, const char *argv0) } if (!s->usable) return KDB_NOTIMP; - s->command = kzalloc((s->count + 1) * sizeof(*(s->command)), GFP_KDB); + s->command = kcalloc(s->count + 1, sizeof(*(s->command)), GFP_KDB); if (!s->command) { kdb_printf("Could not allocate new kdb_defcmd table for %s\n", cmdstr); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 1725b902983f..ccc579a7d32e 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1184,7 +1184,8 @@ static struct xol_area *__create_xol_area(unsigned long vaddr) if (unlikely(!area)) goto out; - area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long), GFP_KERNEL); + area->bitmap = kcalloc(BITS_TO_LONGS(UINSNS_PER_PAGE), sizeof(long), + GFP_KERNEL); if (!area->bitmap) goto free_area; diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c index 4ceeb13a74ed..8402b3349dca 100644 --- a/kernel/locking/locktorture.c +++ b/kernel/locking/locktorture.c @@ -989,7 +989,8 @@ static int __init lock_torture_init(void) } if (nwriters_stress) { - writer_tasks = kzalloc(cxt.nrealwriters_stress * sizeof(writer_tasks[0]), + writer_tasks = kcalloc(cxt.nrealwriters_stress, + sizeof(writer_tasks[0]), GFP_KERNEL); if (writer_tasks == NULL) { VERBOSE_TOROUT_ERRSTRING("writer_tasks: Out of memory"); @@ -999,7 +1000,8 @@ static int __init lock_torture_init(void) } if (cxt.cur_ops->readlock) { - reader_tasks = kzalloc(cxt.nrealreaders_stress * sizeof(reader_tasks[0]), + reader_tasks = kcalloc(cxt.nrealreaders_stress, + sizeof(reader_tasks[0]), GFP_KERNEL); if (reader_tasks == NULL) { VERBOSE_TOROUT_ERRSTRING("reader_tasks: Out of memory"); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e497c05aab7f..1866e64792a7 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -10215,10 +10215,10 @@ int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent) struct cfs_rq *cfs_rq; int i; - tg->cfs_rq = kzalloc(sizeof(cfs_rq) * nr_cpu_ids, GFP_KERNEL); + tg->cfs_rq = kcalloc(nr_cpu_ids, sizeof(cfs_rq), GFP_KERNEL); if (!tg->cfs_rq) goto err; - tg->se = kzalloc(sizeof(se) * nr_cpu_ids, GFP_KERNEL); + tg->se = kcalloc(nr_cpu_ids, sizeof(se), GFP_KERNEL); if (!tg->se) goto err; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index ef3c4e6f5345..47556b0c9a95 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -183,10 +183,10 @@ int alloc_rt_sched_group(struct task_group *tg, struct task_group *parent) struct sched_rt_entity *rt_se; int i; - tg->rt_rq = kzalloc(sizeof(rt_rq) * nr_cpu_ids, GFP_KERNEL); + tg->rt_rq = kcalloc(nr_cpu_ids, sizeof(rt_rq), GFP_KERNEL); if (!tg->rt_rq) goto err; - tg->rt_se = kzalloc(sizeof(rt_se) * nr_cpu_ids, GFP_KERNEL); + tg->rt_se = kcalloc(nr_cpu_ids, sizeof(rt_se), GFP_KERNEL); if (!tg->rt_se) goto err; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 6a78cf70761d..2d9837c0aff4 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -3047,7 +3047,8 @@ int proc_do_large_bitmap(struct ctl_table *table, int write, if (IS_ERR(kbuf)) return PTR_ERR(kbuf); - tmp_bitmap = kzalloc(BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long), + tmp_bitmap = kcalloc(BITS_TO_LONGS(bitmap_len), + sizeof(unsigned long), GFP_KERNEL); if (!tmp_bitmap) { kfree(kbuf); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index df4b6254f986..efed9c1cfb7e 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -728,7 +728,7 @@ static int ftrace_profile_init_cpu(int cpu) */ size = FTRACE_PROFILE_HASH_SIZE; - stat->hash = kzalloc(sizeof(struct hlist_head) * size, GFP_KERNEL); + stat->hash = kcalloc(size, sizeof(struct hlist_head), GFP_KERNEL); if (!stat->hash) return -ENOMEM; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8ea855015613..c9336e98ac59 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4361,7 +4361,8 @@ int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled) if (mask == TRACE_ITER_RECORD_TGID) { if (!tgid_map) - tgid_map = kzalloc((PID_MAX_DEFAULT + 1) * sizeof(*tgid_map), + tgid_map = kcalloc(PID_MAX_DEFAULT + 1, + sizeof(*tgid_map), GFP_KERNEL); if (!tgid_map) { tr->trace_flags &= ~TRACE_ITER_RECORD_TGID; diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 465a28b4cd32..78b192071ef7 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -5638,7 +5638,7 @@ static void __init wq_numa_init(void) * available. Build one from cpu_to_node() which should have been * fully initialized by now. */ - tbl = kzalloc(nr_node_ids * sizeof(tbl[0]), GFP_KERNEL); + tbl = kcalloc(nr_node_ids, sizeof(tbl[0]), GFP_KERNEL); BUG_ON(!tbl); for_each_node(node) diff --git a/lib/lru_cache.c b/lib/lru_cache.c index 28ba40b99337..2b10a4024c35 100644 --- a/lib/lru_cache.c +++ b/lib/lru_cache.c @@ -119,7 +119,7 @@ struct lru_cache *lc_create(const char *name, struct kmem_cache *cache, slot = kcalloc(e_count, sizeof(struct hlist_head), GFP_KERNEL); if (!slot) goto out_fail; - element = kzalloc(e_count * sizeof(struct lc_element *), GFP_KERNEL); + element = kcalloc(e_count, sizeof(struct lc_element *), GFP_KERNEL); if (!element) goto out_fail; diff --git a/lib/mpi/mpiutil.c b/lib/mpi/mpiutil.c index 2dbfc4c8a237..20ed0f766787 100644 --- a/lib/mpi/mpiutil.c +++ b/lib/mpi/mpiutil.c @@ -98,7 +98,7 @@ int mpi_resize(MPI a, unsigned nlimbs) kzfree(a->d); a->d = p; } else { - a->d = kzalloc(nlimbs * sizeof(mpi_limb_t), GFP_KERNEL); + a->d = kcalloc(nlimbs, sizeof(mpi_limb_t), GFP_KERNEL); if (!a->d) return -ENOMEM; } diff --git a/mm/slab.c b/mm/slab.c index 36688f6c87eb..aa76a70e087e 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -4338,7 +4338,8 @@ static int leaks_show(struct seq_file *m, void *p) if (x[0] == x[1]) { /* Increase the buffer size */ mutex_unlock(&slab_mutex); - m->private = kzalloc(x[0] * 4 * sizeof(unsigned long), GFP_KERNEL); + m->private = kcalloc(x[0] * 4, sizeof(unsigned long), + GFP_KERNEL); if (!m->private) { /* Too bad, we are really out */ m->private = x; diff --git a/mm/slub.c b/mm/slub.c index faf5dcb7b44f..a3b8467c14af 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3623,8 +3623,9 @@ static void list_slab_objects(struct kmem_cache *s, struct page *page, #ifdef CONFIG_SLUB_DEBUG void *addr = page_address(page); void *p; - unsigned long *map = kzalloc(BITS_TO_LONGS(page->objects) * - sizeof(long), GFP_ATOMIC); + unsigned long *map = kcalloc(BITS_TO_LONGS(page->objects), + sizeof(long), + GFP_ATOMIC); if (!map) return; slab_err(s, page, text, s->name); @@ -4752,7 +4753,7 @@ static ssize_t show_slab_objects(struct kmem_cache *s, int x; unsigned long *nodes; - nodes = kzalloc(sizeof(unsigned long) * nr_node_ids, GFP_KERNEL); + nodes = kcalloc(nr_node_ids, sizeof(unsigned long), GFP_KERNEL); if (!nodes) return -ENOMEM; diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index cb4729539b82..920665dd92db 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -333,7 +333,7 @@ static int br_mdb_rehash(struct net_bridge_mdb_htable __rcu **mdbp, int max, mdb->max = max; mdb->old = old; - mdb->mhash = kzalloc(max * sizeof(*mdb->mhash), GFP_ATOMIC); + mdb->mhash = kcalloc(max, sizeof(*mdb->mhash), GFP_ATOMIC); if (!mdb->mhash) { kfree(mdb); return -ENOMEM; diff --git a/net/can/bcm.c b/net/can/bcm.c index 394ff1d2791f..9393f25df08d 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1105,7 +1105,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } /* create and init array for received CAN frames */ - op->last_frames = kzalloc(msg_head->nframes * op->cfsiz, + op->last_frames = kcalloc(msg_head->nframes, + op->cfsiz, GFP_KERNEL); if (!op->last_frames) { kfree(op->frames); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 436e4f9cc7f0..8be6be2d9c7b 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -911,7 +911,7 @@ static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev, memset(&info, 0, sizeof(info)); info.cmd = ETHTOOL_GSSET_INFO; - info_buf = kzalloc(n_bits * sizeof(u32), GFP_USER); + info_buf = kcalloc(n_bits, sizeof(u32), GFP_USER); if (!info_buf) return -ENOMEM; @@ -1017,7 +1017,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, if (info.cmd == ETHTOOL_GRXCLSRLALL) { if (info.rule_cnt > 0) { if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32)) - rule_buf = kzalloc(info.rule_cnt * sizeof(u32), + rule_buf = kcalloc(info.rule_cnt, sizeof(u32), GFP_USER); if (!rule_buf) return -ENOMEM; diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index dc2960be51e0..b231e40f006a 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -38,7 +38,7 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, { void *hdr; int i, pages = 0; - uint32_t *buf = kzalloc(32 * sizeof(uint32_t), GFP_KERNEL); + uint32_t *buf = kcalloc(32, sizeof(uint32_t), GFP_KERNEL); pr_debug("%s\n", __func__); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 63aa39b3af03..b21833651394 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -567,7 +567,7 @@ static int rtentry_to_fib_config(struct net *net, int cmd, struct rtentry *rt, struct nlattr *mx; int len = 0; - mx = kzalloc(3 * nla_total_size(4), GFP_KERNEL); + mx = kcalloc(3, nla_total_size(4), GFP_KERNEL); if (!mx) return -ENOMEM; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6bcd1eacc1f0..1df6e97106d7 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -649,7 +649,7 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, hash = rcu_dereference(nh->nh_exceptions); if (!hash) { - hash = kzalloc(FNHE_HASH_SIZE * sizeof(*hash), GFP_ATOMIC); + hash = kcalloc(FNHE_HASH_SIZE, sizeof(*hash), GFP_ATOMIC); if (!hash) goto out_unlock; rcu_assign_pointer(nh->nh_exceptions, hash); diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index d8c4b6374377..be491bf6ab6e 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -956,7 +956,7 @@ static int __net_init icmpv6_sk_init(struct net *net) int err, i, j; net->ipv6.icmp_sk = - kzalloc(nr_cpu_ids * sizeof(struct sock *), GFP_KERNEL); + kcalloc(nr_cpu_ids, sizeof(struct sock *), GFP_KERNEL); if (!net->ipv6.icmp_sk) return -ENOMEM; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 89178b46b32f..d9558ffb8acf 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -1186,7 +1186,7 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, lockdep_assert_held(&local->mtx); lockdep_assert_held(&local->chanctx_mtx); - vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL); + vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL); if (!vif_chsw) return -ENOMEM; diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 7fadfbca9f1b..76048b53c5b2 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -592,7 +592,7 @@ minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) max_rates = sband->n_bitrates; } - mi->r = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp); + mi->r = kcalloc(max_rates, sizeof(struct minstrel_rate), gfp); if (!mi->r) goto error; diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 267ab9d5137e..67ebdeaffbbc 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1313,7 +1313,7 @@ minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) if (!msp) return NULL; - msp->ratelist = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp); + msp->ratelist = kcalloc(max_rates, sizeof(struct minstrel_rate), gfp); if (!msp->ratelist) goto error; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index a3b1bcc2b461..2e917a6d239d 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1157,7 +1157,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, } } - ie = kzalloc(num_bands * iebufsz, GFP_KERNEL); + ie = kcalloc(iebufsz, num_bands, GFP_KERNEL); if (!ie) { ret = -ENOMEM; goto out; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2d82c88efd0b..5e2e511c4a6f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1803,8 +1803,9 @@ static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) if (WARN_ON(res)) return res; - funcs = kzalloc((sdata->local->hw.max_nan_de_entries + 1) * - sizeof(*funcs), GFP_KERNEL); + funcs = kcalloc(sdata->local->hw.max_nan_de_entries + 1, + sizeof(*funcs), + GFP_KERNEL); if (!funcs) return -ENOMEM; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index cae4a026859d..f0411fbffe77 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -5303,7 +5303,7 @@ static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx, if (err < 0) return err; - ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL); + ops = kcalloc(n, sizeof(struct nf_hook_ops), GFP_KERNEL); if (!ops) return -ENOMEM; diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index cb5b5f207777..e5d27b2e4eba 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -190,8 +190,9 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, if (class_max > NF_CT_MAX_EXPECT_CLASSES) return -EOVERFLOW; - expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) * - class_max, GFP_KERNEL); + expect_policy = kcalloc(class_max, + sizeof(struct nf_conntrack_expect_policy), + GFP_KERNEL); if (expect_policy == NULL) return -ENOMEM; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index b97eb766a1d5..93fbcafbf388 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1395,7 +1395,7 @@ static int __init nr_proto_init(void) return -1; } - dev_nr = kzalloc(nr_ndevs * sizeof(struct net_device *), GFP_KERNEL); + dev_nr = kcalloc(nr_ndevs, sizeof(struct net_device *), GFP_KERNEL); if (dev_nr == NULL) { printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device array\n"); return -1; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index f81c1d0ddff4..19f6765566e7 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -47,7 +47,7 @@ static struct hlist_head *dev_table; */ int ovs_vport_init(void) { - dev_table = kzalloc(VPORT_HASH_BUCKETS * sizeof(struct hlist_head), + dev_table = kcalloc(VPORT_HASH_BUCKETS, sizeof(struct hlist_head), GFP_KERNEL); if (!dev_table) return -ENOMEM; diff --git a/net/rds/ib.c b/net/rds/ib.c index 02deee29e7f1..b6ad38e48f62 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -163,7 +163,8 @@ static void rds_ib_add_one(struct ib_device *device) rds_ibdev->max_initiator_depth = device->attrs.max_qp_init_rd_atom; rds_ibdev->max_responder_resources = device->attrs.max_qp_rd_atom; - rds_ibdev->vector_load = kzalloc(sizeof(int) * device->num_comp_vectors, + rds_ibdev->vector_load = kcalloc(device->num_comp_vectors, + sizeof(int), GFP_KERNEL); if (!rds_ibdev->vector_load) { pr_err("RDS/IB: %s failed to allocate vector memory\n", diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 5b73fea849df..ebe42e7eb456 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1514,7 +1514,8 @@ static int __init rose_proto_init(void) rose_callsign = null_ax25_address; - dev_rose = kzalloc(rose_ndevs * sizeof(struct net_device *), GFP_KERNEL); + dev_rose = kcalloc(rose_ndevs, sizeof(struct net_device *), + GFP_KERNEL); if (dev_rose == NULL) { printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate device structure\n"); rc = -ENOMEM; diff --git a/net/sctp/auth.c b/net/sctp/auth.c index e64630cd3331..5b537613946f 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -482,8 +482,9 @@ int sctp_auth_init_hmacs(struct sctp_endpoint *ep, gfp_t gfp) return 0; /* Allocated the array of pointers to transorms */ - ep->auth_hmacs = kzalloc(sizeof(struct crypto_shash *) * - SCTP_AUTH_NUM_HMACS, gfp); + ep->auth_hmacs = kcalloc(SCTP_AUTH_NUM_HMACS, + sizeof(struct crypto_shash *), + gfp); if (!ep->auth_hmacs) return -ENOMEM; diff --git a/net/smc/smc_wr.c b/net/smc/smc_wr.c index cc7c1bb60fe8..dbd2605d1962 100644 --- a/net/smc/smc_wr.c +++ b/net/smc/smc_wr.c @@ -584,9 +584,9 @@ int smc_wr_alloc_link_mem(struct smc_link *link) GFP_KERNEL); if (!link->wr_rx_sges) goto no_mem_wr_tx_sges; - link->wr_tx_mask = kzalloc( - BITS_TO_LONGS(SMC_WR_BUF_CNT) * sizeof(*link->wr_tx_mask), - GFP_KERNEL); + link->wr_tx_mask = kcalloc(BITS_TO_LONGS(SMC_WR_BUF_CNT), + sizeof(*link->wr_tx_mask), + GFP_KERNEL); if (!link->wr_tx_mask) goto no_mem_wr_rx_sges; link->wr_tx_pends = kcalloc(SMC_WR_BUF_CNT, diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c index 46b295e4f2b8..d58bd058b09b 100644 --- a/net/sunrpc/auth_gss/gss_rpc_upcall.c +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c @@ -224,7 +224,7 @@ static void gssp_free_receive_pages(struct gssx_arg_accept_sec_context *arg) static int gssp_alloc_receive_pages(struct gssx_arg_accept_sec_context *arg) { arg->npages = DIV_ROUND_UP(NGROUPS_MAX * 4, PAGE_SIZE); - arg->pages = kzalloc(arg->npages * sizeof(struct page *), GFP_KERNEL); + arg->pages = kcalloc(arg->npages, sizeof(struct page *), GFP_KERNEL); /* * XXX: actual pages are allocated by xdr layer in * xdr_partial_copy_from_skb. diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index cdda4744c9b1..109fbe591e7b 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1683,7 +1683,7 @@ struct cache_detail *cache_create_net(const struct cache_detail *tmpl, struct ne if (cd == NULL) return ERR_PTR(-ENOMEM); - cd->hash_table = kzalloc(cd->hash_size * sizeof(struct hlist_head), + cd->hash_table = kcalloc(cd->hash_size, sizeof(struct hlist_head), GFP_KERNEL); if (cd->hash_table == NULL) { kfree(cd); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 07514ca011b2..c7bbe5f0aae8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10833,7 +10833,7 @@ static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev, struct nlattr **tb; int err; - tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL); + tb = kcalloc(NUM_NL80211_ATTR, sizeof(*tb), GFP_KERNEL); if (!tb) return -ENOMEM; @@ -11793,7 +11793,7 @@ static int nl80211_nan_add_func(struct sk_buff *skb, func->srf_num_macs = n_entries; func->srf_macs = - kzalloc(sizeof(*func->srf_macs) * n_entries, + kcalloc(n_entries, sizeof(*func->srf_macs), GFP_KERNEL); if (!func->srf_macs) { err = -ENOMEM; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index b9e6b2cafa69..0e566a01d217 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -475,7 +475,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) /* currently 4 exec bits and entries 0-3 are reserved iupcx */ if (size > 16 - 4) goto fail; - profile->file.trans.table = kzalloc(sizeof(char *) * size, + profile->file.trans.table = kcalloc(size, sizeof(char *), GFP_KERNEL); if (!profile->file.trans.table) goto fail; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index a2d44824121c..dd2ceec06fef 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2118,7 +2118,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) int rc = 0; struct policy_file file = { data, len }, *fp = &file; - oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL); + oldpolicydb = kcalloc(2, sizeof(*oldpolicydb), GFP_KERNEL); if (!oldpolicydb) { rc = -ENOMEM; goto out; diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index 12aa15df435d..ad7a0a32557d 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -147,7 +147,7 @@ static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) __le32 *reg; int i; - reg = kzalloc(sizeof(__le32) * 18, GFP_KERNEL); + reg = kcalloc(18, sizeof(__le32), GFP_KERNEL); if (reg == NULL) return -ENOMEM; diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 908658a00377..2ada8444abd9 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -275,7 +275,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) /* Get AMIXER resource */ n_amixer = (n_amixer < 2) ? 2 : n_amixer; - apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL); if (!apcm->amixers) { err = -ENOMEM; goto error1; @@ -543,18 +543,18 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) } if (n_srcc) { - apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL); + apcm->srccs = kcalloc(n_srcc, sizeof(void *), GFP_KERNEL); if (!apcm->srccs) return -ENOMEM; } if (n_amixer) { - apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL); if (!apcm->amixers) { err = -ENOMEM; goto error1; } } - apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL); + apcm->srcimps = kcalloc(n_srcimp, sizeof(void *), GFP_KERNEL); if (!apcm->srcimps) { err = -ENOMEM; goto error1; @@ -819,7 +819,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, /* Get AMIXER resource */ n_amixer = (n_amixer < 2) ? 2 : n_amixer; - apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); + apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL); if (!apcm->amixers) { err = -ENOMEM; goto error1; @@ -1378,19 +1378,19 @@ static int atc_get_resources(struct ct_atc *atc) num_daios = ((atc->model == CTSB1270) ? 8 : 7); num_srcs = ((atc->model == CTSB1270) ? 6 : 4); - atc->daios = kzalloc(sizeof(void *)*num_daios, GFP_KERNEL); + atc->daios = kcalloc(num_daios, sizeof(void *), GFP_KERNEL); if (!atc->daios) return -ENOMEM; - atc->srcs = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL); + atc->srcs = kcalloc(num_srcs, sizeof(void *), GFP_KERNEL); if (!atc->srcs) return -ENOMEM; - atc->srcimps = kzalloc(sizeof(void *)*num_srcs, GFP_KERNEL); + atc->srcimps = kcalloc(num_srcs, sizeof(void *), GFP_KERNEL); if (!atc->srcimps) return -ENOMEM; - atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL); + atc->pcm = kcalloc(2 * 4, sizeof(void *), GFP_KERNEL); if (!atc->pcm) return -ENOMEM; diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 7f089cb433e1..f35a7341e446 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -398,7 +398,8 @@ static int dao_rsc_init(struct dao *dao, if (err) return err; - dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL); + dao->imappers = kzalloc(array3_size(sizeof(void *), desc->msr, 2), + GFP_KERNEL); if (!dao->imappers) { err = -ENOMEM; goto error1; diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 4f4a2a5dedb8..db710d0a609f 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -910,13 +910,14 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer) if (!mixer) return -ENOMEM; - mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM), + mixer->amixers = kcalloc(NUM_CT_AMIXERS * CHN_NUM, sizeof(void *), GFP_KERNEL); if (!mixer->amixers) { err = -ENOMEM; goto error1; } - mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL); + mixer->sums = kcalloc(NUM_CT_SUMS * CHN_NUM, sizeof(void *), + GFP_KERNEL); if (!mixer->sums) { err = -ENOMEM; goto error2; diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index bb4c9c3c89ae..a4fc10723fc6 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -679,7 +679,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp, return err; /* Reserve memory for imapper nodes */ - srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr, + srcimp->imappers = kcalloc(desc->msr, sizeof(struct imapper), GFP_KERNEL); if (!srcimp->imappers) { err = -ENOMEM; diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 292e2c592c17..04e949aa01ad 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -7482,7 +7482,9 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) spec->chip_init_verbs = ca0132_init_verbs0; if (spec->quirk == QUIRK_SBZ) spec->sbz_init_verbs = sbz_init_verbs; - spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL); + spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS, + sizeof(struct hda_verb), + GFP_KERNEL); if (!spec->spec_init_verbs) return -ENOMEM; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 2175dccdf388..2fcdd84021a5 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1899,7 +1899,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", pos + len, be32_to_cpu(val)); - alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); + alg = kcalloc(len, 2, GFP_KERNEL | GFP_DMA); if (!alg) return ERR_PTR(-ENOMEM); diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index 62f3a8e0ec87..dcff13802c00 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -121,8 +121,8 @@ static int msg_empty_list_init(struct sst_generic_ipc *ipc) { int i; - ipc->msg = kzalloc(sizeof(struct ipc_message) * - IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + ipc->msg = kcalloc(IPC_EMPTY_LIST_SIZE, sizeof(struct ipc_message), + GFP_KERNEL); if (ipc->msg == NULL) return -ENOMEM; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3d56f1fe5914..61542847cb3b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -373,8 +373,8 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( if (!rtd->dai_link->ops) rtd->dai_link->ops = &null_snd_soc_ops; - rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) * - dai_link->num_codecs, + rtd->codec_dais = kcalloc(dai_link->num_codecs, + sizeof(struct snd_soc_dai *), GFP_KERNEL); if (!rtd->codec_dais) { kfree(rtd); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 255cad43a972..229c12349803 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3055,7 +3055,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) continue; if (w->num_kcontrols) { - w->kcontrols = kzalloc(w->num_kcontrols * + w->kcontrols = kcalloc(w->num_kcontrols, sizeof(struct snd_kcontrol *), GFP_KERNEL); if (!w->kcontrols) { diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 3fd5d9c867b9..53f121a50c97 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -885,7 +885,7 @@ static int soc_tplg_denum_create_texts(struct soc_enum *se, int i, ret; se->dobj.control.dtexts = - kzalloc(sizeof(char *) * ec->items, GFP_KERNEL); + kcalloc(ec->items, sizeof(char *), GFP_KERNEL); if (se->dobj.control.dtexts == NULL) return -ENOMEM; diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 224a6a5d1c0e..2dd2518a71d3 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -591,12 +591,14 @@ static int usb6fire_pcm_buffers_init(struct pcm_runtime *rt) int i; for (i = 0; i < PCM_N_URBS; i++) { - rt->out_urbs[i].buffer = kzalloc(PCM_N_PACKETS_PER_URB - * PCM_MAX_PACKET_SIZE, GFP_KERNEL); + rt->out_urbs[i].buffer = kcalloc(PCM_MAX_PACKET_SIZE, + PCM_N_PACKETS_PER_URB, + GFP_KERNEL); if (!rt->out_urbs[i].buffer) return -ENOMEM; - rt->in_urbs[i].buffer = kzalloc(PCM_N_PACKETS_PER_URB - * PCM_MAX_PACKET_SIZE, GFP_KERNEL); + rt->in_urbs[i].buffer = kcalloc(PCM_MAX_PACKET_SIZE, + PCM_N_PACKETS_PER_URB, + GFP_KERNEL); if (!rt->in_urbs[i].buffer) return -ENOMEM; } diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c index 947d6168f24a..d8a14d769f48 100644 --- a/sound/usb/line6/capture.c +++ b/sound/usb/line6/capture.c @@ -264,8 +264,8 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) struct usb_line6 *line6 = line6pcm->line6; int i; - line6pcm->in.urbs = kzalloc( - sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL); + line6pcm->in.urbs = kcalloc(line6->iso_buffers, sizeof(struct urb *), + GFP_KERNEL); if (line6pcm->in.urbs == NULL) return -ENOMEM; diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c index 819e9b2d1d6e..dec89d2beb57 100644 --- a/sound/usb/line6/playback.c +++ b/sound/usb/line6/playback.c @@ -409,8 +409,8 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) struct usb_line6 *line6 = line6pcm->line6; int i; - line6pcm->out.urbs = kzalloc( - sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL); + line6pcm->out.urbs = kcalloc(line6->iso_buffers, sizeof(struct urb *), + GFP_KERNEL); if (line6pcm->out.urbs == NULL) return -ENOMEM; diff --git a/virt/kvm/arm/vgic/vgic-v4.c b/virt/kvm/arm/vgic/vgic-v4.c index bc4265154bac..1ed5f2286b8e 100644 --- a/virt/kvm/arm/vgic/vgic-v4.c +++ b/virt/kvm/arm/vgic/vgic-v4.c @@ -126,7 +126,7 @@ int vgic_v4_init(struct kvm *kvm) nr_vcpus = atomic_read(&kvm->online_vcpus); - dist->its_vm.vpes = kzalloc(sizeof(*dist->its_vm.vpes) * nr_vcpus, + dist->its_vm.vpes = kcalloc(nr_vcpus, sizeof(*dist->its_vm.vpes), GFP_KERNEL); if (!dist->its_vm.vpes) return -ENOMEM; -- cgit v1.2.3 From a86854d0c599b3202307abceb68feee4d7061578 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 12 Jun 2018 14:07:58 -0700 Subject: treewide: devm_kzalloc() -> devm_kcalloc() The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc(). This patch replaces cases of: devm_kzalloc(handle, a * b, gfp) with: devm_kcalloc(handle, a * b, gfp) as well as handling cases of: devm_kzalloc(handle, a * b * c, gfp) with: devm_kzalloc(handle, array3_size(a, b, c), gfp) as it's slightly less ugly than: devm_kcalloc(handle, array_size(a, b), c, gfp) This does, however, attempt to ignore constant size factors like: devm_kzalloc(handle, 4 * 1024, gfp) though any constants defined via macros get caught up in the conversion. Any factors with a sizeof() of "unsigned char", "char", and "u8" were dropped, since they're redundant. Some manual whitespace fixes were needed in this patch, as Coccinelle really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...". The Coccinelle script used for this was: // Fix redundant parens around sizeof(). @@ expression HANDLE; type TYPE; expression THING, E; @@ ( devm_kzalloc(HANDLE, - (sizeof(TYPE)) * E + sizeof(TYPE) * E , ...) | devm_kzalloc(HANDLE, - (sizeof(THING)) * E + sizeof(THING) * E , ...) ) // Drop single-byte sizes and redundant parens. @@ expression HANDLE; expression COUNT; typedef u8; typedef __u8; @@ ( devm_kzalloc(HANDLE, - sizeof(u8) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(__u8) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(char) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(unsigned char) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(u8) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(__u8) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(char) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(unsigned char) * COUNT + COUNT , ...) ) // 2-factor product with sizeof(type/expression) and identifier or constant. @@ expression HANDLE; type TYPE; expression THING; identifier COUNT_ID; constant COUNT_CONST; @@ ( - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (COUNT_ID) + COUNT_ID, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * COUNT_ID + COUNT_ID, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (COUNT_CONST) + COUNT_CONST, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * COUNT_CONST + COUNT_CONST, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (COUNT_ID) + COUNT_ID, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * COUNT_ID + COUNT_ID, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (COUNT_CONST) + COUNT_CONST, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * COUNT_CONST + COUNT_CONST, sizeof(THING) , ...) ) // 2-factor product, only identifiers. @@ expression HANDLE; identifier SIZE, COUNT; @@ - devm_kzalloc + devm_kcalloc (HANDLE, - SIZE * COUNT + COUNT, SIZE , ...) // 3-factor product with 1 sizeof(type) or sizeof(expression), with // redundant parens removed. @@ expression HANDLE; expression THING; identifier STRIDE, COUNT; type TYPE; @@ ( devm_kzalloc(HANDLE, - sizeof(TYPE) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) ) // 3-factor product with 2 sizeof(variable), with redundant parens removed. @@ expression HANDLE; expression THING1, THING2; identifier COUNT; type TYPE1, TYPE2; @@ ( devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(TYPE2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) ) // 3-factor product, only identifiers, with redundant parens removed. @@ expression HANDLE; identifier STRIDE, SIZE, COUNT; @@ ( devm_kzalloc(HANDLE, - (COUNT) * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) ) // Any remaining multi-factor products, first at least 3-factor products, // when they're not all constants... @@ expression HANDLE; expression E1, E2, E3; constant C1, C2, C3; @@ ( devm_kzalloc(HANDLE, C1 * C2 * C3, ...) | devm_kzalloc(HANDLE, - (E1) * E2 * E3 + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - (E1) * (E2) * E3 + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - (E1) * (E2) * (E3) + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - E1 * E2 * E3 + array3_size(E1, E2, E3) , ...) ) // And then all remaining 2 factors products when they're not all constants, // keeping sizeof() as the second factor argument. @@ expression HANDLE; expression THING, E1, E2; type TYPE; constant C1, C2, C3; @@ ( devm_kzalloc(HANDLE, sizeof(THING) * C2, ...) | devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...) | devm_kzalloc(HANDLE, C1 * C2 * C3, ...) | devm_kzalloc(HANDLE, C1 * C2, ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (E2) + E2, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * E2 + E2, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (E2) + E2, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * E2 + E2, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - (E1) * E2 + E1, E2 , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - (E1) * (E2) + E1, E2 , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - E1 * E2 + E1, E2 , ...) ) Signed-off-by: Kees Cook --- drivers/acpi/fan.c | 4 +-- drivers/acpi/nfit/core.c | 7 ++-- drivers/ata/sata_mv.c | 8 ++--- drivers/bus/fsl-mc/fsl-mc-allocator.c | 6 ++-- drivers/char/tpm/tpm2-cmd.c | 2 +- drivers/clk/bcm/clk-bcm2835.c | 4 +-- drivers/clk/ti/adpll.c | 6 ++-- drivers/cpufreq/brcmstb-avs-cpufreq.c | 2 +- drivers/cpufreq/imx6q-cpufreq.c | 3 +- drivers/crypto/marvell/cesa.c | 2 +- drivers/crypto/talitos.c | 13 +++++--- drivers/devfreq/devfreq.c | 15 +++++---- drivers/devfreq/event/exynos-ppmu.c | 2 +- drivers/dma/k3dma.c | 8 ++--- drivers/dma/mv_xor_v2.c | 5 +-- drivers/dma/s3c24xx-dma.c | 6 ++-- drivers/dma/zx_dma.c | 8 ++--- drivers/firmware/arm_scpi.c | 2 +- drivers/firmware/ti_sci.c | 6 ++-- drivers/gpio/gpio-adnp.c | 2 +- drivers/gpio/gpio-aspeed.c | 4 +-- drivers/gpio/gpio-bcm-kona.c | 7 ++-- drivers/gpio/gpio-davinci.c | 4 +-- drivers/gpio/gpio-htc-egpio.c | 4 +-- drivers/gpio/gpio-thunderx.c | 9 ++--- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 4 +-- drivers/gpu/drm/exynos/exynos_drm_fimc.c | 3 +- drivers/gpu/drm/exynos/exynos_drm_gsc.c | 5 +-- drivers/gpu/drm/exynos/exynos_hdmi.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi.c | 24 ++++++++----- drivers/gpu/drm/msm/hdmi/hdmi_phy.c | 4 +-- drivers/hid/hid-sensor-hub.c | 3 +- drivers/hid/intel-ish-hid/ishtp-hid-client.c | 4 +-- drivers/hid/wacom_sys.c | 4 +-- drivers/hwmon/aspeed-pwm-tacho.c | 2 +- drivers/hwmon/gpio-fan.c | 8 ++--- drivers/hwmon/ibmpowernv.c | 9 ++--- drivers/hwmon/iio_hwmon.c | 4 +-- drivers/hwmon/nct6683.c | 4 +-- drivers/hwmon/nct6775.c | 4 +-- drivers/hwmon/pmbus/pmbus_core.c | 4 +-- drivers/hwmon/pmbus/ucd9000.c | 4 +-- drivers/hwmon/pwm-fan.c | 2 +- drivers/hwtracing/coresight/coresight-etb10.c | 4 +-- drivers/hwtracing/coresight/of_coresight.c | 9 +++-- drivers/i2c/busses/i2c-qup.c | 8 ++--- drivers/i2c/muxes/i2c-mux-gpio.c | 9 ++--- drivers/i2c/muxes/i2c-mux-reg.c | 4 +-- drivers/iio/adc/at91_adc.c | 7 ++-- drivers/iio/adc/max1363.c | 6 ++-- drivers/iio/adc/twl6030-gpadc.c | 7 ++-- drivers/iio/dac/ad5592r-base.c | 5 +-- drivers/iio/multiplexer/iio-mux.c | 7 ++-- drivers/input/keyboard/clps711x-keypad.c | 4 +-- drivers/input/keyboard/matrix_keypad.c | 6 ++-- drivers/input/keyboard/samsung-keypad.c | 2 +- drivers/input/matrix-keymap.c | 4 +-- drivers/input/misc/rotary_encoder.c | 4 +-- drivers/input/rmi4/rmi_driver.c | 9 ++--- drivers/input/rmi4/rmi_f11.c | 15 +++++---- drivers/input/rmi4/rmi_f12.c | 15 +++++---- drivers/input/rmi4/rmi_f54.c | 2 +- drivers/input/rmi4/rmi_spi.c | 9 ++--- drivers/iommu/arm-smmu.c | 2 +- drivers/iommu/rockchip-iommu.c | 2 +- drivers/irqchip/irq-imgpdc.c | 2 +- drivers/irqchip/irq-mvebu-gicp.c | 8 ++--- drivers/leds/leds-adp5520.c | 2 +- drivers/leds/leds-apu.c | 4 +-- drivers/leds/leds-da9052.c | 4 +-- drivers/leds/leds-lp5521.c | 4 +-- drivers/leds/leds-lp5523.c | 4 +-- drivers/leds/leds-lp5562.c | 4 +-- drivers/leds/leds-lp55xx-common.c | 2 +- drivers/leds/leds-lp8501.c | 4 +-- drivers/leds/leds-lt3593.c | 4 +-- drivers/leds/leds-mc13783.c | 4 +-- drivers/leds/leds-mlxcpld.c | 6 ++-- drivers/leds/leds-netxbig.c | 16 ++++----- drivers/leds/leds-ns2.c | 7 ++-- drivers/leds/leds-pca955x.c | 8 ++--- drivers/leds/leds-pca963x.c | 6 ++-- drivers/leds/leds-tca6507.c | 4 +-- drivers/mailbox/hi6220-mailbox.c | 8 ++--- drivers/mailbox/mailbox-sti.c | 4 +-- drivers/mailbox/omap-mailbox.c | 10 +++--- drivers/mailbox/ti-msgmgr.c | 4 +-- drivers/media/i2c/s5k5baf.c | 2 +- drivers/media/platform/am437x/am437x-vpfe.c | 6 ++-- drivers/media/platform/davinci/vpif_capture.c | 10 +++--- .../media/platform/qcom/camss-8x16/camss-csid.c | 8 +++-- .../media/platform/qcom/camss-8x16/camss-csiphy.c | 11 +++--- .../media/platform/qcom/camss-8x16/camss-ispif.c | 9 +++-- drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 8 +++-- drivers/media/platform/qcom/camss-8x16/camss.c | 3 +- drivers/media/platform/vsp1/vsp1_entity.c | 3 +- drivers/media/platform/xilinx/xilinx-vipp.c | 2 +- drivers/media/v4l2-core/v4l2-flash-led-class.c | 7 ++-- drivers/memory/of_memory.c | 4 +-- drivers/mfd/ab8500-debugfs.c | 12 +++---- drivers/mfd/htc-i2cpld.c | 4 ++- drivers/mfd/motorola-cpcap.c | 6 ++-- drivers/mfd/sprd-sc27xx-spi.c | 5 +-- drivers/mfd/twl-core.c | 5 +-- drivers/mfd/wm8994-core.c | 7 ++-- drivers/misc/sram.c | 4 +-- drivers/mmc/host/sdhci-omap.c | 6 ++-- drivers/mtd/devices/docg3.c | 2 +- drivers/mtd/nand/raw/qcom_nandc.c | 4 +-- drivers/mtd/nand/raw/s3c2410.c | 2 +- drivers/net/dsa/b53/b53_common.c | 8 ++--- drivers/net/ethernet/amazon/ena/ena_ethtool.c | 8 ++--- drivers/net/ethernet/ethoc.c | 3 +- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2 +- drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 6 ++-- drivers/net/ethernet/ni/nixge.c | 5 ++- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 4 +-- drivers/net/ethernet/ti/cpsw.c | 9 ++--- drivers/net/ethernet/ti/netcp_ethss.c | 24 ++++++------- drivers/net/phy/phy_led_triggers.c | 6 ++-- drivers/net/wireless/mediatek/mt76/mac80211.c | 2 +- drivers/pci/cadence/pcie-cadence-ep.c | 3 +- drivers/pci/dwc/pci-dra7xx.c | 4 +-- drivers/pci/dwc/pcie-designware-ep.c | 8 +++-- drivers/pci/host/pcie-rockchip-ep.c | 2 +- drivers/pinctrl/berlin/berlin.c | 10 +++--- drivers/pinctrl/freescale/pinctrl-imx.c | 10 +++--- drivers/pinctrl/freescale/pinctrl-imx1-core.c | 20 +++++------ drivers/pinctrl/freescale/pinctrl-mxs.c | 18 ++++++---- drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 21 +++++++----- drivers/pinctrl/mvebu/pinctrl-mvebu.c | 16 +++++---- drivers/pinctrl/pinctrl-at91-pio4.c | 39 +++++++++++++--------- drivers/pinctrl/pinctrl-at91.c | 34 ++++++++++++------- drivers/pinctrl/pinctrl-axp209.c | 7 ++-- drivers/pinctrl/pinctrl-digicolor.c | 5 +-- drivers/pinctrl/pinctrl-ingenic.c | 4 +-- drivers/pinctrl/pinctrl-lpc18xx.c | 5 +-- drivers/pinctrl/pinctrl-ocelot.c | 3 +- drivers/pinctrl/pinctrl-rockchip.c | 24 +++++++------ drivers/pinctrl/pinctrl-single.c | 26 ++++++++------- drivers/pinctrl/pinctrl-st.c | 31 ++++++++--------- drivers/pinctrl/pinctrl-xway.c | 4 +-- drivers/pinctrl/samsung/pinctrl-exynos.c | 5 +-- drivers/pinctrl/samsung/pinctrl-samsung.c | 17 ++++++---- drivers/pinctrl/sh-pfc/core.c | 6 ++-- drivers/pinctrl/sh-pfc/gpio.c | 7 ++-- drivers/pinctrl/sh-pfc/pinctrl.c | 8 ++--- drivers/pinctrl/spear/pinctrl-plgpio.c | 4 +-- drivers/pinctrl/sprd/pinctrl-sprd.c | 19 ++++++----- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 18 +++++----- drivers/pinctrl/tegra/pinctrl-tegra.c | 6 ++-- drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 6 ++-- drivers/pinctrl/zte/pinctrl-zx.c | 6 ++-- drivers/platform/mellanox/mlxreg-hotplug.c | 3 +- drivers/power/supply/charger-manager.c | 29 +++++++++------- drivers/power/supply/power_supply_core.c | 4 +-- drivers/pwm/pwm-lp3943.c | 2 +- drivers/regulator/act8865-regulator.c | 7 ++-- drivers/regulator/as3711-regulator.c | 6 ++-- drivers/regulator/bcm590xx-regulator.c | 6 ++-- drivers/regulator/da9063-regulator.c | 4 +-- drivers/regulator/gpio-regulator.c | 10 +++--- drivers/regulator/max1586.c | 6 ++-- drivers/regulator/max8660.c | 6 ++-- drivers/regulator/max8997-regulator.c | 5 +-- drivers/regulator/max8998.c | 5 +-- drivers/regulator/mc13xxx-regulator-core.c | 2 +- drivers/regulator/pbias-regulator.c | 5 +-- drivers/regulator/rc5t583-regulator.c | 6 ++-- drivers/regulator/s5m8767.c | 10 +++--- drivers/regulator/ti-abb-regulator.c | 4 +-- drivers/regulator/tps65090-regulator.c | 10 +++--- drivers/regulator/tps65217-regulator.c | 5 +-- drivers/regulator/tps65218-regulator.c | 5 +-- drivers/regulator/tps65910-regulator.c | 18 ++++++---- drivers/regulator/tps80031-regulator.c | 4 +-- drivers/reset/reset-ti-syscon.c | 3 +- drivers/scsi/isci/init.c | 8 ++--- drivers/scsi/ufs/ufshcd-pltfrm.c | 4 +-- drivers/scsi/ufs/ufshcd.c | 4 +-- drivers/soc/bcm/raspberrypi-power.c | 6 ++-- drivers/soc/mediatek/mtk-scpsys.c | 8 ++--- drivers/soc/ti/knav_qmss_acc.c | 6 ++-- drivers/spi/spi-davinci.c | 7 ++-- drivers/spi/spi-ep93xx.c | 4 +-- drivers/spi/spi-gpio.c | 5 +-- drivers/spi/spi-imx.c | 5 +-- drivers/spi/spi-oc-tiny.c | 4 +-- drivers/spi/spi-pl022.c | 2 +- drivers/spi/spi.c | 2 +- drivers/staging/greybus/audio_topology.c | 2 +- drivers/staging/media/imx/imx-media-dev.c | 6 ++-- drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c | 24 ++++++++----- drivers/thermal/tegra/soctherm.c | 8 ++--- drivers/thermal/thermal-generic-adc.c | 5 +-- drivers/tty/serial/rp2.c | 2 +- drivers/usb/gadget/udc/atmel_usba_udc.c | 2 +- drivers/usb/gadget/udc/renesas_usb3.c | 3 +- drivers/video/backlight/adp8860_bl.c | 2 +- drivers/video/backlight/adp8870_bl.c | 2 +- drivers/video/backlight/lp855x_bl.c | 2 +- drivers/video/fbdev/au1100fb.c | 2 +- drivers/video/fbdev/mxsfb.c | 2 +- drivers/video/fbdev/omap2/omapfb/vrfb.c | 4 +-- sound/soc/au1x/dbdma2.c | 4 +-- sound/soc/codecs/hdmi-codec.c | 2 +- sound/soc/codecs/rt5645.c | 5 +-- sound/soc/codecs/wm8994.c | 4 +-- sound/soc/davinci/davinci-mcasp.c | 14 ++++---- sound/soc/generic/audio-graph-card.c | 4 +-- sound/soc/generic/audio-graph-scu-card.c | 4 +-- sound/soc/generic/simple-card.c | 8 ++--- sound/soc/generic/simple-scu-card.c | 4 +-- sound/soc/img/img-i2s-in.c | 4 +-- sound/soc/img/img-i2s-out.c | 4 +-- sound/soc/intel/skylake/skl-topology.c | 20 ++++++----- sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 3 +- sound/soc/pxa/mmp-sspa.c | 4 +-- sound/soc/rockchip/rk3399_gru_sound.c | 2 +- sound/soc/sh/rcar/cmd.c | 2 +- sound/soc/sh/rcar/core.c | 4 +-- sound/soc/sh/rcar/ctu.c | 2 +- sound/soc/sh/rcar/dvc.c | 2 +- sound/soc/sh/rcar/mix.c | 2 +- sound/soc/sh/rcar/src.c | 2 +- sound/soc/sh/rcar/ssi.c | 2 +- sound/soc/sh/rcar/ssiu.c | 2 +- sound/soc/soc-core.c | 6 ++-- sound/soc/uniphier/aio-cpu.c | 10 +++--- 229 files changed, 847 insertions(+), 664 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 3563103590c6..fe0183d48dcd 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -298,8 +298,8 @@ static int acpi_fan_get_fps(struct acpi_device *device) } fan->fps_count = obj->package.count - 1; /* minus revision field */ - fan->fps = devm_kzalloc(&device->dev, - fan->fps_count * sizeof(struct acpi_fan_fps), + fan->fps = devm_kcalloc(&device->dev, + fan->fps_count, sizeof(struct acpi_fan_fps), GFP_KERNEL); if (!fan->fps) { dev_err(&device->dev, "Not enough memory\n"); diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index b87252bf4571..d15814e1727f 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1082,9 +1082,10 @@ static int __nfit_mem_init(struct acpi_nfit_desc *acpi_desc, continue; nfit_mem->nfit_flush = nfit_flush; flush = nfit_flush->flush; - nfit_mem->flush_wpq = devm_kzalloc(acpi_desc->dev, - flush->hint_count - * sizeof(struct resource), GFP_KERNEL); + nfit_mem->flush_wpq = devm_kcalloc(acpi_desc->dev, + flush->hint_count, + sizeof(struct resource), + GFP_KERNEL); if (!nfit_mem->flush_wpq) return -ENOMEM; for (i = 0; i < flush->hint_count; i++) { diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index cddf96f6e431..73ba8e134ca9 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4114,13 +4114,13 @@ static int mv_platform_probe(struct platform_device *pdev) if (!host || !hpriv) return -ENOMEM; - hpriv->port_clks = devm_kzalloc(&pdev->dev, - sizeof(struct clk *) * n_ports, + hpriv->port_clks = devm_kcalloc(&pdev->dev, + n_ports, sizeof(struct clk *), GFP_KERNEL); if (!hpriv->port_clks) return -ENOMEM; - hpriv->port_phys = devm_kzalloc(&pdev->dev, - sizeof(struct phy *) * n_ports, + hpriv->port_phys = devm_kcalloc(&pdev->dev, + n_ports, sizeof(struct phy *), GFP_KERNEL); if (!hpriv->port_phys) return -ENOMEM; diff --git a/drivers/bus/fsl-mc/fsl-mc-allocator.c b/drivers/bus/fsl-mc/fsl-mc-allocator.c index fb1442b08962..e906ecfe23dd 100644 --- a/drivers/bus/fsl-mc/fsl-mc-allocator.c +++ b/drivers/bus/fsl-mc/fsl-mc-allocator.c @@ -354,8 +354,8 @@ int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, if (error < 0) return error; - irq_resources = devm_kzalloc(&mc_bus_dev->dev, - sizeof(*irq_resources) * irq_count, + irq_resources = devm_kcalloc(&mc_bus_dev->dev, + irq_count, sizeof(*irq_resources), GFP_KERNEL); if (!irq_resources) { error = -ENOMEM; @@ -455,7 +455,7 @@ int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) return -ENOSPC; } - irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]), + irqs = devm_kcalloc(&mc_dev->dev, irq_count, sizeof(irqs[0]), GFP_KERNEL); if (!irqs) return -ENOMEM; diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 96c77c8e7f40..d31b09099216 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -980,7 +980,7 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) goto out; } - chip->cc_attrs_tbl = devm_kzalloc(&chip->dev, 4 * nr_commands, + chip->cc_attrs_tbl = devm_kcalloc(&chip->dev, 4, nr_commands, GFP_KERNEL); rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY); diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 9e0b2f2b48e7..7bef0666ae7e 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -734,7 +734,7 @@ static void bcm2835_pll_debug_init(struct clk_hw *hw, const struct bcm2835_pll_data *data = pll->data; struct debugfs_reg32 *regs; - regs = devm_kzalloc(cprman->dev, 7 * sizeof(*regs), GFP_KERNEL); + regs = devm_kcalloc(cprman->dev, 7, sizeof(*regs), GFP_KERNEL); if (!regs) return; @@ -865,7 +865,7 @@ static void bcm2835_pll_divider_debug_init(struct clk_hw *hw, const struct bcm2835_pll_divider_data *data = divider->data; struct debugfs_reg32 *regs; - regs = devm_kzalloc(cprman->dev, 7 * sizeof(*regs), GFP_KERNEL); + regs = devm_kcalloc(cprman->dev, 7, sizeof(*regs), GFP_KERNEL); if (!regs) return; diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index d6036c788fab..688e403333b9 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -501,8 +501,9 @@ static int ti_adpll_init_dco(struct ti_adpll_data *d) const char *postfix; int width, err; - d->outputs.clks = devm_kzalloc(d->dev, sizeof(struct clk *) * + d->outputs.clks = devm_kcalloc(d->dev, MAX_ADPLL_OUTPUTS, + sizeof(struct clk *), GFP_KERNEL); if (!d->outputs.clks) return -ENOMEM; @@ -915,8 +916,9 @@ static int ti_adpll_probe(struct platform_device *pdev) if (err) return err; - d->clocks = devm_kzalloc(d->dev, sizeof(struct ti_adpll_clock) * + d->clocks = devm_kcalloc(d->dev, TI_ADPLL_NR_CLOCKS, + sizeof(struct ti_adpll_clock), GFP_KERNEL); if (!d->clocks) return -ENOMEM; diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index b07559b9ed99..e6f9cbe5835f 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -410,7 +410,7 @@ brcm_avs_get_freq_table(struct device *dev, struct private_data *priv) if (ret) return ERR_PTR(ret); - table = devm_kzalloc(dev, (AVS_PSTATE_MAX + 1) * sizeof(*table), + table = devm_kcalloc(dev, AVS_PSTATE_MAX + 1, sizeof(*table), GFP_KERNEL); if (!table) return ERR_PTR(-ENOMEM); diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 83cf631fc9bc..70912104a199 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -377,7 +377,8 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) } /* Make imx6_soc_volt array's size same as arm opp number */ - imx6_soc_volt = devm_kzalloc(cpu_dev, sizeof(*imx6_soc_volt) * num, GFP_KERNEL); + imx6_soc_volt = devm_kcalloc(cpu_dev, num, sizeof(*imx6_soc_volt), + GFP_KERNEL); if (imx6_soc_volt == NULL) { ret = -ENOMEM; goto free_freq_table; diff --git a/drivers/crypto/marvell/cesa.c b/drivers/crypto/marvell/cesa.c index f81fa4a3e66b..a4aa6813de4b 100644 --- a/drivers/crypto/marvell/cesa.c +++ b/drivers/crypto/marvell/cesa.c @@ -471,7 +471,7 @@ static int mv_cesa_probe(struct platform_device *pdev) sram_size = CESA_SA_MIN_SRAM_SIZE; cesa->sram_size = sram_size; - cesa->engines = devm_kzalloc(dev, caps->nengines * sizeof(*engines), + cesa->engines = devm_kcalloc(dev, caps->nengines, sizeof(*engines), GFP_KERNEL); if (!cesa->engines) return -ENOMEM; diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 7cebf0a6ffbc..cf14f099ce4a 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -3393,8 +3393,10 @@ static int talitos_probe(struct platform_device *ofdev) } } - priv->chan = devm_kzalloc(dev, sizeof(struct talitos_channel) * - priv->num_channels, GFP_KERNEL); + priv->chan = devm_kcalloc(dev, + priv->num_channels, + sizeof(struct talitos_channel), + GFP_KERNEL); if (!priv->chan) { dev_err(dev, "failed to allocate channel management space\n"); err = -ENOMEM; @@ -3411,9 +3413,10 @@ static int talitos_probe(struct platform_device *ofdev) spin_lock_init(&priv->chan[i].head_lock); spin_lock_init(&priv->chan[i].tail_lock); - priv->chan[i].fifo = devm_kzalloc(dev, - sizeof(struct talitos_request) * - priv->fifo_len, GFP_KERNEL); + priv->chan[i].fifo = devm_kcalloc(dev, + priv->fifo_len, + sizeof(struct talitos_request), + GFP_KERNEL); if (!priv->chan[i].fifo) { dev_err(dev, "failed to allocate request fifo %d\n", i); err = -ENOMEM; diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index fe2af6aa88fc..0b5b3abe054e 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -628,14 +628,15 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_dev; } - devfreq->trans_table = devm_kzalloc(&devfreq->dev, - sizeof(unsigned int) * - devfreq->profile->max_state * - devfreq->profile->max_state, - GFP_KERNEL); - devfreq->time_in_state = devm_kzalloc(&devfreq->dev, - sizeof(unsigned long) * + devfreq->trans_table = + devm_kzalloc(&devfreq->dev, + array3_size(sizeof(unsigned int), + devfreq->profile->max_state, + devfreq->profile->max_state), + GFP_KERNEL); + devfreq->time_in_state = devm_kcalloc(&devfreq->dev, devfreq->profile->max_state, + sizeof(unsigned long), GFP_KERNEL); devfreq->last_stat_updated = jiffies; diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c index d96e3dc71cf8..3cd6a184fe7c 100644 --- a/drivers/devfreq/event/exynos-ppmu.c +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -518,7 +518,7 @@ static int of_get_devfreq_events(struct device_node *np, event_ops = exynos_bus_get_ops(np); count = of_get_child_count(events_np); - desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL); + desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL); if (!desc) return -ENOMEM; info->num_events = count; diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 26b67455208f..fa31cccbe04f 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -848,8 +848,8 @@ static int k3_dma_probe(struct platform_device *op) return -ENOMEM; /* init phy channel */ - d->phy = devm_kzalloc(&op->dev, - d->dma_channels * sizeof(struct k3_dma_phy), GFP_KERNEL); + d->phy = devm_kcalloc(&op->dev, + d->dma_channels, sizeof(struct k3_dma_phy), GFP_KERNEL); if (d->phy == NULL) return -ENOMEM; @@ -879,8 +879,8 @@ static int k3_dma_probe(struct platform_device *op) d->slave.copy_align = DMAENGINE_ALIGN_8_BYTES; /* init virtual channel */ - d->chans = devm_kzalloc(&op->dev, - d->dma_requests * sizeof(struct k3_dma_chan), GFP_KERNEL); + d->chans = devm_kcalloc(&op->dev, + d->dma_requests, sizeof(struct k3_dma_chan), GFP_KERNEL); if (d->chans == NULL) return -ENOMEM; diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c index 3548caa9e933..c6589ccf1b9a 100644 --- a/drivers/dma/mv_xor_v2.c +++ b/drivers/dma/mv_xor_v2.c @@ -809,8 +809,9 @@ static int mv_xor_v2_probe(struct platform_device *pdev) } /* alloc memory for the SW descriptors */ - xor_dev->sw_desq = devm_kzalloc(&pdev->dev, sizeof(*sw_desc) * - MV_XOR_V2_DESC_NUM, GFP_KERNEL); + xor_dev->sw_desq = devm_kcalloc(&pdev->dev, + MV_XOR_V2_DESC_NUM, sizeof(*sw_desc), + GFP_KERNEL); if (!xor_dev->sw_desq) { ret = -ENOMEM; goto free_hw_desq; diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index cd92d696bcf9..7056fe7513b4 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c @@ -1223,9 +1223,9 @@ static int s3c24xx_dma_probe(struct platform_device *pdev) if (IS_ERR(s3cdma->base)) return PTR_ERR(s3cdma->base); - s3cdma->phy_chans = devm_kzalloc(&pdev->dev, - sizeof(struct s3c24xx_dma_phy) * - pdata->num_phy_channels, + s3cdma->phy_chans = devm_kcalloc(&pdev->dev, + pdata->num_phy_channels, + sizeof(struct s3c24xx_dma_phy), GFP_KERNEL); if (!s3cdma->phy_chans) return -ENOMEM; diff --git a/drivers/dma/zx_dma.c b/drivers/dma/zx_dma.c index 2bb695315300..2571bc7693df 100644 --- a/drivers/dma/zx_dma.c +++ b/drivers/dma/zx_dma.c @@ -798,8 +798,8 @@ static int zx_dma_probe(struct platform_device *op) return -ENOMEM; /* init phy channel */ - d->phy = devm_kzalloc(&op->dev, - d->dma_channels * sizeof(struct zx_dma_phy), GFP_KERNEL); + d->phy = devm_kcalloc(&op->dev, + d->dma_channels, sizeof(struct zx_dma_phy), GFP_KERNEL); if (!d->phy) return -ENOMEM; @@ -834,8 +834,8 @@ static int zx_dma_probe(struct platform_device *op) d->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; /* init virtual channel */ - d->chans = devm_kzalloc(&op->dev, - d->dma_requests * sizeof(struct zx_dma_chan), GFP_KERNEL); + d->chans = devm_kcalloc(&op->dev, + d->dma_requests, sizeof(struct zx_dma_chan), GFP_KERNEL); if (!d->chans) return -ENOMEM; diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 6d7a6c0a5e07..c7d06a36b23a 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -890,7 +890,7 @@ static int scpi_alloc_xfer_list(struct device *dev, struct scpi_chan *ch) int i; struct scpi_xfer *xfers; - xfers = devm_kzalloc(dev, MAX_SCPI_XFERS * sizeof(*xfers), GFP_KERNEL); + xfers = devm_kcalloc(dev, MAX_SCPI_XFERS, sizeof(*xfers), GFP_KERNEL); if (!xfers) return -ENOMEM; diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 5229036dcfbf..a7d9a2046352 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -1862,9 +1862,9 @@ static int ti_sci_probe(struct platform_device *pdev) if (!minfo->xfer_block) return -ENOMEM; - minfo->xfer_alloc_table = devm_kzalloc(dev, - BITS_TO_LONGS(desc->max_msgs) - * sizeof(unsigned long), + minfo->xfer_alloc_table = devm_kcalloc(dev, + BITS_TO_LONGS(desc->max_msgs), + sizeof(unsigned long), GFP_KERNEL); if (!minfo->xfer_alloc_table) return -ENOMEM; diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 44c09904daa6..91b90c0cea73 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -427,7 +427,7 @@ static int adnp_irq_setup(struct adnp *adnp) * is chosen to match the register layout of the hardware in that * each segment contains the corresponding bits for all interrupts. */ - adnp->irq_enable = devm_kzalloc(chip->parent, num_regs * 6, + adnp->irq_enable = devm_kcalloc(chip->parent, num_regs, 6, GFP_KERNEL); if (!adnp->irq_enable) return -ENOMEM; diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 5e89f1c74a33..b31ae16170e7 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -897,8 +897,8 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) /* Allocate a cache of the output registers */ banks = gpio->config->nr_gpios >> 5; - gpio->dcache = devm_kzalloc(&pdev->dev, - sizeof(u32) * banks, GFP_KERNEL); + gpio->dcache = devm_kcalloc(&pdev->dev, + banks, sizeof(u32), GFP_KERNEL); if (!gpio->dcache) return -ENOMEM; diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index eb8369b21e90..00272fa7cc4f 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -601,9 +601,10 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev) GPIO_MAX_BANK_NUM); return -ENXIO; } - kona_gpio->banks = devm_kzalloc(dev, - kona_gpio->num_bank * - sizeof(*kona_gpio->banks), GFP_KERNEL); + kona_gpio->banks = devm_kcalloc(dev, + kona_gpio->num_bank, + sizeof(*kona_gpio->banks), + GFP_KERNEL); if (!kona_gpio->banks) return -ENOMEM; diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index b574ecff7761..035a454eca43 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -198,8 +198,8 @@ static int davinci_gpio_probe(struct platform_device *pdev) ngpio = ARCH_NR_GPIOS; nbank = DIV_ROUND_UP(ngpio, 32); - chips = devm_kzalloc(dev, - nbank * sizeof(struct davinci_gpio_controller), + chips = devm_kcalloc(dev, + nbank, sizeof(struct davinci_gpio_controller), GFP_KERNEL); if (!chips) return -ENOMEM; diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index 516383934945..ad6e5b518669 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -321,8 +321,8 @@ static int __init egpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ei); ei->nchips = pdata->num_chips; - ei->chip = devm_kzalloc(&pdev->dev, - sizeof(struct egpio_chip) * ei->nchips, + ei->chip = devm_kcalloc(&pdev->dev, + ei->nchips, sizeof(struct egpio_chip), GFP_KERNEL); if (!ei->chip) { ret = -ENOMEM; diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index d16e9d4a129b..1306722faa5a 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -504,16 +504,17 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, txgpio->base_msi = (c >> 8) & 0xff; } - txgpio->msix_entries = devm_kzalloc(dev, - sizeof(struct msix_entry) * ngpio, + txgpio->msix_entries = devm_kcalloc(dev, + ngpio, sizeof(struct msix_entry), GFP_KERNEL); if (!txgpio->msix_entries) { err = -ENOMEM; goto out; } - txgpio->line_entries = devm_kzalloc(dev, - sizeof(struct thunderx_line) * ngpio, + txgpio->line_entries = devm_kcalloc(dev, + ngpio, + sizeof(struct thunderx_line), GFP_KERNEL); if (!txgpio->line_entries) { err = -ENOMEM; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 7c3030b7e586..6d29777884f9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1723,8 +1723,8 @@ static int exynos_dsi_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - dsi->clks = devm_kzalloc(dev, - sizeof(*dsi->clks) * dsi->driver_data->num_clks, + dsi->clks = devm_kcalloc(dev, + dsi->driver_data->num_clks, sizeof(*dsi->clks), GFP_KERNEL); if (!dsi->clks) return -ENOMEM; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 5ce84025d1cb..6127ef25acd6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -1271,7 +1271,8 @@ static int fimc_probe(struct platform_device *pdev) /* construct formats/limits array */ num_formats = ARRAY_SIZE(fimc_formats) + ARRAY_SIZE(fimc_tiled_formats); - formats = devm_kzalloc(dev, sizeof(*formats) * num_formats, GFP_KERNEL); + formats = devm_kcalloc(dev, num_formats, sizeof(*formats), + GFP_KERNEL); if (!formats) return -ENOMEM; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index e99dd1e4ba65..35ac66730563 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -1202,8 +1202,9 @@ static int gsc_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; - formats = devm_kzalloc(dev, sizeof(*formats) * - (ARRAY_SIZE(gsc_formats)), GFP_KERNEL); + formats = devm_kcalloc(dev, + ARRAY_SIZE(gsc_formats), sizeof(*formats), + GFP_KERNEL); if (!formats) return -ENOMEM; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 09c4bc0b1859..db91932550cf 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1692,7 +1692,7 @@ static int hdmi_clk_init(struct hdmi_context *hdata) if (!count) return 0; - clks = devm_kzalloc(dev, sizeof(*clks) * count, GFP_KERNEL); + clks = devm_kcalloc(dev, count, sizeof(*clks), GFP_KERNEL); if (!clks) return -ENOMEM; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index e63dc0fb55f8..c79659ca5706 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -157,8 +157,10 @@ static struct hdmi *msm_hdmi_init(struct platform_device *pdev) hdmi->qfprom_mmio = NULL; } - hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) * - config->hpd_reg_cnt, GFP_KERNEL); + hdmi->hpd_regs = devm_kcalloc(&pdev->dev, + config->hpd_reg_cnt, + sizeof(hdmi->hpd_regs[0]), + GFP_KERNEL); if (!hdmi->hpd_regs) { ret = -ENOMEM; goto fail; @@ -178,8 +180,10 @@ static struct hdmi *msm_hdmi_init(struct platform_device *pdev) hdmi->hpd_regs[i] = reg; } - hdmi->pwr_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_regs[0]) * - config->pwr_reg_cnt, GFP_KERNEL); + hdmi->pwr_regs = devm_kcalloc(&pdev->dev, + config->pwr_reg_cnt, + sizeof(hdmi->pwr_regs[0]), + GFP_KERNEL); if (!hdmi->pwr_regs) { ret = -ENOMEM; goto fail; @@ -199,8 +203,10 @@ static struct hdmi *msm_hdmi_init(struct platform_device *pdev) hdmi->pwr_regs[i] = reg; } - hdmi->hpd_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_clks[0]) * - config->hpd_clk_cnt, GFP_KERNEL); + hdmi->hpd_clks = devm_kcalloc(&pdev->dev, + config->hpd_clk_cnt, + sizeof(hdmi->hpd_clks[0]), + GFP_KERNEL); if (!hdmi->hpd_clks) { ret = -ENOMEM; goto fail; @@ -219,8 +225,10 @@ static struct hdmi *msm_hdmi_init(struct platform_device *pdev) hdmi->hpd_clks[i] = clk; } - hdmi->pwr_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_clks[0]) * - config->pwr_clk_cnt, GFP_KERNEL); + hdmi->pwr_clks = devm_kcalloc(&pdev->dev, + config->pwr_clk_cnt, + sizeof(hdmi->pwr_clks[0]), + GFP_KERNEL); if (!hdmi->pwr_clks) { ret = -ENOMEM; goto fail; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c index 5e631392dc85..4157722d6b4d 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c @@ -21,12 +21,12 @@ static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy) struct device *dev = &phy->pdev->dev; int i, ret; - phy->regs = devm_kzalloc(dev, sizeof(phy->regs[0]) * cfg->num_regs, + phy->regs = devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]), GFP_KERNEL); if (!phy->regs) return -ENOMEM; - phy->clks = devm_kzalloc(dev, sizeof(phy->clks[0]) * cfg->num_clks, + phy->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]), GFP_KERNEL); if (!phy->clks) return -ENOMEM; diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 25363fc571bc..50af72baa5ca 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -624,7 +624,8 @@ static int sensor_hub_probe(struct hid_device *hdev, ret = -EINVAL; goto err_stop_hw; } - sd->hid_sensor_hub_client_devs = devm_kzalloc(&hdev->dev, dev_cnt * + sd->hid_sensor_hub_client_devs = devm_kcalloc(&hdev->dev, + dev_cnt, sizeof(struct mfd_cell), GFP_KERNEL); if (sd->hid_sensor_hub_client_devs == NULL) { diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c index acc2536c8094..2d28cffc1404 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c @@ -121,9 +121,9 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, } client_data->hid_dev_count = (unsigned int)*payload; if (!client_data->hid_devices) - client_data->hid_devices = devm_kzalloc( + client_data->hid_devices = devm_kcalloc( &client_data->cl_device->dev, - client_data->hid_dev_count * + client_data->hid_dev_count, sizeof(struct device_info), GFP_KERNEL); if (!client_data->hid_devices) { diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index ee7a37eb159a..c101369b51de 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1363,7 +1363,7 @@ static int wacom_led_groups_alloc_and_register_one(struct device *dev, if (!devres_open_group(dev, &wacom->led.groups[group_id], GFP_KERNEL)) return -ENOMEM; - leds = devm_kzalloc(dev, sizeof(struct wacom_led) * count, GFP_KERNEL); + leds = devm_kcalloc(dev, count, sizeof(struct wacom_led), GFP_KERNEL); if (!leds) { error = -ENOMEM; goto err; @@ -1463,7 +1463,7 @@ static int wacom_led_groups_allocate(struct wacom *wacom, int count) struct wacom_group_leds *groups; int error; - groups = devm_kzalloc(dev, sizeof(struct wacom_group_leds) * count, + groups = devm_kcalloc(dev, count, sizeof(struct wacom_group_leds), GFP_KERNEL); if (!groups) return -ENOMEM; diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 693a3d53cab5..5e449eac788a 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -894,7 +894,7 @@ static int aspeed_create_fan(struct device *dev, count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch"); if (count < 1) return -EINVAL; - fan_tach_ch = devm_kzalloc(dev, sizeof(*fan_tach_ch) * count, + fan_tach_ch = devm_kcalloc(dev, count, sizeof(*fan_tach_ch), GFP_KERNEL); if (!fan_tach_ch) return -ENOMEM; diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 5c9a52599cf6..a3974cddef07 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -441,8 +441,8 @@ static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data) dev_err(dev, "DT properties empty / missing"); return -ENODEV; } - gpios = devm_kzalloc(dev, - fan_data->num_gpios * sizeof(struct gpio_desc *), + gpios = devm_kcalloc(dev, + fan_data->num_gpios, sizeof(struct gpio_desc *), GFP_KERNEL); if (!gpios) return -ENOMEM; @@ -471,8 +471,8 @@ static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data) * Speed map is in the form * this needs splitting into pairs to create gpio_fan_speed structs */ - speed = devm_kzalloc(dev, - fan_data->num_speed * sizeof(struct gpio_fan_speed), + speed = devm_kcalloc(dev, + fan_data->num_speed, sizeof(struct gpio_fan_speed), GFP_KERNEL); if (!speed) return -ENOMEM; diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 0298745d46e4..f829dadfd5a0 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -326,9 +326,9 @@ static int populate_attr_groups(struct platform_device *pdev) of_node_put(opal); for (type = 0; type < MAX_SENSOR_TYPE; type++) { - sensor_groups[type].group.attrs = devm_kzalloc(&pdev->dev, - sizeof(struct attribute *) * - (sensor_groups[type].attr_count + 1), + sensor_groups[type].group.attrs = devm_kcalloc(&pdev->dev, + sensor_groups[type].attr_count + 1, + sizeof(struct attribute *), GFP_KERNEL); if (!sensor_groups[type].group.attrs) return -ENOMEM; @@ -409,7 +409,8 @@ static int create_device_attrs(struct platform_device *pdev) int err = 0; opal = of_find_node_by_path("/ibm,opal/sensors"); - sdata = devm_kzalloc(&pdev->dev, pdata->sensors_count * sizeof(*sdata), + sdata = devm_kcalloc(&pdev->dev, + pdata->sensors_count, sizeof(*sdata), GFP_KERNEL); if (!sdata) { err = -ENOMEM; diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 5e5b32a1ec4b..69031a0f7ed2 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -92,8 +92,8 @@ static int iio_hwmon_probe(struct platform_device *pdev) while (st->channels[st->num_channels].indio_dev) st->num_channels++; - st->attrs = devm_kzalloc(dev, - sizeof(*st->attrs) * (st->num_channels + 1), + st->attrs = devm_kcalloc(dev, + st->num_channels + 1, sizeof(*st->attrs), GFP_KERNEL); if (st->attrs == NULL) { ret = -ENOMEM; diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index b0bc77bf2cd9..a753464a1a33 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -426,12 +426,12 @@ nct6683_create_attr_group(struct device *dev, if (group == NULL) return ERR_PTR(-ENOMEM); - attrs = devm_kzalloc(dev, sizeof(*attrs) * (repeat * count + 1), + attrs = devm_kcalloc(dev, repeat * count + 1, sizeof(*attrs), GFP_KERNEL); if (attrs == NULL) return ERR_PTR(-ENOMEM); - su = devm_kzalloc(dev, sizeof(*su) * repeat * count, + su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)), GFP_KERNEL); if (su == NULL) return ERR_PTR(-ENOMEM); diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index aebce560bfaf..155d4d1d1585 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -1190,12 +1190,12 @@ nct6775_create_attr_group(struct device *dev, if (group == NULL) return ERR_PTR(-ENOMEM); - attrs = devm_kzalloc(dev, sizeof(*attrs) * (repeat * count + 1), + attrs = devm_kcalloc(dev, repeat * count + 1, sizeof(*attrs), GFP_KERNEL); if (attrs == NULL) return ERR_PTR(-ENOMEM); - su = devm_kzalloc(dev, sizeof(*su) * repeat * count, + su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)), GFP_KERNEL); if (su == NULL) return ERR_PTR(-ENOMEM); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index f7c47d7994e7..82c3754e21e3 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2176,8 +2176,8 @@ static int pmbus_init_debugfs(struct i2c_client *client, } /* Allocate the max possible entries we need. */ - entries = devm_kzalloc(data->dev, - sizeof(*entries) * (data->info->pages * 10), + entries = devm_kcalloc(data->dev, + data->info->pages * 10, sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 70cecb06f93c..ae93885fccd8 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -454,8 +454,8 @@ static int ucd9000_init_debugfs(struct i2c_client *client, */ if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 || mid->driver_data == ucd90910) { - entries = devm_kzalloc(&client->dev, - sizeof(*entries) * UCD9000_GPI_COUNT, + entries = devm_kcalloc(&client->dev, + UCD9000_GPI_COUNT, sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 70cc0d134f3c..7838af58f92d 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -180,7 +180,7 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, } num = ret; - ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32), + ctx->pwm_fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32), GFP_KERNEL); if (!ctx->pwm_fan_cooling_levels) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 9b6c55523c58..320d29df17e1 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -683,8 +683,8 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) if (drvdata->buffer_depth & 0x80000000) return -EINVAL; - drvdata->buf = devm_kzalloc(dev, - drvdata->buffer_depth * 4, GFP_KERNEL); + drvdata->buf = devm_kcalloc(dev, + drvdata->buffer_depth, 4, GFP_KERNEL); if (!drvdata->buf) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index a33a92ebe74b..6880bee195c8 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -71,21 +71,24 @@ static int of_coresight_alloc_memory(struct device *dev, struct coresight_platform_data *pdata) { /* List of output port on this component */ - pdata->outports = devm_kzalloc(dev, pdata->nr_outport * + pdata->outports = devm_kcalloc(dev, + pdata->nr_outport, sizeof(*pdata->outports), GFP_KERNEL); if (!pdata->outports) return -ENOMEM; /* Children connected to this component via @outports */ - pdata->child_names = devm_kzalloc(dev, pdata->nr_outport * + pdata->child_names = devm_kcalloc(dev, + pdata->nr_outport, sizeof(*pdata->child_names), GFP_KERNEL); if (!pdata->child_names) return -ENOMEM; /* Port number on the child this component is connected to */ - pdata->child_ports = devm_kzalloc(dev, pdata->nr_outport * + pdata->child_ports = devm_kcalloc(dev, + pdata->nr_outport, sizeof(*pdata->child_ports), GFP_KERNEL); if (!pdata->child_ports) diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 904dfec7ab96..ebbf9cdec86b 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -1691,8 +1691,8 @@ static int qup_i2c_probe(struct platform_device *pdev) qup->max_xfer_sg_len = (MX_BLOCKS << 1); blocks = (MX_DMA_BLOCKS << 1) + 1; - qup->btx.sg = devm_kzalloc(&pdev->dev, - sizeof(*qup->btx.sg) * blocks, + qup->btx.sg = devm_kcalloc(&pdev->dev, + blocks, sizeof(*qup->btx.sg), GFP_KERNEL); if (!qup->btx.sg) { ret = -ENOMEM; @@ -1700,8 +1700,8 @@ static int qup_i2c_probe(struct platform_device *pdev) } sg_init_table(qup->btx.sg, blocks); - qup->brx.sg = devm_kzalloc(&pdev->dev, - sizeof(*qup->brx.sg) * blocks, + qup->brx.sg = devm_kcalloc(&pdev->dev, + blocks, sizeof(*qup->brx.sg), GFP_KERNEL); if (!qup->brx.sg) { ret = -ENOMEM; diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 1a9973ede443..ddc4bd4ca13b 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -88,8 +88,8 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, mux->data.n_values = of_get_child_count(np); - values = devm_kzalloc(&pdev->dev, - sizeof(*mux->data.values) * mux->data.n_values, + values = devm_kcalloc(&pdev->dev, + mux->data.n_values, sizeof(*mux->data.values), GFP_KERNEL); if (!values) { dev_err(&pdev->dev, "Cannot allocate values array"); @@ -111,8 +111,9 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, return -EINVAL; } - gpios = devm_kzalloc(&pdev->dev, - sizeof(*mux->data.gpios) * mux->data.n_gpios, GFP_KERNEL); + gpios = devm_kcalloc(&pdev->dev, + mux->data.n_gpios, sizeof(*mux->data.gpios), + GFP_KERNEL); if (!gpios) { dev_err(&pdev->dev, "Cannot allocate gpios array"); return -ENOMEM; diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index c948e5a4cb04..f583f805fee9 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -124,8 +124,8 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, } mux->data.write_only = of_property_read_bool(np, "write-only"); - values = devm_kzalloc(&pdev->dev, - sizeof(*mux->data.values) * mux->data.n_values, + values = devm_kcalloc(&pdev->dev, + mux->data.n_values, sizeof(*mux->data.values), GFP_KERNEL); if (!values) { dev_err(&pdev->dev, "Cannot allocate values array"); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 71a5ee652b79..44b516863c9d 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -624,8 +624,8 @@ static int at91_adc_trigger_init(struct iio_dev *idev) struct at91_adc_state *st = iio_priv(idev); int i, ret; - st->trig = devm_kzalloc(&idev->dev, - st->trigger_number * sizeof(*st->trig), + st->trig = devm_kcalloc(&idev->dev, + st->trigger_number, sizeof(*st->trig), GFP_KERNEL); if (st->trig == NULL) { @@ -908,7 +908,8 @@ static int at91_adc_probe_dt(struct at91_adc_state *st, st->registers = &st->caps->registers; st->num_channels = st->caps->num_channels; st->trigger_number = of_get_child_count(node); - st->trigger_list = devm_kzalloc(&idev->dev, st->trigger_number * + st->trigger_list = devm_kcalloc(&idev->dev, + st->trigger_number, sizeof(struct at91_adc_trigger), GFP_KERNEL); if (!st->trigger_list) { diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 7f1848dac9bf..7fb4f525714a 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1453,8 +1453,10 @@ static int max1363_alloc_scan_masks(struct iio_dev *indio_dev) int i; masks = devm_kzalloc(&indio_dev->dev, - BITS_TO_LONGS(MAX1363_MAX_CHANNELS) * sizeof(long) * - (st->chip_info->num_modes + 1), GFP_KERNEL); + array3_size(BITS_TO_LONGS(MAX1363_MAX_CHANNELS), + sizeof(long), + st->chip_info->num_modes + 1), + GFP_KERNEL); if (!masks) return -ENOMEM; diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index dc83f8f6c3d3..e470510e76ea 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -898,9 +898,10 @@ static int twl6030_gpadc_probe(struct platform_device *pdev) gpadc = iio_priv(indio_dev); - gpadc->twl6030_cal_tbl = devm_kzalloc(dev, - sizeof(*gpadc->twl6030_cal_tbl) * - pdata->nchannels, GFP_KERNEL); + gpadc->twl6030_cal_tbl = devm_kcalloc(dev, + pdata->nchannels, + sizeof(*gpadc->twl6030_cal_tbl), + GFP_KERNEL); if (!gpadc->twl6030_cal_tbl) return -ENOMEM; diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 9234c6a09a93..095530c233e4 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -536,8 +536,9 @@ static int ad5592r_alloc_channels(struct ad5592r_state *st) st->channel_offstate[reg] = tmp; } - channels = devm_kzalloc(st->dev, - (1 + 2 * num_channels) * sizeof(*channels), GFP_KERNEL); + channels = devm_kcalloc(st->dev, + 1 + 2 * num_channels, sizeof(*channels), + GFP_KERNEL); if (!channels) return -ENOMEM; diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index 60621ccd67e4..e1f44cecdef4 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -281,9 +281,10 @@ static int mux_configure_channel(struct device *dev, struct mux *mux, if (!page) return -ENOMEM; } - child->ext_info_cache = devm_kzalloc(dev, - sizeof(*child->ext_info_cache) * - num_ext_info, GFP_KERNEL); + child->ext_info_cache = devm_kcalloc(dev, + num_ext_info, + sizeof(*child->ext_info_cache), + GFP_KERNEL); if (!child->ext_info_cache) return -ENOMEM; diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c index 997e3e97f573..e319f745771a 100644 --- a/drivers/input/keyboard/clps711x-keypad.c +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -109,8 +109,8 @@ static int clps711x_keypad_probe(struct platform_device *pdev) if (priv->row_count < 1) return -EINVAL; - priv->gpio_data = devm_kzalloc(dev, - sizeof(*priv->gpio_data) * priv->row_count, + priv->gpio_data = devm_kcalloc(dev, + priv->row_count, sizeof(*priv->gpio_data), GFP_KERNEL); if (!priv->gpio_data) return -ENOMEM; diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 41614c185918..f51ae09596ef 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -443,9 +443,9 @@ matrix_keypad_parse_dt(struct device *dev) of_property_read_u32(np, "col-scan-delay-us", &pdata->col_scan_delay_us); - gpios = devm_kzalloc(dev, - sizeof(unsigned int) * - (pdata->num_row_gpios + pdata->num_col_gpios), + gpios = devm_kcalloc(dev, + pdata->num_row_gpios + pdata->num_col_gpios, + sizeof(unsigned int), GFP_KERNEL); if (!gpios) { dev_err(dev, "could not allocate memory for gpios\n"); diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 316414465c77..1fe1aa2adf85 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -281,7 +281,7 @@ samsung_keypad_parse_dt(struct device *dev) key_count = of_get_child_count(np); keymap_data->keymap_size = key_count; - keymap = devm_kzalloc(dev, sizeof(uint32_t) * key_count, GFP_KERNEL); + keymap = devm_kcalloc(dev, key_count, sizeof(uint32_t), GFP_KERNEL); if (!keymap) { dev_err(dev, "could not allocate memory for keymap\n"); return ERR_PTR(-ENOMEM); diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c index 8ccefc15c7a4..8b3a5758451e 100644 --- a/drivers/input/matrix-keymap.c +++ b/drivers/input/matrix-keymap.c @@ -170,8 +170,8 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, return -EINVAL; if (!keymap) { - keymap = devm_kzalloc(input_dev->dev.parent, - max_keys * sizeof(*keymap), + keymap = devm_kcalloc(input_dev->dev.parent, + max_keys, sizeof(*keymap), GFP_KERNEL); if (!keymap) { dev_err(input_dev->dev.parent, diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 1588aecafff7..6d304381fc30 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -283,8 +283,8 @@ static int rotary_encoder_probe(struct platform_device *pdev) } encoder->irq = - devm_kzalloc(dev, - sizeof(*encoder->irq) * encoder->gpios->ndescs, + devm_kcalloc(dev, + encoder->gpios->ndescs, sizeof(*encoder->irq), GFP_KERNEL); if (!encoder->irq) return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index f5954981e9ee..7d29053dfb0f 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -636,9 +636,10 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, rdesc->num_registers = bitmap_weight(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS); - rdesc->registers = devm_kzalloc(&d->dev, rdesc->num_registers * - sizeof(struct rmi_register_desc_item), - GFP_KERNEL); + rdesc->registers = devm_kcalloc(&d->dev, + rdesc->num_registers, + sizeof(struct rmi_register_desc_item), + GFP_KERNEL); if (!rdesc->registers) return -ENOMEM; @@ -1061,7 +1062,7 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) data->num_of_irq_regs = (data->irq_count + 7) / 8; size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); - data->irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); + data->irq_memory = devm_kcalloc(dev, size, 4, GFP_KERNEL); if (!data->irq_memory) { dev_err(dev, "Failed to allocate memory for irq masks.\n"); return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index bc5e37f30ac1..12a233251793 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -1190,14 +1190,15 @@ static int rmi_f11_initialize(struct rmi_function *fn) f11->sensor.attn_size += f11->sensor.nbr_fingers * 2; /* allocate the in-kernel tracking buffers */ - sensor->tracking_pos = devm_kzalloc(&fn->dev, - sizeof(struct input_mt_pos) * sensor->nbr_fingers, + sensor->tracking_pos = devm_kcalloc(&fn->dev, + sensor->nbr_fingers, sizeof(struct input_mt_pos), + GFP_KERNEL); + sensor->tracking_slots = devm_kcalloc(&fn->dev, + sensor->nbr_fingers, sizeof(int), GFP_KERNEL); + sensor->objs = devm_kcalloc(&fn->dev, + sensor->nbr_fingers, + sizeof(struct rmi_2d_sensor_abs_object), GFP_KERNEL); - sensor->tracking_slots = devm_kzalloc(&fn->dev, - sizeof(int) * sensor->nbr_fingers, GFP_KERNEL); - sensor->objs = devm_kzalloc(&fn->dev, - sizeof(struct rmi_2d_sensor_abs_object) - * sensor->nbr_fingers, GFP_KERNEL); if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs) return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 8b0db086d68a..a3d1aa88f2a9 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -502,14 +502,15 @@ static int rmi_f12_probe(struct rmi_function *fn) } /* allocate the in-kernel tracking buffers */ - sensor->tracking_pos = devm_kzalloc(&fn->dev, - sizeof(struct input_mt_pos) * sensor->nbr_fingers, + sensor->tracking_pos = devm_kcalloc(&fn->dev, + sensor->nbr_fingers, sizeof(struct input_mt_pos), + GFP_KERNEL); + sensor->tracking_slots = devm_kcalloc(&fn->dev, + sensor->nbr_fingers, sizeof(int), GFP_KERNEL); + sensor->objs = devm_kcalloc(&fn->dev, + sensor->nbr_fingers, + sizeof(struct rmi_2d_sensor_abs_object), GFP_KERNEL); - sensor->tracking_slots = devm_kzalloc(&fn->dev, - sizeof(int) * sensor->nbr_fingers, GFP_KERNEL); - sensor->objs = devm_kzalloc(&fn->dev, - sizeof(struct rmi_2d_sensor_abs_object) - * sensor->nbr_fingers, GFP_KERNEL); if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs) return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 5343f2c08f15..e8a59d164019 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -685,7 +685,7 @@ static int rmi_f54_probe(struct rmi_function *fn) rx = f54->num_rx_electrodes; tx = f54->num_tx_electrodes; f54->report_data = devm_kzalloc(&fn->dev, - sizeof(u16) * tx * rx, + array3_size(tx, rx, sizeof(u16)), GFP_KERNEL); if (f54->report_data == NULL) return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c index 082defc329a8..33b8c6e7ac0a 100644 --- a/drivers/input/rmi4/rmi_spi.c +++ b/drivers/input/rmi4/rmi_spi.c @@ -69,7 +69,7 @@ static int rmi_spi_manage_pools(struct rmi_spi_xport *rmi_spi, int len) buf_size = RMI_SPI_XFER_SIZE_LIMIT; tmp = rmi_spi->rx_buf; - buf = devm_kzalloc(&spi->dev, buf_size * 2, + buf = devm_kcalloc(&spi->dev, buf_size, 2, GFP_KERNEL | GFP_DMA); if (!buf) return -ENOMEM; @@ -96,9 +96,10 @@ static int rmi_spi_manage_pools(struct rmi_spi_xport *rmi_spi, int len) * per byte delays. */ tmp = rmi_spi->rx_xfers; - xfer_buf = devm_kzalloc(&spi->dev, - (rmi_spi->rx_xfer_count + rmi_spi->tx_xfer_count) - * sizeof(struct spi_transfer), GFP_KERNEL); + xfer_buf = devm_kcalloc(&spi->dev, + rmi_spi->rx_xfer_count + rmi_spi->tx_xfer_count, + sizeof(struct spi_transfer), + GFP_KERNEL); if (!xfer_buf) return -ENOMEM; diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 69e7c60792a8..f7a96bcf94a6 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -2082,7 +2082,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev) return -ENODEV; } - smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs, + smmu->irqs = devm_kcalloc(dev, num_irqs, sizeof(*smmu->irqs), GFP_KERNEL); if (!smmu->irqs) { dev_err(dev, "failed to allocate %d irqs\n", num_irqs); diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 0468acfa131f..054cd2c8e9c8 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -1135,7 +1135,7 @@ static int rk_iommu_probe(struct platform_device *pdev) iommu->dev = dev; iommu->num_mmu = 0; - iommu->bases = devm_kzalloc(dev, sizeof(*iommu->bases) * num_res, + iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases), GFP_KERNEL); if (!iommu->bases) return -ENOMEM; diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c index e80263e16c4c..d00489a4b54f 100644 --- a/drivers/irqchip/irq-imgpdc.c +++ b/drivers/irqchip/irq-imgpdc.c @@ -354,7 +354,7 @@ static int pdc_intc_probe(struct platform_device *pdev) priv->nr_syswakes = val; /* Get peripheral IRQ numbers */ - priv->perip_irqs = devm_kzalloc(&pdev->dev, 4 * priv->nr_perips, + priv->perip_irqs = devm_kcalloc(&pdev->dev, 4, priv->nr_perips, GFP_KERNEL); if (!priv->perip_irqs) { dev_err(&pdev->dev, "cannot allocate perip IRQ list\n"); diff --git a/drivers/irqchip/irq-mvebu-gicp.c b/drivers/irqchip/irq-mvebu-gicp.c index 4e17f7081efc..3be5c5dba1da 100644 --- a/drivers/irqchip/irq-mvebu-gicp.c +++ b/drivers/irqchip/irq-mvebu-gicp.c @@ -191,8 +191,8 @@ static int mvebu_gicp_probe(struct platform_device *pdev) gicp->spi_ranges_cnt = ret / 2; gicp->spi_ranges = - devm_kzalloc(&pdev->dev, - gicp->spi_ranges_cnt * + devm_kcalloc(&pdev->dev, + gicp->spi_ranges_cnt, sizeof(struct mvebu_gicp_spi_range), GFP_KERNEL); if (!gicp->spi_ranges) @@ -210,8 +210,8 @@ static int mvebu_gicp_probe(struct platform_device *pdev) gicp->spi_cnt += gicp->spi_ranges[i].count; } - gicp->spi_bitmap = devm_kzalloc(&pdev->dev, - BITS_TO_LONGS(gicp->spi_cnt) * sizeof(long), + gicp->spi_bitmap = devm_kcalloc(&pdev->dev, + BITS_TO_LONGS(gicp->spi_cnt), sizeof(long), GFP_KERNEL); if (!gicp->spi_bitmap) return -ENOMEM; diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c index 853b2d3bdb17..7ecf080f73ad 100644 --- a/drivers/leds/leds-adp5520.c +++ b/drivers/leds/leds-adp5520.c @@ -108,7 +108,7 @@ static int adp5520_led_probe(struct platform_device *pdev) return -EFAULT; } - led = devm_kzalloc(&pdev->dev, sizeof(*led) * pdata->num_leds, + led = devm_kcalloc(&pdev->dev, pdata->num_leds, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; diff --git a/drivers/leds/leds-apu.c b/drivers/leds/leds-apu.c index 90eeedcbf371..8c93d68964c7 100644 --- a/drivers/leds/leds-apu.c +++ b/drivers/leds/leds-apu.c @@ -171,8 +171,8 @@ static int apu_led_config(struct device *dev, struct apu_led_pdata *apuld) int i; int err; - apu_led->pled = devm_kzalloc(dev, - sizeof(struct apu_led_priv) * apu_led->num_led_instances, + apu_led->pled = devm_kcalloc(dev, + apu_led->num_led_instances, sizeof(struct apu_led_priv), GFP_KERNEL); if (!apu_led->pled) diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c index f8c7d82c2652..31d4c94e6fd8 100644 --- a/drivers/leds/leds-da9052.c +++ b/drivers/leds/leds-da9052.c @@ -113,8 +113,8 @@ static int da9052_led_probe(struct platform_device *pdev) goto err; } - led = devm_kzalloc(&pdev->dev, - sizeof(struct da9052_led) * pled->num_leds, + led = devm_kcalloc(&pdev->dev, + pled->num_leds, sizeof(struct da9052_led), GFP_KERNEL); if (!led) { error = -ENOMEM; diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 55c0517fbe03..99689b51a73d 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -533,8 +533,8 @@ static int lp5521_probe(struct i2c_client *client, if (!chip) return -ENOMEM; - led = devm_kzalloc(&client->dev, - sizeof(*led) * pdata->num_channels, GFP_KERNEL); + led = devm_kcalloc(&client->dev, + pdata->num_channels, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 52b6f529e278..a2e74feee2b2 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -898,8 +898,8 @@ static int lp5523_probe(struct i2c_client *client, if (!chip) return -ENOMEM; - led = devm_kzalloc(&client->dev, - sizeof(*led) * pdata->num_channels, GFP_KERNEL); + led = devm_kcalloc(&client->dev, + pdata->num_channels, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 05ffa34fb6ad..2a9009fe5545 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -534,8 +534,8 @@ static int lp5562_probe(struct i2c_client *client, if (!chip) return -ENOMEM; - led = devm_kzalloc(&client->dev, - sizeof(*led) * pdata->num_channels, GFP_KERNEL); + led = devm_kcalloc(&client->dev, + pdata->num_channels, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 5377f22ff994..3d79a6380761 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -560,7 +560,7 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev, return ERR_PTR(-EINVAL); } - cfg = devm_kzalloc(dev, sizeof(*cfg) * num_channels, GFP_KERNEL); + cfg = devm_kcalloc(dev, num_channels, sizeof(*cfg), GFP_KERNEL); if (!cfg) return ERR_PTR(-ENOMEM); diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index 3adb113cf02e..4c800b5989a9 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -327,8 +327,8 @@ static int lp8501_probe(struct i2c_client *client, if (!chip) return -ENOMEM; - led = devm_kzalloc(&client->dev, - sizeof(*led) * pdata->num_channels, GFP_KERNEL); + led = devm_kcalloc(&client->dev, + pdata->num_channels, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index a7ff510cbdd0..5ec730a31b65 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -128,8 +128,8 @@ static int lt3593_led_probe(struct platform_device *pdev) if (!pdata) return -EBUSY; - leds_data = devm_kzalloc(&pdev->dev, - sizeof(struct lt3593_led_data) * pdata->num_leds, + leds_data = devm_kcalloc(&pdev->dev, + pdata->num_leds, sizeof(struct lt3593_led_data), GFP_KERNEL); if (!leds_data) return -ENOMEM; diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 2421cf104991..47ad7de9553c 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -136,7 +136,7 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( pdata->num_leds = of_get_child_count(parent); - pdata->led = devm_kzalloc(dev, pdata->num_leds * sizeof(*pdata->led), + pdata->led = devm_kcalloc(dev, pdata->num_leds, sizeof(*pdata->led), GFP_KERNEL); if (!pdata->led) { ret = -ENOMEM; @@ -210,7 +210,7 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) return -EINVAL; } - leds->led = devm_kzalloc(dev, leds->num_leds * sizeof(*leds->led), + leds->led = devm_kcalloc(dev, leds->num_leds, sizeof(*leds->led), GFP_KERNEL); if (!leds->led) return -ENOMEM; diff --git a/drivers/leds/leds-mlxcpld.c b/drivers/leds/leds-mlxcpld.c index 281482e1d50f..f4721f8065f0 100644 --- a/drivers/leds/leds-mlxcpld.c +++ b/drivers/leds/leds-mlxcpld.c @@ -329,8 +329,10 @@ static int mlxcpld_led_config(struct device *dev, int i; int err; - cpld->pled = devm_kzalloc(dev, sizeof(struct mlxcpld_led_priv) * - cpld->num_led_instances, GFP_KERNEL); + cpld->pled = devm_kcalloc(dev, + cpld->num_led_instances, + sizeof(struct mlxcpld_led_priv), + GFP_KERNEL); if (!cpld->pled) return -ENOMEM; diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index f48b1aed9b4e..62fa0de526ee 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -335,7 +335,7 @@ static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np, return ret; } num_addr = ret; - addr = devm_kzalloc(dev, num_addr * sizeof(*addr), GFP_KERNEL); + addr = devm_kcalloc(dev, num_addr, sizeof(*addr), GFP_KERNEL); if (!addr) return -ENOMEM; @@ -355,7 +355,7 @@ static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np, return ret; } num_data = ret; - data = devm_kzalloc(dev, num_data * sizeof(*data), GFP_KERNEL); + data = devm_kcalloc(dev, num_data, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -415,7 +415,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev, if (ret % 3) return -EINVAL; num_timers = ret / 3; - timers = devm_kzalloc(dev, num_timers * sizeof(*timers), + timers = devm_kcalloc(dev, num_timers, sizeof(*timers), GFP_KERNEL); if (!timers) return -ENOMEM; @@ -444,7 +444,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev, return -ENODEV; } - leds = devm_kzalloc(dev, num_leds * sizeof(*leds), GFP_KERNEL); + leds = devm_kcalloc(dev, num_leds, sizeof(*leds), GFP_KERNEL); if (!leds) return -ENOMEM; @@ -470,8 +470,8 @@ static int netxbig_leds_get_of_pdata(struct device *dev, goto err_node_put; mode_val = - devm_kzalloc(dev, - NETXBIG_LED_MODE_NUM * sizeof(*mode_val), + devm_kcalloc(dev, + NETXBIG_LED_MODE_NUM, sizeof(*mode_val), GFP_KERNEL); if (!mode_val) { ret = -ENOMEM; @@ -560,8 +560,8 @@ static int netxbig_led_probe(struct platform_device *pdev) return ret; } - leds_data = devm_kzalloc(&pdev->dev, - pdata->num_leds * sizeof(*leds_data), + leds_data = devm_kcalloc(&pdev->dev, + pdata->num_leds, sizeof(*leds_data), GFP_KERNEL); if (!leds_data) return -ENOMEM; diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index 506b75b190e7..14fe5cd43232 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -264,7 +264,7 @@ ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) if (!num_leds) return -ENODEV; - leds = devm_kzalloc(dev, num_leds * sizeof(struct ns2_led), + leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led), GFP_KERNEL); if (!leds) return -ENOMEM; @@ -298,8 +298,9 @@ ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) } num_modes = ret / 3; - modval = devm_kzalloc(dev, - num_modes * sizeof(struct ns2_led_modval), + modval = devm_kcalloc(dev, + num_modes, + sizeof(struct ns2_led_modval), GFP_KERNEL); if (!modval) return -ENOMEM; diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 78183f90820e..f51b356d4426 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -390,8 +390,8 @@ pca955x_pdata_of_init(struct i2c_client *client, struct pca955x_chipdef *chip) if (!pdata) return ERR_PTR(-ENOMEM); - pdata->leds = devm_kzalloc(&client->dev, - sizeof(struct pca955x_led) * chip->bits, + pdata->leds = devm_kcalloc(&client->dev, + chip->bits, sizeof(struct pca955x_led), GFP_KERNEL); if (!pdata->leds) return ERR_PTR(-ENOMEM); @@ -494,8 +494,8 @@ static int pca955x_probe(struct i2c_client *client, if (!pca955x) return -ENOMEM; - pca955x->leds = devm_kzalloc(&client->dev, - sizeof(*pca955x_led) * chip->bits, GFP_KERNEL); + pca955x->leds = devm_kcalloc(&client->dev, + chip->bits, sizeof(*pca955x_led), GFP_KERNEL); if (!pca955x->leds) return -ENOMEM; diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 3bf9a1271819..5c0908113e38 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -300,8 +300,8 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) if (!count || count > chip->n_leds) return ERR_PTR(-ENODEV); - pca963x_leds = devm_kzalloc(&client->dev, - sizeof(struct led_info) * chip->n_leds, GFP_KERNEL); + pca963x_leds = devm_kcalloc(&client->dev, + chip->n_leds, sizeof(struct led_info), GFP_KERNEL); if (!pca963x_leds) return ERR_PTR(-ENOMEM); @@ -407,7 +407,7 @@ static int pca963x_probe(struct i2c_client *client, GFP_KERNEL); if (!pca963x_chip) return -ENOMEM; - pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x), + pca963x = devm_kcalloc(&client->dev, chip->n_leds, sizeof(*pca963x), GFP_KERNEL); if (!pca963x) return -ENOMEM; diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index c12c16fb1b9c..8f343afa4787 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -697,8 +697,8 @@ tca6507_led_dt_init(struct i2c_client *client) if (!count || count > NUM_LEDS) return ERR_PTR(-ENODEV); - tca_leds = devm_kzalloc(&client->dev, - sizeof(struct led_info) * NUM_LEDS, GFP_KERNEL); + tca_leds = devm_kcalloc(&client->dev, + NUM_LEDS, sizeof(struct led_info), GFP_KERNEL); if (!tca_leds) return ERR_PTR(-ENOMEM); diff --git a/drivers/mailbox/hi6220-mailbox.c b/drivers/mailbox/hi6220-mailbox.c index 519376d3534c..4fa9803cd204 100644 --- a/drivers/mailbox/hi6220-mailbox.c +++ b/drivers/mailbox/hi6220-mailbox.c @@ -282,13 +282,13 @@ static int hi6220_mbox_probe(struct platform_device *pdev) mbox->dev = dev; mbox->chan_num = MBOX_CHAN_MAX; - mbox->mchan = devm_kzalloc(dev, - mbox->chan_num * sizeof(*mbox->mchan), GFP_KERNEL); + mbox->mchan = devm_kcalloc(dev, + mbox->chan_num, sizeof(*mbox->mchan), GFP_KERNEL); if (!mbox->mchan) return -ENOMEM; - mbox->chan = devm_kzalloc(dev, - mbox->chan_num * sizeof(*mbox->chan), GFP_KERNEL); + mbox->chan = devm_kcalloc(dev, + mbox->chan_num, sizeof(*mbox->chan), GFP_KERNEL); if (!mbox->chan) return -ENOMEM; diff --git a/drivers/mailbox/mailbox-sti.c b/drivers/mailbox/mailbox-sti.c index 41bcd339b68a..779d41262ef0 100644 --- a/drivers/mailbox/mailbox-sti.c +++ b/drivers/mailbox/mailbox-sti.c @@ -442,8 +442,8 @@ static int sti_mbox_probe(struct platform_device *pdev) if (!mbox) return -ENOMEM; - chans = devm_kzalloc(&pdev->dev, - sizeof(*chans) * STI_MBOX_CHAN_MAX, GFP_KERNEL); + chans = devm_kcalloc(&pdev->dev, + STI_MBOX_CHAN_MAX, sizeof(*chans), GFP_KERNEL); if (!chans) return -ENOMEM; diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index 2517038a8452..e1e2c085e68e 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -729,7 +729,7 @@ static int omap_mbox_probe(struct platform_device *pdev) return -ENODEV; } - finfoblk = devm_kzalloc(&pdev->dev, info_count * sizeof(*finfoblk), + finfoblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*finfoblk), GFP_KERNEL); if (!finfoblk) return -ENOMEM; @@ -773,23 +773,23 @@ static int omap_mbox_probe(struct platform_device *pdev) if (IS_ERR(mdev->mbox_base)) return PTR_ERR(mdev->mbox_base); - mdev->irq_ctx = devm_kzalloc(&pdev->dev, num_users * sizeof(u32), + mdev->irq_ctx = devm_kcalloc(&pdev->dev, num_users, sizeof(u32), GFP_KERNEL); if (!mdev->irq_ctx) return -ENOMEM; /* allocate one extra for marking end of list */ - list = devm_kzalloc(&pdev->dev, (info_count + 1) * sizeof(*list), + list = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*list), GFP_KERNEL); if (!list) return -ENOMEM; - chnls = devm_kzalloc(&pdev->dev, (info_count + 1) * sizeof(*chnls), + chnls = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*chnls), GFP_KERNEL); if (!chnls) return -ENOMEM; - mboxblk = devm_kzalloc(&pdev->dev, info_count * sizeof(*mbox), + mboxblk = devm_kcalloc(&pdev->dev, info_count, sizeof(*mbox), GFP_KERNEL); if (!mboxblk) return -ENOMEM; diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c index 78753a87ba4d..5d04738c3c8a 100644 --- a/drivers/mailbox/ti-msgmgr.c +++ b/drivers/mailbox/ti-msgmgr.c @@ -568,12 +568,12 @@ static int ti_msgmgr_probe(struct platform_device *pdev) } inst->num_valid_queues = queue_count; - qinst = devm_kzalloc(dev, sizeof(*qinst) * queue_count, GFP_KERNEL); + qinst = devm_kcalloc(dev, queue_count, sizeof(*qinst), GFP_KERNEL); if (!qinst) return -ENOMEM; inst->qinsts = qinst; - chans = devm_kzalloc(dev, sizeof(*chans) * queue_count, GFP_KERNEL); + chans = devm_kcalloc(dev, queue_count, sizeof(*chans), GFP_KERNEL); if (!chans) return -ENOMEM; inst->chans = chans; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index ff46d2c96cea..5007c9659342 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -373,7 +373,7 @@ static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, data += S5K5BAG_FW_TAG_LEN; count -= S5K5BAG_FW_TAG_LEN; - d = devm_kzalloc(dev, count * sizeof(u16), GFP_KERNEL); + d = devm_kcalloc(dev, count, sizeof(u16), GFP_KERNEL); if (!d) return -ENOMEM; diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 58ebc2220d0e..b05738a95e55 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2586,8 +2586,10 @@ static int vpfe_probe(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); - vpfe->sd = devm_kzalloc(&pdev->dev, sizeof(struct v4l2_subdev *) * - ARRAY_SIZE(vpfe->cfg->asd), GFP_KERNEL); + vpfe->sd = devm_kcalloc(&pdev->dev, + ARRAY_SIZE(vpfe->cfg->asd), + sizeof(struct v4l2_subdev *), + GFP_KERNEL); if (!vpfe->sd) { ret = -ENOMEM; goto probe_out_v4l2_unregister; diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 9364cdf62f54..a96f53ce8088 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1528,8 +1528,10 @@ vpif_capture_get_pdata(struct platform_device *pdev) if (!pdata) return NULL; pdata->subdev_info = - devm_kzalloc(&pdev->dev, sizeof(*pdata->subdev_info) * - VPIF_CAPTURE_NUM_CHANNELS, GFP_KERNEL); + devm_kcalloc(&pdev->dev, + VPIF_CAPTURE_NUM_CHANNELS, + sizeof(*pdata->subdev_info), + GFP_KERNEL); if (!pdata->subdev_info) return NULL; @@ -1546,9 +1548,9 @@ vpif_capture_get_pdata(struct platform_device *pdev) sdinfo = &pdata->subdev_info[i]; chan = &pdata->chan_config[i]; - chan->inputs = devm_kzalloc(&pdev->dev, - sizeof(*chan->inputs) * + chan->inputs = devm_kcalloc(&pdev->dev, VPIF_CAPTURE_NUM_CHANNELS, + sizeof(*chan->inputs), GFP_KERNEL); if (!chan->inputs) return NULL; diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss-8x16/camss-csid.c index 64df82817de3..226f36ef7419 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-csid.c +++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.c @@ -845,7 +845,7 @@ int msm_csid_subdev_init(struct csid_device *csid, while (res->clock[csid->nclocks]) csid->nclocks++; - csid->clock = devm_kzalloc(dev, csid->nclocks * sizeof(*csid->clock), + csid->clock = devm_kcalloc(dev, csid->nclocks, sizeof(*csid->clock), GFP_KERNEL); if (!csid->clock) return -ENOMEM; @@ -868,8 +868,10 @@ int msm_csid_subdev_init(struct csid_device *csid, continue; } - clock->freq = devm_kzalloc(dev, clock->nfreqs * - sizeof(*clock->freq), GFP_KERNEL); + clock->freq = devm_kcalloc(dev, + clock->nfreqs, + sizeof(*clock->freq), + GFP_KERNEL); if (!clock->freq) return -ENOMEM; diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c index 072c6cf053f6..7e61caba6a2d 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c @@ -732,8 +732,9 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy, while (res->clock[csiphy->nclocks]) csiphy->nclocks++; - csiphy->clock = devm_kzalloc(dev, csiphy->nclocks * - sizeof(*csiphy->clock), GFP_KERNEL); + csiphy->clock = devm_kcalloc(dev, + csiphy->nclocks, sizeof(*csiphy->clock), + GFP_KERNEL); if (!csiphy->clock) return -ENOMEM; @@ -755,8 +756,10 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy, continue; } - clock->freq = devm_kzalloc(dev, clock->nfreqs * - sizeof(*clock->freq), GFP_KERNEL); + clock->freq = devm_kcalloc(dev, + clock->nfreqs, + sizeof(*clock->freq), + GFP_KERNEL); if (!clock->freq) return -ENOMEM; diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c index 24da529397b5..9d1af9353c1d 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c +++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c @@ -948,7 +948,8 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, while (res->clock[ispif->nclocks]) ispif->nclocks++; - ispif->clock = devm_kzalloc(dev, ispif->nclocks * sizeof(*ispif->clock), + ispif->clock = devm_kcalloc(dev, + ispif->nclocks, sizeof(*ispif->clock), GFP_KERNEL); if (!ispif->clock) return -ENOMEM; @@ -968,8 +969,10 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, while (res->clock_for_reset[ispif->nclocks_for_reset]) ispif->nclocks_for_reset++; - ispif->clock_for_reset = devm_kzalloc(dev, ispif->nclocks_for_reset * - sizeof(*ispif->clock_for_reset), GFP_KERNEL); + ispif->clock_for_reset = devm_kcalloc(dev, + ispif->nclocks_for_reset, + sizeof(*ispif->clock_for_reset), + GFP_KERNEL); if (!ispif->clock_for_reset) return -ENOMEM; diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c index 55232a912950..a6329a8a7c4a 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c @@ -2794,7 +2794,7 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) while (res->clock[vfe->nclocks]) vfe->nclocks++; - vfe->clock = devm_kzalloc(dev, vfe->nclocks * sizeof(*vfe->clock), + vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock), GFP_KERNEL); if (!vfe->clock) return -ENOMEM; @@ -2817,8 +2817,10 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) continue; } - clock->freq = devm_kzalloc(dev, clock->nfreqs * - sizeof(*clock->freq), GFP_KERNEL); + clock->freq = devm_kcalloc(dev, + clock->nfreqs, + sizeof(*clock->freq), + GFP_KERNEL); if (!clock->freq) return -ENOMEM; diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss-8x16/camss.c index 05f06c98aa64..23fda6207a23 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss.c +++ b/drivers/media/platform/qcom/camss-8x16/camss.c @@ -271,7 +271,8 @@ static int camss_of_parse_endpoint_node(struct device *dev, lncfg->clk.pol = mipi_csi2->lane_polarities[0]; lncfg->num_data = mipi_csi2->num_data_lanes; - lncfg->data = devm_kzalloc(dev, lncfg->num_data * sizeof(*lncfg->data), + lncfg->data = devm_kcalloc(dev, + lncfg->num_data, sizeof(*lncfg->data), GFP_KERNEL); if (!lncfg->data) return -ENOMEM; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index da276a85aa95..36a29e13109e 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -630,7 +630,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, entity->source_pad = num_pads - 1; /* Allocate and initialize pads. */ - entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads), + entity->pads = devm_kcalloc(vsp1->dev, + num_pads, sizeof(*entity->pads), GFP_KERNEL); if (entity->pads == NULL) return -ENOMEM; diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index 6bb28cd49dae..6d95ec1e9a6b 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -532,7 +532,7 @@ static int xvip_graph_init(struct xvip_composite_device *xdev) /* Register the subdevices notifier. */ num_subdevs = xdev->num_subdevs; - subdevs = devm_kzalloc(xdev->dev, sizeof(*subdevs) * num_subdevs, + subdevs = devm_kcalloc(xdev->dev, num_subdevs, sizeof(*subdevs), GFP_KERNEL); if (subdevs == NULL) { ret = -ENOMEM; diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c index 4ceef217de83..215b4804ada2 100644 --- a/drivers/media/v4l2-core/v4l2-flash-led-class.c +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c @@ -412,9 +412,10 @@ static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash, struct v4l2_ctrl_config *ctrl_cfg; int i, ret, num_ctrls = 0; - v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev, - sizeof(*v4l2_flash->ctrls) * - (STROBE_SOURCE + 1), GFP_KERNEL); + v4l2_flash->ctrls = devm_kcalloc(v4l2_flash->sd.dev, + STROBE_SOURCE + 1, + sizeof(*v4l2_flash->ctrls), + GFP_KERNEL); if (!v4l2_flash->ctrls) return -ENOMEM; diff --git a/drivers/memory/of_memory.c b/drivers/memory/of_memory.c index 568f05ed961a..2f5ed7366eec 100644 --- a/drivers/memory/of_memory.c +++ b/drivers/memory/of_memory.c @@ -126,8 +126,8 @@ const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr, arr_sz++; if (arr_sz) - timings = devm_kzalloc(dev, sizeof(*timings) * arr_sz, - GFP_KERNEL); + timings = devm_kcalloc(dev, arr_sz, sizeof(*timings), + GFP_KERNEL); if (!timings) goto default_timings; diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 831a1ceb2ed2..8d652b2f9d14 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -2659,18 +2659,18 @@ static int ab8500_debug_probe(struct platform_device *plf) ab8500 = dev_get_drvdata(plf->dev.parent); num_irqs = ab8500->mask_size; - irq_count = devm_kzalloc(&plf->dev, - sizeof(*irq_count)*num_irqs, GFP_KERNEL); + irq_count = devm_kcalloc(&plf->dev, + num_irqs, sizeof(*irq_count), GFP_KERNEL); if (!irq_count) return -ENOMEM; - dev_attr = devm_kzalloc(&plf->dev, - sizeof(*dev_attr)*num_irqs, GFP_KERNEL); + dev_attr = devm_kcalloc(&plf->dev, + num_irqs, sizeof(*dev_attr), GFP_KERNEL); if (!dev_attr) return -ENOMEM; - event_name = devm_kzalloc(&plf->dev, - sizeof(*event_name)*num_irqs, GFP_KERNEL); + event_name = devm_kcalloc(&plf->dev, + num_irqs, sizeof(*event_name), GFP_KERNEL); if (!event_name) return -ENOMEM; diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c index 4bf8b7781c77..01572b5e79e8 100644 --- a/drivers/mfd/htc-i2cpld.c +++ b/drivers/mfd/htc-i2cpld.c @@ -477,7 +477,9 @@ static int htcpld_setup_chips(struct platform_device *pdev) /* Setup each chip's output GPIOs */ htcpld->nchips = pdata->num_chip; - htcpld->chip = devm_kzalloc(dev, sizeof(struct htcpld_chip) * htcpld->nchips, + htcpld->chip = devm_kcalloc(dev, + htcpld->nchips, + sizeof(struct htcpld_chip), GFP_KERNEL); if (!htcpld->chip) return -ENOMEM; diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c index d2cc1eabac05..5276911caaec 100644 --- a/drivers/mfd/motorola-cpcap.c +++ b/drivers/mfd/motorola-cpcap.c @@ -173,9 +173,9 @@ static int cpcap_init_irq(struct cpcap_ddata *cpcap) int ret; cpcap->irqs = devm_kzalloc(&cpcap->spi->dev, - sizeof(*cpcap->irqs) * - CPCAP_NR_IRQ_REG_BANKS * - cpcap->regmap_conf->val_bits, + array3_size(sizeof(*cpcap->irqs), + CPCAP_NR_IRQ_REG_BANKS, + cpcap->regmap_conf->val_bits), GFP_KERNEL); if (!cpcap->irqs) return -ENOMEM; diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c index 3460ef07623c..69df27769c21 100644 --- a/drivers/mfd/sprd-sc27xx-spi.c +++ b/drivers/mfd/sprd-sc27xx-spi.c @@ -199,8 +199,9 @@ static int sprd_pmic_probe(struct spi_device *spi) ddata->irq_chip.num_irqs = pdata->num_irqs; ddata->irq_chip.mask_invert = true; - ddata->irqs = devm_kzalloc(&spi->dev, sizeof(struct regmap_irq) * - pdata->num_irqs, GFP_KERNEL); + ddata->irqs = devm_kcalloc(&spi->dev, + pdata->num_irqs, sizeof(struct regmap_irq), + GFP_KERNEL); if (!ddata->irqs) return -ENOMEM; diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index c649344fd7f2..4be3d239da9e 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1139,8 +1139,9 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) } num_slaves = twl_get_num_slaves(); - twl_priv->twl_modules = devm_kzalloc(&client->dev, - sizeof(struct twl_client) * num_slaves, + twl_priv->twl_modules = devm_kcalloc(&client->dev, + num_slaves, + sizeof(struct twl_client), GFP_KERNEL); if (!twl_priv->twl_modules) { status = -ENOMEM; diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 953d0790ffd5..5d5888ee2966 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -368,9 +368,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) goto err; } - wm8994->supplies = devm_kzalloc(wm8994->dev, - sizeof(struct regulator_bulk_data) * - wm8994->num_supplies, GFP_KERNEL); + wm8994->supplies = devm_kcalloc(wm8994->dev, + wm8994->num_supplies, + sizeof(struct regulator_bulk_data), + GFP_KERNEL); if (!wm8994->supplies) { ret = -ENOMEM; goto err; diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index e2e31b65bc5a..c5dc6095686a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -264,8 +264,8 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) list_sort(NULL, &reserve_list, sram_reserve_cmp); if (exports) { - sram->partition = devm_kzalloc(sram->dev, - exports * sizeof(*sram->partition), + sram->partition = devm_kcalloc(sram->dev, + exports, sizeof(*sram->partition), GFP_KERNEL); if (!sram->partition) { ret = -ENOMEM; diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index f3a7c8ece4be..88347ce78f23 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -797,8 +797,10 @@ static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY)) return 0; - pinctrl_state = devm_kzalloc(dev, sizeof(*pinctrl_state) * - (MMC_TIMING_MMC_HS200 + 1), GFP_KERNEL); + pinctrl_state = devm_kcalloc(dev, + MMC_TIMING_MMC_HS200 + 1, + sizeof(*pinctrl_state), + GFP_KERNEL); if (!pinctrl_state) return -ENOMEM; diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index a0d485f52cbe..512bd4c2eec0 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1993,7 +1993,7 @@ static int __init docg3_probe(struct platform_device *pdev) base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE); ret = -ENOMEM; - cascade = devm_kzalloc(dev, sizeof(*cascade) * DOC_MAX_NBFLOORS, + cascade = devm_kcalloc(dev, DOC_MAX_NBFLOORS, sizeof(*cascade), GFP_KERNEL); if (!cascade) return ret; diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index b554fb6e609c..6a5519f0ff25 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2510,8 +2510,8 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) if (!nandc->regs) return -ENOMEM; - nandc->reg_read_buf = devm_kzalloc(nandc->dev, - MAX_REG_RD * sizeof(*nandc->reg_read_buf), + nandc->reg_read_buf = devm_kcalloc(nandc->dev, + MAX_REG_RD, sizeof(*nandc->reg_read_buf), GFP_KERNEL); if (!nandc->reg_read_buf) return -ENOMEM; diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index 1bc0458063d8..19661c5d3220 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -1038,7 +1038,7 @@ static int s3c24xx_nand_probe_dt(struct platform_device *pdev) if (!pdata->nr_sets) return 0; - sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets, + sets = devm_kcalloc(&pdev->dev, pdata->nr_sets, sizeof(*sets), GFP_KERNEL); if (!sets) return -ENOMEM; diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 5e010b1592f7..d93c790bfbe8 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -2044,14 +2044,14 @@ static int b53_switch_init(struct b53_device *dev) } } - dev->ports = devm_kzalloc(dev->dev, - sizeof(struct b53_port) * dev->num_ports, + dev->ports = devm_kcalloc(dev->dev, + dev->num_ports, sizeof(struct b53_port), GFP_KERNEL); if (!dev->ports) return -ENOMEM; - dev->vlans = devm_kzalloc(dev->dev, - sizeof(struct b53_vlan) * dev->num_vlans, + dev->vlans = devm_kcalloc(dev->dev, + dev->num_vlans, sizeof(struct b53_vlan), GFP_KERNEL); if (!dev->vlans) return -ENOMEM; diff --git a/drivers/net/ethernet/amazon/ena/ena_ethtool.c b/drivers/net/ethernet/amazon/ena/ena_ethtool.c index 060cb18fa659..521607bc4393 100644 --- a/drivers/net/ethernet/amazon/ena/ena_ethtool.c +++ b/drivers/net/ethernet/amazon/ena/ena_ethtool.c @@ -838,8 +838,8 @@ static void ena_dump_stats_ex(struct ena_adapter *adapter, u8 *buf) return; } - strings_buf = devm_kzalloc(&adapter->pdev->dev, - strings_num * ETH_GSTRING_LEN, + strings_buf = devm_kcalloc(&adapter->pdev->dev, + ETH_GSTRING_LEN, strings_num, GFP_ATOMIC); if (!strings_buf) { netif_err(adapter, drv, netdev, @@ -847,8 +847,8 @@ static void ena_dump_stats_ex(struct ena_adapter *adapter, u8 *buf) return; } - data_buf = devm_kzalloc(&adapter->pdev->dev, - strings_num * sizeof(u64), + data_buf = devm_kcalloc(&adapter->pdev->dev, + strings_num, sizeof(u64), GFP_ATOMIC); if (!data_buf) { netif_err(adapter, drv, netdev, diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index 00a57273b753..60da0499ad66 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -1141,7 +1141,8 @@ static int ethoc_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "ethoc: num_tx: %d num_rx: %d\n", priv->num_tx, priv->num_rx); - priv->vma = devm_kzalloc(&pdev->dev, num_bd*sizeof(void *), GFP_KERNEL); + priv->vma = devm_kcalloc(&pdev->dev, num_bd, sizeof(void *), + GFP_KERNEL); if (!priv->vma) { ret = -ENOMEM; goto free; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index fd43f98ddbe7..5f4e1ffa7b95 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -664,7 +664,7 @@ static struct dpaa_fq *dpaa_fq_alloc(struct device *dev, struct dpaa_fq *dpaa_fq; int i; - dpaa_fq = devm_kzalloc(dev, sizeof(*dpaa_fq) * count, + dpaa_fq = devm_kcalloc(dev, count, sizeof(*dpaa_fq), GFP_KERNEL); if (!dpaa_fq) return NULL; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index f2b31d278bc9..25a73bb2e642 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -2846,8 +2846,10 @@ static int hns3_get_ring_config(struct hns3_nic_priv *priv) struct pci_dev *pdev = h->pdev; int i, ret; - priv->ring_data = devm_kzalloc(&pdev->dev, h->kinfo.num_tqps * - sizeof(*priv->ring_data) * 2, + priv->ring_data = devm_kzalloc(&pdev->dev, + array3_size(h->kinfo.num_tqps, + sizeof(*priv->ring_data), + 2), GFP_KERNEL); if (!priv->ring_data) return -ENOMEM; diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c index b092894dd128..09f674ec0f9e 100644 --- a/drivers/net/ethernet/ni/nixge.c +++ b/drivers/net/ethernet/ni/nixge.c @@ -247,9 +247,8 @@ static int nixge_hw_dma_bd_init(struct net_device *ndev) if (!priv->tx_bd_v) goto out; - priv->tx_skb = devm_kzalloc(ndev->dev.parent, - sizeof(*priv->tx_skb) * - TX_BD_NUM, + priv->tx_skb = devm_kcalloc(ndev->dev.parent, + TX_BD_NUM, sizeof(*priv->tx_skb), GFP_KERNEL); if (!priv->tx_skb) goto out; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 881c94b73e2f..2258cd8cc844 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -277,8 +277,8 @@ static int tc_init(struct stmmac_priv *priv) /* Reserve one last filter which lets all pass */ priv->tc_entries_max = count; - priv->tc_entries = devm_kzalloc(priv->device, - sizeof(*priv->tc_entries) * count, GFP_KERNEL); + priv->tc_entries = devm_kcalloc(priv->device, + count, sizeof(*priv->tc_entries), GFP_KERNEL); if (!priv->tc_entries) return -ENOMEM; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 534596ce00d3..358edab9e72e 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2740,8 +2740,9 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->active_slave = prop; - data->slave_data = devm_kzalloc(&pdev->dev, data->slaves - * sizeof(struct cpsw_slave_data), + data->slave_data = devm_kcalloc(&pdev->dev, + data->slaves, + sizeof(struct cpsw_slave_data), GFP_KERNEL); if (!data->slave_data) return -ENOMEM; @@ -3045,8 +3046,8 @@ static int cpsw_probe(struct platform_device *pdev) memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); - cpsw->slaves = devm_kzalloc(&pdev->dev, - sizeof(struct cpsw_slave) * data->slaves, + cpsw->slaves = devm_kcalloc(&pdev->dev, + data->slaves, sizeof(struct cpsw_slave), GFP_KERNEL); if (!cpsw->slaves) { ret = -ENOMEM; diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index 6e455a27a8de..72b98e27c992 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -3285,8 +3285,8 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev, gbe_dev->et_stats = xgbe10_et_stats; gbe_dev->num_et_stats = ARRAY_SIZE(xgbe10_et_stats); - gbe_dev->hw_stats = devm_kzalloc(gbe_dev->dev, - gbe_dev->num_et_stats * sizeof(u64), + gbe_dev->hw_stats = devm_kcalloc(gbe_dev->dev, + gbe_dev->num_et_stats, sizeof(u64), GFP_KERNEL); if (!gbe_dev->hw_stats) { dev_err(gbe_dev->dev, "hw_stats memory allocation failed\n"); @@ -3294,8 +3294,8 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev, } gbe_dev->hw_stats_prev = - devm_kzalloc(gbe_dev->dev, - gbe_dev->num_et_stats * sizeof(u32), + devm_kcalloc(gbe_dev->dev, + gbe_dev->num_et_stats, sizeof(u32), GFP_KERNEL); if (!gbe_dev->hw_stats_prev) { dev_err(gbe_dev->dev, @@ -3405,8 +3405,8 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev, gbe_dev->et_stats = gbe13_et_stats; gbe_dev->num_et_stats = ARRAY_SIZE(gbe13_et_stats); - gbe_dev->hw_stats = devm_kzalloc(gbe_dev->dev, - gbe_dev->num_et_stats * sizeof(u64), + gbe_dev->hw_stats = devm_kcalloc(gbe_dev->dev, + gbe_dev->num_et_stats, sizeof(u64), GFP_KERNEL); if (!gbe_dev->hw_stats) { dev_err(gbe_dev->dev, "hw_stats memory allocation failed\n"); @@ -3414,8 +3414,8 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev, } gbe_dev->hw_stats_prev = - devm_kzalloc(gbe_dev->dev, - gbe_dev->num_et_stats * sizeof(u32), + devm_kcalloc(gbe_dev->dev, + gbe_dev->num_et_stats, sizeof(u32), GFP_KERNEL); if (!gbe_dev->hw_stats_prev) { dev_err(gbe_dev->dev, @@ -3477,8 +3477,8 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev, gbe_dev->num_et_stats = GBENU_ET_STATS_HOST_SIZE + GBENU_ET_STATS_PORT_SIZE; - gbe_dev->hw_stats = devm_kzalloc(gbe_dev->dev, - gbe_dev->num_et_stats * sizeof(u64), + gbe_dev->hw_stats = devm_kcalloc(gbe_dev->dev, + gbe_dev->num_et_stats, sizeof(u64), GFP_KERNEL); if (!gbe_dev->hw_stats) { dev_err(gbe_dev->dev, "hw_stats memory allocation failed\n"); @@ -3486,8 +3486,8 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev, } gbe_dev->hw_stats_prev = - devm_kzalloc(gbe_dev->dev, - gbe_dev->num_et_stats * sizeof(u32), + devm_kcalloc(gbe_dev->dev, + gbe_dev->num_et_stats, sizeof(u32), GFP_KERNEL); if (!gbe_dev->hw_stats_prev) { dev_err(gbe_dev->dev, diff --git a/drivers/net/phy/phy_led_triggers.c b/drivers/net/phy/phy_led_triggers.c index 39ecad25b201..491efc1bf5c4 100644 --- a/drivers/net/phy/phy_led_triggers.c +++ b/drivers/net/phy/phy_led_triggers.c @@ -128,9 +128,9 @@ int phy_led_triggers_register(struct phy_device *phy) if (err) goto out_free_link; - phy->phy_led_triggers = devm_kzalloc(&phy->mdio.dev, - sizeof(struct phy_led_trigger) * - phy->phy_num_led_triggers, + phy->phy_led_triggers = devm_kcalloc(&phy->mdio.dev, + phy->phy_num_led_triggers, + sizeof(struct phy_led_trigger), GFP_KERNEL); if (!phy->phy_led_triggers) { err = -ENOMEM; diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index fcd079a96782..d62e34e7eadf 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -181,7 +181,7 @@ mt76_init_sband(struct mt76_dev *dev, struct mt76_sband *msband, if (!chanlist) return -ENOMEM; - msband->chan = devm_kzalloc(dev->dev, n_chan * sizeof(*msband->chan), + msband->chan = devm_kcalloc(dev->dev, n_chan, sizeof(*msband->chan), GFP_KERNEL); if (!msband->chan) return -ENOMEM; diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c index 3d8283e450a9..e3fe4124e3af 100644 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ b/drivers/pci/cadence/pcie-cadence-ep.c @@ -467,7 +467,8 @@ static int cdns_pcie_ep_probe(struct platform_device *pdev) dev_err(dev, "missing \"cdns,max-outbound-regions\"\n"); return ret; } - ep->ob_addr = devm_kzalloc(dev, ep->max_regions * sizeof(*ep->ob_addr), + ep->ob_addr = devm_kcalloc(dev, + ep->max_regions, sizeof(*ep->ob_addr), GFP_KERNEL); if (!ep->ob_addr) return -ENOMEM; diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c index f688204e50c5..2810c6ab6199 100644 --- a/drivers/pci/dwc/pci-dra7xx.c +++ b/drivers/pci/dwc/pci-dra7xx.c @@ -639,11 +639,11 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) return phy_count; } - phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL); + phy = devm_kcalloc(dev, phy_count, sizeof(*phy), GFP_KERNEL); if (!phy) return -ENOMEM; - link = devm_kzalloc(dev, sizeof(*link) * phy_count, GFP_KERNEL); + link = devm_kcalloc(dev, phy_count, sizeof(*link), GFP_KERNEL); if (!link) return -ENOMEM; diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index 1eec4415a77f..8650416f6f9e 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -366,19 +366,21 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) return -EINVAL; } - ep->ib_window_map = devm_kzalloc(dev, sizeof(long) * + ep->ib_window_map = devm_kcalloc(dev, BITS_TO_LONGS(ep->num_ib_windows), + sizeof(long), GFP_KERNEL); if (!ep->ib_window_map) return -ENOMEM; - ep->ob_window_map = devm_kzalloc(dev, sizeof(long) * + ep->ob_window_map = devm_kcalloc(dev, BITS_TO_LONGS(ep->num_ob_windows), + sizeof(long), GFP_KERNEL); if (!ep->ob_window_map) return -ENOMEM; - addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows, + addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(phys_addr_t), GFP_KERNEL); if (!addr) return -ENOMEM; diff --git a/drivers/pci/host/pcie-rockchip-ep.c b/drivers/pci/host/pcie-rockchip-ep.c index fc267a49a932..6beba8ed7b84 100644 --- a/drivers/pci/host/pcie-rockchip-ep.c +++ b/drivers/pci/host/pcie-rockchip-ep.c @@ -593,7 +593,7 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) PCIE_CLIENT_CONFIG); max_regions = ep->max_regions; - ep->ob_addr = devm_kzalloc(dev, max_regions * sizeof(*ep->ob_addr), + ep->ob_addr = devm_kcalloc(dev, max_regions, sizeof(*ep->ob_addr), GFP_KERNEL); if (!ep->ob_addr) { diff --git a/drivers/pinctrl/berlin/berlin.c b/drivers/pinctrl/berlin/berlin.c index a620a8e8fa78..d6d183e9db17 100644 --- a/drivers/pinctrl/berlin/berlin.c +++ b/drivers/pinctrl/berlin/berlin.c @@ -216,8 +216,9 @@ static int berlin_pinctrl_build_state(struct platform_device *pdev) } /* we will reallocate later */ - pctrl->functions = devm_kzalloc(&pdev->dev, - max_functions * sizeof(*pctrl->functions), + pctrl->functions = devm_kcalloc(&pdev->dev, + max_functions, + sizeof(*pctrl->functions), GFP_KERNEL); if (!pctrl->functions) return -ENOMEM; @@ -261,8 +262,9 @@ static int berlin_pinctrl_build_state(struct platform_device *pdev) if (!function->groups) { function->groups = - devm_kzalloc(&pdev->dev, - function->ngroups * sizeof(char *), + devm_kcalloc(&pdev->dev, + function->ngroups, + sizeof(char *), GFP_KERNEL); if (!function->groups) diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index 28e5b7f62044..1c6bb15579e1 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -477,10 +477,12 @@ static int imx_pinctrl_parse_groups(struct device_node *np, config = imx_pinconf_parse_generic_config(np, ipctl); grp->num_pins = size / pin_size; - grp->data = devm_kzalloc(ipctl->dev, grp->num_pins * - sizeof(struct imx_pin), GFP_KERNEL); - grp->pins = devm_kzalloc(ipctl->dev, grp->num_pins * - sizeof(unsigned int), GFP_KERNEL); + grp->data = devm_kcalloc(ipctl->dev, + grp->num_pins, sizeof(struct imx_pin), + GFP_KERNEL); + grp->pins = devm_kcalloc(ipctl->dev, + grp->num_pins, sizeof(unsigned int), + GFP_KERNEL); if (!grp->pins || !grp->data) return -ENOMEM; diff --git a/drivers/pinctrl/freescale/pinctrl-imx1-core.c b/drivers/pinctrl/freescale/pinctrl-imx1-core.c index e7169ac7799f..c3bdd90b1422 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx1-core.c +++ b/drivers/pinctrl/freescale/pinctrl-imx1-core.c @@ -483,10 +483,10 @@ static int imx1_pinctrl_parse_groups(struct device_node *np, } grp->npins = size / 12; - grp->pins = devm_kzalloc(info->dev, - grp->npins * sizeof(struct imx1_pin), GFP_KERNEL); - grp->pin_ids = devm_kzalloc(info->dev, - grp->npins * sizeof(unsigned int), GFP_KERNEL); + grp->pins = devm_kcalloc(info->dev, + grp->npins, sizeof(struct imx1_pin), GFP_KERNEL); + grp->pin_ids = devm_kcalloc(info->dev, + grp->npins, sizeof(unsigned int), GFP_KERNEL); if (!grp->pins || !grp->pin_ids) return -ENOMEM; @@ -523,8 +523,8 @@ static int imx1_pinctrl_parse_functions(struct device_node *np, if (func->num_groups == 0) return -EINVAL; - func->groups = devm_kzalloc(info->dev, - func->num_groups * sizeof(char *), GFP_KERNEL); + func->groups = devm_kcalloc(info->dev, + func->num_groups, sizeof(char *), GFP_KERNEL); if (!func->groups) return -ENOMEM; @@ -566,12 +566,12 @@ static int imx1_pinctrl_parse_dt(struct platform_device *pdev, } info->nfunctions = nfuncs; - info->functions = devm_kzalloc(&pdev->dev, - nfuncs * sizeof(struct imx1_pmx_func), GFP_KERNEL); + info->functions = devm_kcalloc(&pdev->dev, + nfuncs, sizeof(struct imx1_pmx_func), GFP_KERNEL); info->ngroups = ngroups; - info->groups = devm_kzalloc(&pdev->dev, - ngroups * sizeof(struct imx1_pin_group), GFP_KERNEL); + info->groups = devm_kcalloc(&pdev->dev, + ngroups, sizeof(struct imx1_pin_group), GFP_KERNEL); if (!info->functions || !info->groups) diff --git a/drivers/pinctrl/freescale/pinctrl-mxs.c b/drivers/pinctrl/freescale/pinctrl-mxs.c index 3a17846aa31f..a612e46ca51c 100644 --- a/drivers/pinctrl/freescale/pinctrl-mxs.c +++ b/drivers/pinctrl/freescale/pinctrl-mxs.c @@ -370,12 +370,12 @@ static int mxs_pinctrl_parse_group(struct platform_device *pdev, return -EINVAL; g->npins = length / sizeof(u32); - g->pins = devm_kzalloc(&pdev->dev, g->npins * sizeof(*g->pins), + g->pins = devm_kcalloc(&pdev->dev, g->npins, sizeof(*g->pins), GFP_KERNEL); if (!g->pins) return -ENOMEM; - g->muxsel = devm_kzalloc(&pdev->dev, g->npins * sizeof(*g->muxsel), + g->muxsel = devm_kcalloc(&pdev->dev, g->npins, sizeof(*g->muxsel), GFP_KERNEL); if (!g->muxsel) return -ENOMEM; @@ -426,13 +426,16 @@ static int mxs_pinctrl_probe_dt(struct platform_device *pdev, } } - soc->functions = devm_kzalloc(&pdev->dev, soc->nfunctions * - sizeof(*soc->functions), GFP_KERNEL); + soc->functions = devm_kcalloc(&pdev->dev, + soc->nfunctions, + sizeof(*soc->functions), + GFP_KERNEL); if (!soc->functions) return -ENOMEM; - soc->groups = devm_kzalloc(&pdev->dev, soc->ngroups * - sizeof(*soc->groups), GFP_KERNEL); + soc->groups = devm_kcalloc(&pdev->dev, + soc->ngroups, sizeof(*soc->groups), + GFP_KERNEL); if (!soc->groups) return -ENOMEM; @@ -492,7 +495,8 @@ static int mxs_pinctrl_probe_dt(struct platform_device *pdev, if (strcmp(fn, child->name)) { f = &soc->functions[idxf++]; - f->groups = devm_kzalloc(&pdev->dev, f->ngroups * + f->groups = devm_kcalloc(&pdev->dev, + f->ngroups, sizeof(*f->groups), GFP_KERNEL); if (!f->groups) diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index 674ffdf8103c..53cf800688e9 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -856,9 +856,10 @@ static int armada_37xx_fill_group(struct armada_37xx_pinctrl *info) struct armada_37xx_pin_group *grp = &info->groups[n]; int i, j, f; - grp->pins = devm_kzalloc(info->dev, - (grp->npins + grp->extra_npins) * - sizeof(*grp->pins), GFP_KERNEL); + grp->pins = devm_kcalloc(info->dev, + grp->npins + grp->extra_npins, + sizeof(*grp->pins), + GFP_KERNEL); if (!grp->pins) return -ENOMEM; @@ -908,7 +909,8 @@ static int armada_37xx_fill_func(struct armada_37xx_pinctrl *info) const char **groups; int g; - funcs[n].groups = devm_kzalloc(info->dev, funcs[n].ngroups * + funcs[n].groups = devm_kcalloc(info->dev, + funcs[n].ngroups, sizeof(*(funcs[n].groups)), GFP_KERNEL); if (!funcs[n].groups) @@ -948,8 +950,9 @@ static int armada_37xx_pinctrl_register(struct platform_device *pdev, ctrldesc->pmxops = &armada_37xx_pmx_ops; ctrldesc->confops = &armada_37xx_pinconf_ops; - pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * - pin_data->nr_pins, GFP_KERNEL); + pindesc = devm_kcalloc(&pdev->dev, + pin_data->nr_pins, sizeof(*pindesc), + GFP_KERNEL); if (!pindesc) return -ENOMEM; @@ -968,8 +971,10 @@ static int armada_37xx_pinctrl_register(struct platform_device *pdev, * we allocate functions for number of pins and hope there are * fewer unique functions than pins available */ - info->funcs = devm_kzalloc(&pdev->dev, pin_data->nr_pins * - sizeof(struct armada_37xx_pmx_func), GFP_KERNEL); + info->funcs = devm_kcalloc(&pdev->dev, + pin_data->nr_pins, + sizeof(struct armada_37xx_pmx_func), + GFP_KERNEL); if (!info->funcs) return -ENOMEM; diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c index 9e05cfaf75f0..d7ec7119701b 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c @@ -501,8 +501,9 @@ static int mvebu_pinctrl_build_functions(struct platform_device *pdev, /* we allocate functions for number of pins and hope * there are fewer unique functions than pins available */ - funcs = devm_kzalloc(&pdev->dev, funcsize * - sizeof(struct mvebu_pinctrl_function), GFP_KERNEL); + funcs = devm_kcalloc(&pdev->dev, + funcsize, sizeof(struct mvebu_pinctrl_function), + GFP_KERNEL); if (!funcs) return -ENOMEM; @@ -549,8 +550,9 @@ static int mvebu_pinctrl_build_functions(struct platform_device *pdev, /* allocate group name array if not done already */ if (!f->groups) { - f->groups = devm_kzalloc(&pdev->dev, - f->num_groups * sizeof(char *), + f->groups = devm_kcalloc(&pdev->dev, + f->num_groups, + sizeof(char *), GFP_KERNEL); if (!f->groups) return -ENOMEM; @@ -622,8 +624,10 @@ int mvebu_pinctrl_probe(struct platform_device *pdev) } } - pdesc = devm_kzalloc(&pdev->dev, pctl->desc.npins * - sizeof(struct pinctrl_pin_desc), GFP_KERNEL); + pdesc = devm_kcalloc(&pdev->dev, + pctl->desc.npins, + sizeof(struct pinctrl_pin_desc), + GFP_KERNEL); if (!pdesc) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c index bafb3d40545e..67e4d9ffa6b1 100644 --- a/drivers/pinctrl/pinctrl-at91-pio4.c +++ b/drivers/pinctrl/pinctrl-at91-pio4.c @@ -945,27 +945,30 @@ static int atmel_pinctrl_probe(struct platform_device *pdev) return PTR_ERR(atmel_pioctrl->clk); } - atmel_pioctrl->pins = devm_kzalloc(dev, sizeof(*atmel_pioctrl->pins) - * atmel_pioctrl->npins, GFP_KERNEL); + atmel_pioctrl->pins = devm_kcalloc(dev, + atmel_pioctrl->npins, + sizeof(*atmel_pioctrl->pins), + GFP_KERNEL); if (!atmel_pioctrl->pins) return -ENOMEM; - pin_desc = devm_kzalloc(dev, sizeof(*pin_desc) - * atmel_pioctrl->npins, GFP_KERNEL); + pin_desc = devm_kcalloc(dev, atmel_pioctrl->npins, sizeof(*pin_desc), + GFP_KERNEL); if (!pin_desc) return -ENOMEM; atmel_pinctrl_desc.pins = pin_desc; atmel_pinctrl_desc.npins = atmel_pioctrl->npins; /* One pin is one group since a pin can achieve all functions. */ - group_names = devm_kzalloc(dev, sizeof(*group_names) - * atmel_pioctrl->npins, GFP_KERNEL); + group_names = devm_kcalloc(dev, + atmel_pioctrl->npins, sizeof(*group_names), + GFP_KERNEL); if (!group_names) return -ENOMEM; atmel_pioctrl->group_names = group_names; - atmel_pioctrl->groups = devm_kzalloc(&pdev->dev, - sizeof(*atmel_pioctrl->groups) * atmel_pioctrl->npins, + atmel_pioctrl->groups = devm_kcalloc(&pdev->dev, + atmel_pioctrl->npins, sizeof(*atmel_pioctrl->groups), GFP_KERNEL); if (!atmel_pioctrl->groups) return -ENOMEM; @@ -1001,20 +1004,24 @@ static int atmel_pinctrl_probe(struct platform_device *pdev) atmel_pioctrl->gpio_chip->parent = dev; atmel_pioctrl->gpio_chip->names = atmel_pioctrl->group_names; - atmel_pioctrl->pm_wakeup_sources = devm_kzalloc(dev, - sizeof(*atmel_pioctrl->pm_wakeup_sources) - * atmel_pioctrl->nbanks, GFP_KERNEL); + atmel_pioctrl->pm_wakeup_sources = devm_kcalloc(dev, + atmel_pioctrl->nbanks, + sizeof(*atmel_pioctrl->pm_wakeup_sources), + GFP_KERNEL); if (!atmel_pioctrl->pm_wakeup_sources) return -ENOMEM; - atmel_pioctrl->pm_suspend_backup = devm_kzalloc(dev, - sizeof(*atmel_pioctrl->pm_suspend_backup) - * atmel_pioctrl->nbanks, GFP_KERNEL); + atmel_pioctrl->pm_suspend_backup = devm_kcalloc(dev, + atmel_pioctrl->nbanks, + sizeof(*atmel_pioctrl->pm_suspend_backup), + GFP_KERNEL); if (!atmel_pioctrl->pm_suspend_backup) return -ENOMEM; - atmel_pioctrl->irqs = devm_kzalloc(dev, sizeof(*atmel_pioctrl->irqs) - * atmel_pioctrl->nbanks, GFP_KERNEL); + atmel_pioctrl->irqs = devm_kcalloc(dev, + atmel_pioctrl->nbanks, + sizeof(*atmel_pioctrl->irqs), + GFP_KERNEL); if (!atmel_pioctrl->irqs) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 297f1d161211..50f0ec42c637 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -269,7 +269,8 @@ static int at91_dt_node_to_map(struct pinctrl_dev *pctldev, } map_num += grp->npins; - new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, GFP_KERNEL); + new_map = devm_kcalloc(pctldev->dev, map_num, sizeof(*new_map), + GFP_KERNEL); if (!new_map) return -ENOMEM; @@ -1049,7 +1050,8 @@ static int at91_pinctrl_mux_mask(struct at91_pinctrl *info, } info->nmux = size / gpio_banks; - info->mux_mask = devm_kzalloc(info->dev, sizeof(u32) * size, GFP_KERNEL); + info->mux_mask = devm_kcalloc(info->dev, size, sizeof(u32), + GFP_KERNEL); if (!info->mux_mask) return -ENOMEM; @@ -1087,10 +1089,12 @@ static int at91_pinctrl_parse_groups(struct device_node *np, } grp->npins = size / 4; - pin = grp->pins_conf = devm_kzalloc(info->dev, grp->npins * sizeof(struct at91_pmx_pin), - GFP_KERNEL); - grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), - GFP_KERNEL); + pin = grp->pins_conf = devm_kcalloc(info->dev, + grp->npins, + sizeof(struct at91_pmx_pin), + GFP_KERNEL); + grp->pins = devm_kcalloc(info->dev, grp->npins, sizeof(unsigned int), + GFP_KERNEL); if (!grp->pins_conf || !grp->pins) return -ENOMEM; @@ -1129,8 +1133,8 @@ static int at91_pinctrl_parse_functions(struct device_node *np, dev_err(info->dev, "no groups defined\n"); return -EINVAL; } - func->groups = devm_kzalloc(info->dev, - func->ngroups * sizeof(char *), GFP_KERNEL); + func->groups = devm_kcalloc(info->dev, + func->ngroups, sizeof(char *), GFP_KERNEL); if (!func->groups) return -ENOMEM; @@ -1192,12 +1196,16 @@ static int at91_pinctrl_probe_dt(struct platform_device *pdev, dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); - info->functions = devm_kzalloc(&pdev->dev, info->nfunctions * sizeof(struct at91_pmx_func), + info->functions = devm_kcalloc(&pdev->dev, + info->nfunctions, + sizeof(struct at91_pmx_func), GFP_KERNEL); if (!info->functions) return -ENOMEM; - info->groups = devm_kzalloc(&pdev->dev, info->ngroups * sizeof(struct at91_pin_group), + info->groups = devm_kcalloc(&pdev->dev, + info->ngroups, + sizeof(struct at91_pin_group), GFP_KERNEL); if (!info->groups) return -ENOMEM; @@ -1256,7 +1264,9 @@ static int at91_pinctrl_probe(struct platform_device *pdev) at91_pinctrl_desc.name = dev_name(&pdev->dev); at91_pinctrl_desc.npins = gpio_banks * MAX_NB_GPIO_PER_BANK; at91_pinctrl_desc.pins = pdesc = - devm_kzalloc(&pdev->dev, sizeof(*pdesc) * at91_pinctrl_desc.npins, GFP_KERNEL); + devm_kcalloc(&pdev->dev, + at91_pinctrl_desc.npins, sizeof(*pdesc), + GFP_KERNEL); if (!at91_pinctrl_desc.pins) return -ENOMEM; @@ -1763,7 +1773,7 @@ static int at91_gpio_probe(struct platform_device *pdev) chip->ngpio = ngpio; } - names = devm_kzalloc(&pdev->dev, sizeof(char *) * chip->ngpio, + names = devm_kcalloc(&pdev->dev, chip->ngpio, sizeof(char *), GFP_KERNEL); if (!names) { diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c index 1231bbbfa744..a52779f33ad4 100644 --- a/drivers/pinctrl/pinctrl-axp209.c +++ b/drivers/pinctrl/pinctrl-axp209.c @@ -328,7 +328,8 @@ static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask, func->ngroups = ngroups; if (func->ngroups > 0) { - func->groups = devm_kzalloc(dev, ngroups * sizeof(const char *), + func->groups = devm_kcalloc(dev, + ngroups, sizeof(const char *), GFP_KERNEL); group = func->groups; for_each_set_bit(bit, &mask_cpy, mask_len) { @@ -358,8 +359,8 @@ static void axp20x_build_funcs_groups(struct platform_device *pdev) /* Every pin supports GPIO_OUT and GPIO_IN functions */ for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) { pctl->funcs[i].ngroups = npins; - pctl->funcs[i].groups = devm_kzalloc(&pdev->dev, - npins * sizeof(char *), + pctl->funcs[i].groups = devm_kcalloc(&pdev->dev, + npins, sizeof(char *), GFP_KERNEL); for (pin = 0; pin < npins; pin++) pctl->funcs[i].groups[pin] = pctl->desc->pins[pin].name; diff --git a/drivers/pinctrl/pinctrl-digicolor.c b/drivers/pinctrl/pinctrl-digicolor.c index ce269ced4d49..5353b23f775c 100644 --- a/drivers/pinctrl/pinctrl-digicolor.c +++ b/drivers/pinctrl/pinctrl-digicolor.c @@ -291,10 +291,11 @@ static int dc_pinctrl_probe(struct platform_device *pdev) if (IS_ERR(pmap->regs)) return PTR_ERR(pmap->regs); - pins = devm_kzalloc(&pdev->dev, sizeof(*pins)*PINS_COUNT, GFP_KERNEL); + pins = devm_kcalloc(&pdev->dev, PINS_COUNT, sizeof(*pins), + GFP_KERNEL); if (!pins) return -ENOMEM; - pin_names = devm_kzalloc(&pdev->dev, name_len * PINS_COUNT, + pin_names = devm_kcalloc(&pdev->dev, PINS_COUNT, name_len, GFP_KERNEL); if (!pin_names) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index ac38a3f9f86b..a1d7156d0a43 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -770,8 +770,8 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev) pctl_desc->pmxops = &ingenic_pmxops; pctl_desc->confops = &ingenic_confops; pctl_desc->npins = chip_info->num_chips * PINS_PER_GPIO_CHIP; - pctl_desc->pins = jzpc->pdesc = devm_kzalloc(&pdev->dev, - sizeof(*jzpc->pdesc) * pctl_desc->npins, GFP_KERNEL); + pctl_desc->pins = jzpc->pdesc = devm_kcalloc(&pdev->dev, + pctl_desc->npins, sizeof(*jzpc->pdesc), GFP_KERNEL); if (!jzpc->pdesc) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-lpc18xx.c b/drivers/pinctrl/pinctrl-lpc18xx.c index d090f37ca4a1..190f17e4bbda 100644 --- a/drivers/pinctrl/pinctrl-lpc18xx.c +++ b/drivers/pinctrl/pinctrl-lpc18xx.c @@ -1308,8 +1308,9 @@ static int lpc18xx_create_group_func_map(struct device *dev, } scu->func[func].ngroups = ngroups; - scu->func[func].groups = devm_kzalloc(dev, ngroups * - sizeof(char *), GFP_KERNEL); + scu->func[func].groups = devm_kcalloc(dev, + ngroups, sizeof(char *), + GFP_KERNEL); if (!scu->func[func].groups) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c index b5b3547fdcb2..15bb1cb8729b 100644 --- a/drivers/pinctrl/pinctrl-ocelot.c +++ b/drivers/pinctrl/pinctrl-ocelot.c @@ -330,7 +330,8 @@ static int ocelot_create_group_func_map(struct device *dev, } info->func[f].ngroups = npins; - info->func[f].groups = devm_kzalloc(dev, npins * + info->func[f].groups = devm_kcalloc(dev, + npins, sizeof(char *), GFP_KERNEL); if (!info->func[f].groups) diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index 1882713e68f9..f4a61429e06e 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -507,7 +507,7 @@ static int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev, } map_num += grp->npins; - new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, + new_map = devm_kcalloc(pctldev->dev, map_num, sizeof(*new_map), GFP_KERNEL); if (!new_map) return -ENOMEM; @@ -2473,10 +2473,11 @@ static int rockchip_pinctrl_parse_groups(struct device_node *np, grp->npins = size / 4; - grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), + grp->pins = devm_kcalloc(info->dev, grp->npins, sizeof(unsigned int), GFP_KERNEL); - grp->data = devm_kzalloc(info->dev, grp->npins * - sizeof(struct rockchip_pin_config), + grp->data = devm_kcalloc(info->dev, + grp->npins, + sizeof(struct rockchip_pin_config), GFP_KERNEL); if (!grp->pins || !grp->data) return -ENOMEM; @@ -2528,8 +2529,8 @@ static int rockchip_pinctrl_parse_functions(struct device_node *np, if (func->ngroups <= 0) return 0; - func->groups = devm_kzalloc(info->dev, - func->ngroups * sizeof(char *), GFP_KERNEL); + func->groups = devm_kcalloc(info->dev, + func->ngroups, sizeof(char *), GFP_KERNEL); if (!func->groups) return -ENOMEM; @@ -2560,13 +2561,15 @@ static int rockchip_pinctrl_parse_dt(struct platform_device *pdev, dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); - info->functions = devm_kzalloc(dev, info->nfunctions * + info->functions = devm_kcalloc(dev, + info->nfunctions, sizeof(struct rockchip_pmx_func), GFP_KERNEL); if (!info->functions) return -EINVAL; - info->groups = devm_kzalloc(dev, info->ngroups * + info->groups = devm_kcalloc(dev, + info->ngroups, sizeof(struct rockchip_pin_group), GFP_KERNEL); if (!info->groups) @@ -2604,8 +2607,9 @@ static int rockchip_pinctrl_register(struct platform_device *pdev, ctrldesc->pmxops = &rockchip_pmx_ops; ctrldesc->confops = &rockchip_pinconf_ops; - pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * - info->ctrl->nr_pins, GFP_KERNEL); + pindesc = devm_kcalloc(&pdev->dev, + info->ctrl->nr_pins, sizeof(*pindesc), + GFP_KERNEL); if (!pindesc) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 9c3c00515aa0..b3153c095199 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -712,8 +712,8 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs) } dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins); - pcs->pins.pa = devm_kzalloc(pcs->dev, - sizeof(*pcs->pins.pa) * nr_pins, + pcs->pins.pa = devm_kcalloc(pcs->dev, + nr_pins, sizeof(*pcs->pins.pa), GFP_KERNEL); if (!pcs->pins.pa) return -ENOMEM; @@ -924,15 +924,15 @@ static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, if (!nconfs) return 0; - func->conf = devm_kzalloc(pcs->dev, - sizeof(struct pcs_conf_vals) * nconfs, + func->conf = devm_kcalloc(pcs->dev, + nconfs, sizeof(struct pcs_conf_vals), GFP_KERNEL); if (!func->conf) return -ENOMEM; func->nconfs = nconfs; conf = &(func->conf[0]); m++; - settings = devm_kzalloc(pcs->dev, sizeof(unsigned long) * nconfs, + settings = devm_kcalloc(pcs->dev, nconfs, sizeof(unsigned long), GFP_KERNEL); if (!settings) return -ENOMEM; @@ -988,11 +988,11 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, return -EINVAL; } - vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL); + vals = devm_kcalloc(pcs->dev, rows, sizeof(*vals), GFP_KERNEL); if (!vals) return -ENOMEM; - pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows, GFP_KERNEL); + pins = devm_kcalloc(pcs->dev, rows, sizeof(*pins), GFP_KERNEL); if (!pins) goto free_vals; @@ -1089,13 +1089,15 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, npins_in_row = pcs->width / pcs->bits_per_pin; - vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows * npins_in_row, - GFP_KERNEL); + vals = devm_kzalloc(pcs->dev, + array3_size(rows, npins_in_row, sizeof(*vals)), + GFP_KERNEL); if (!vals) return -ENOMEM; - pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows * npins_in_row, - GFP_KERNEL); + pins = devm_kzalloc(pcs->dev, + array3_size(rows, npins_in_row, sizeof(*pins)), + GFP_KERNEL); if (!pins) goto free_vals; @@ -1217,7 +1219,7 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, pcs = pinctrl_dev_get_drvdata(pctldev); /* create 2 maps. One is for pinmux, and the other is for pinconf. */ - *map = devm_kzalloc(pcs->dev, sizeof(**map) * 2, GFP_KERNEL); + *map = devm_kcalloc(pcs->dev, 2, sizeof(**map), GFP_KERNEL); if (!*map) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 2081c67667a8..0966bb0bf71f 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -823,8 +823,8 @@ static int st_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, } map_num = grp->npins + 1; - new_map = devm_kzalloc(pctldev->dev, - sizeof(*new_map) * map_num, GFP_KERNEL); + new_map = devm_kcalloc(pctldev->dev, + map_num, sizeof(*new_map), GFP_KERNEL); if (!new_map) return -ENOMEM; @@ -1191,9 +1191,9 @@ static int st_pctl_dt_parse_groups(struct device_node *np, grp->npins = npins; grp->name = np->name; - grp->pins = devm_kzalloc(info->dev, npins * sizeof(u32), GFP_KERNEL); - grp->pin_conf = devm_kzalloc(info->dev, - npins * sizeof(*conf), GFP_KERNEL); + grp->pins = devm_kcalloc(info->dev, npins, sizeof(u32), GFP_KERNEL); + grp->pin_conf = devm_kcalloc(info->dev, + npins, sizeof(*conf), GFP_KERNEL); if (!grp->pins || !grp->pin_conf) return -ENOMEM; @@ -1249,8 +1249,8 @@ static int st_pctl_parse_functions(struct device_node *np, dev_err(info->dev, "No groups defined\n"); return -EINVAL; } - func->groups = devm_kzalloc(info->dev, - func->ngroups * sizeof(char *), GFP_KERNEL); + func->groups = devm_kcalloc(info->dev, + func->ngroups, sizeof(char *), GFP_KERNEL); if (!func->groups) return -ENOMEM; @@ -1573,14 +1573,15 @@ static int st_pctl_probe_dt(struct platform_device *pdev, dev_info(&pdev->dev, "nfunctions = %d\n", info->nfunctions); dev_info(&pdev->dev, "ngroups = %d\n", info->ngroups); - info->functions = devm_kzalloc(&pdev->dev, - info->nfunctions * sizeof(*info->functions), GFP_KERNEL); + info->functions = devm_kcalloc(&pdev->dev, + info->nfunctions, sizeof(*info->functions), GFP_KERNEL); - info->groups = devm_kzalloc(&pdev->dev, - info->ngroups * sizeof(*info->groups) , GFP_KERNEL); + info->groups = devm_kcalloc(&pdev->dev, + info->ngroups, sizeof(*info->groups), + GFP_KERNEL); - info->banks = devm_kzalloc(&pdev->dev, - info->nbanks * sizeof(*info->banks), GFP_KERNEL); + info->banks = devm_kcalloc(&pdev->dev, + info->nbanks, sizeof(*info->banks), GFP_KERNEL); if (!info->functions || !info->groups || !info->banks) return -ENOMEM; @@ -1608,8 +1609,8 @@ static int st_pctl_probe_dt(struct platform_device *pdev, } pctl_desc->npins = info->nbanks * ST_GPIO_PINS_PER_BANK; - pdesc = devm_kzalloc(&pdev->dev, - sizeof(*pdesc) * pctl_desc->npins, GFP_KERNEL); + pdesc = devm_kcalloc(&pdev->dev, + pctl_desc->npins, sizeof(*pdesc), GFP_KERNEL); if (!pdesc) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c index cd0f402c1164..93f8bd04e7fe 100644 --- a/drivers/pinctrl/pinctrl-xway.c +++ b/drivers/pinctrl/pinctrl-xway.c @@ -1727,8 +1727,8 @@ static int pinmux_xway_probe(struct platform_device *pdev) xway_chip.ngpio = xway_soc->pin_count; /* load our pad descriptors */ - xway_info.pads = devm_kzalloc(&pdev->dev, - sizeof(struct pinctrl_pin_desc) * xway_chip.ngpio, + xway_info.pads = devm_kcalloc(&pdev->dev, + xway_chip.ngpio, sizeof(struct pinctrl_pin_desc), GFP_KERNEL); if (!xway_info.pads) return -ENOMEM; diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c index 0a625a64ff5d..a263ddd94945 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c @@ -491,8 +491,9 @@ int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d) continue; } - weint_data = devm_kzalloc(dev, bank->nr_pins - * sizeof(*weint_data), GFP_KERNEL); + weint_data = devm_kcalloc(dev, + bank->nr_pins, sizeof(*weint_data), + GFP_KERNEL); if (!weint_data) return -ENOMEM; diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c index 618945a0fd38..698c7d8c9a08 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.c +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c @@ -674,7 +674,7 @@ static struct samsung_pin_group *samsung_pinctrl_create_groups( const struct pinctrl_pin_desc *pdesc; int i; - groups = devm_kzalloc(dev, ctrldesc->npins * sizeof(*groups), + groups = devm_kcalloc(dev, ctrldesc->npins, sizeof(*groups), GFP_KERNEL); if (!groups) return ERR_PTR(-EINVAL); @@ -711,7 +711,7 @@ static int samsung_pinctrl_create_function(struct device *dev, func->name = func_np->full_name; - func->groups = devm_kzalloc(dev, npins * sizeof(char *), GFP_KERNEL); + func->groups = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL); if (!func->groups) return -ENOMEM; @@ -768,7 +768,7 @@ static struct samsung_pmx_func *samsung_pinctrl_create_functions( } } - functions = devm_kzalloc(dev, func_cnt * sizeof(*functions), + functions = devm_kcalloc(dev, func_cnt, sizeof(*functions), GFP_KERNEL); if (!functions) return ERR_PTR(-ENOMEM); @@ -860,8 +860,9 @@ static int samsung_pinctrl_register(struct platform_device *pdev, ctrldesc->pmxops = &samsung_pinmux_ops; ctrldesc->confops = &samsung_pinconf_ops; - pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * - drvdata->nr_pins, GFP_KERNEL); + pindesc = devm_kcalloc(&pdev->dev, + drvdata->nr_pins, sizeof(*pindesc), + GFP_KERNEL); if (!pindesc) return -ENOMEM; ctrldesc->pins = pindesc; @@ -875,8 +876,10 @@ static int samsung_pinctrl_register(struct platform_device *pdev, * allocate space for storing the dynamically generated names for all * the pins which belong to this pin-controller. */ - pin_names = devm_kzalloc(&pdev->dev, sizeof(char) * PIN_NAME_LENGTH * - drvdata->nr_pins, GFP_KERNEL); + pin_names = devm_kzalloc(&pdev->dev, + array3_size(sizeof(char), PIN_NAME_LENGTH, + drvdata->nr_pins), + GFP_KERNEL); if (!pin_names) return -ENOMEM; diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index eb06981538b4..c671c3c4aca6 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -57,7 +57,7 @@ static int sh_pfc_map_resources(struct sh_pfc *pfc, return -EINVAL; /* Allocate memory windows and IRQs arrays. */ - windows = devm_kzalloc(pfc->dev, num_windows * sizeof(*windows), + windows = devm_kcalloc(pfc->dev, num_windows, sizeof(*windows), GFP_KERNEL); if (windows == NULL) return -ENOMEM; @@ -66,7 +66,7 @@ static int sh_pfc_map_resources(struct sh_pfc *pfc, pfc->windows = windows; if (num_irqs) { - irqs = devm_kzalloc(pfc->dev, num_irqs * sizeof(*irqs), + irqs = devm_kcalloc(pfc->dev, num_irqs, sizeof(*irqs), GFP_KERNEL); if (irqs == NULL) return -ENOMEM; @@ -444,7 +444,7 @@ static int sh_pfc_init_ranges(struct sh_pfc *pfc) } pfc->nr_ranges = nr_ranges; - pfc->ranges = devm_kzalloc(pfc->dev, sizeof(*pfc->ranges) * nr_ranges, + pfc->ranges = devm_kcalloc(pfc->dev, nr_ranges, sizeof(*pfc->ranges), GFP_KERNEL); if (pfc->ranges == NULL) return -ENOMEM; diff --git a/drivers/pinctrl/sh-pfc/gpio.c b/drivers/pinctrl/sh-pfc/gpio.c index 946d9be50b62..6ffdc6beb203 100644 --- a/drivers/pinctrl/sh-pfc/gpio.c +++ b/drivers/pinctrl/sh-pfc/gpio.c @@ -107,7 +107,7 @@ static int gpio_setup_data_regs(struct sh_pfc_chip *chip) for (i = 0; pfc->info->data_regs[i].reg_width; ++i) ; - chip->regs = devm_kzalloc(pfc->dev, i * sizeof(*chip->regs), + chip->regs = devm_kcalloc(pfc->dev, i, sizeof(*chip->regs), GFP_KERNEL); if (chip->regs == NULL) return -ENOMEM; @@ -224,8 +224,9 @@ static int gpio_pin_setup(struct sh_pfc_chip *chip) struct gpio_chip *gc = &chip->gpio_chip; int ret; - chip->pins = devm_kzalloc(pfc->dev, pfc->info->nr_pins * - sizeof(*chip->pins), GFP_KERNEL); + chip->pins = devm_kcalloc(pfc->dev, + pfc->info->nr_pins, sizeof(*chip->pins), + GFP_KERNEL); if (chip->pins == NULL) return -ENOMEM; diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 70db21638901..654dc20e171b 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -770,14 +770,14 @@ static int sh_pfc_map_pins(struct sh_pfc *pfc, struct sh_pfc_pinctrl *pmx) unsigned int i; /* Allocate and initialize the pins and configs arrays. */ - pmx->pins = devm_kzalloc(pfc->dev, - sizeof(*pmx->pins) * pfc->info->nr_pins, + pmx->pins = devm_kcalloc(pfc->dev, + pfc->info->nr_pins, sizeof(*pmx->pins), GFP_KERNEL); if (unlikely(!pmx->pins)) return -ENOMEM; - pmx->configs = devm_kzalloc(pfc->dev, - sizeof(*pmx->configs) * pfc->info->nr_pins, + pmx->configs = devm_kcalloc(pfc->dev, + pfc->info->nr_pins, sizeof(*pmx->configs), GFP_KERNEL); if (unlikely(!pmx->configs)) return -ENOMEM; diff --git a/drivers/pinctrl/spear/pinctrl-plgpio.c b/drivers/pinctrl/spear/pinctrl-plgpio.c index d2123e396b29..9d906474f3e4 100644 --- a/drivers/pinctrl/spear/pinctrl-plgpio.c +++ b/drivers/pinctrl/spear/pinctrl-plgpio.c @@ -538,9 +538,9 @@ static int plgpio_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "clk_get() failed, work without it\n"); #ifdef CONFIG_PM_SLEEP - plgpio->csave_regs = devm_kzalloc(&pdev->dev, - sizeof(*plgpio->csave_regs) * + plgpio->csave_regs = devm_kcalloc(&pdev->dev, DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG), + sizeof(*plgpio->csave_regs), GFP_KERNEL); if (!plgpio->csave_regs) return -ENOMEM; diff --git a/drivers/pinctrl/sprd/pinctrl-sprd.c b/drivers/pinctrl/sprd/pinctrl-sprd.c index ba1c2ca406e4..78c2f548b25f 100644 --- a/drivers/pinctrl/sprd/pinctrl-sprd.c +++ b/drivers/pinctrl/sprd/pinctrl-sprd.c @@ -879,8 +879,9 @@ static int sprd_pinctrl_parse_groups(struct device_node *np, grp->name = np->name; grp->npins = ret; - grp->pins = devm_kzalloc(sprd_pctl->dev, grp->npins * - sizeof(unsigned int), GFP_KERNEL); + grp->pins = devm_kcalloc(sprd_pctl->dev, + grp->npins, sizeof(unsigned int), + GFP_KERNEL); if (!grp->pins) return -ENOMEM; @@ -931,14 +932,15 @@ static int sprd_pinctrl_parse_dt(struct sprd_pinctrl *sprd_pctl) if (!info->ngroups) return 0; - info->groups = devm_kzalloc(sprd_pctl->dev, info->ngroups * + info->groups = devm_kcalloc(sprd_pctl->dev, + info->ngroups, sizeof(struct sprd_pin_group), GFP_KERNEL); if (!info->groups) return -ENOMEM; - info->grp_names = devm_kzalloc(sprd_pctl->dev, - info->ngroups * sizeof(char *), + info->grp_names = devm_kcalloc(sprd_pctl->dev, + info->ngroups, sizeof(char *), GFP_KERNEL); if (!info->grp_names) return -ENOMEM; @@ -980,8 +982,8 @@ static int sprd_pinctrl_add_pins(struct sprd_pinctrl *sprd_pctl, int i; info->npins = pins_cnt; - info->pins = devm_kzalloc(sprd_pctl->dev, - info->npins * sizeof(struct sprd_pin), + info->pins = devm_kcalloc(sprd_pctl->dev, + info->npins, sizeof(struct sprd_pin), GFP_KERNEL); if (!info->pins) return -ENOMEM; @@ -1057,7 +1059,8 @@ int sprd_pinctrl_core_probe(struct platform_device *pdev, return ret; } - pin_desc = devm_kzalloc(&pdev->dev, pinctrl_info->npins * + pin_desc = devm_kcalloc(&pdev->dev, + pinctrl_info->npins, sizeof(struct pinctrl_pin_desc), GFP_KERNEL); if (!pin_desc) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index eaace8ec6afc..4d9bf9b3e9f3 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -1055,8 +1055,8 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) * this means that the number of pins is the maximum group * number we will ever see. */ - pctl->groups = devm_kzalloc(&pdev->dev, - pctl->desc->npins * sizeof(*pctl->groups), + pctl->groups = devm_kcalloc(&pdev->dev, + pctl->desc->npins, sizeof(*pctl->groups), GFP_KERNEL); if (!pctl->groups) return -ENOMEM; @@ -1079,8 +1079,9 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) * We suppose that we won't have any more functions than pins, * we'll reallocate that later anyway */ - pctl->functions = devm_kzalloc(&pdev->dev, - pctl->ngroups * sizeof(*pctl->functions), + pctl->functions = devm_kcalloc(&pdev->dev, + pctl->ngroups, + sizeof(*pctl->functions), GFP_KERNEL); if (!pctl->functions) return -ENOMEM; @@ -1137,8 +1138,9 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) if (!func_item->groups) { func_item->groups = - devm_kzalloc(&pdev->dev, - func_item->ngroups * sizeof(*func_item->groups), + devm_kcalloc(&pdev->dev, + func_item->ngroups, + sizeof(*func_item->groups), GFP_KERNEL); if (!func_item->groups) return -ENOMEM; @@ -1281,8 +1283,8 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, return ret; } - pins = devm_kzalloc(&pdev->dev, - pctl->desc->npins * sizeof(*pins), + pins = devm_kcalloc(&pdev->dev, + pctl->desc->npins, sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index 49c7c1499bc3..f974eee29a19 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -665,8 +665,8 @@ int tegra_pinctrl_probe(struct platform_device *pdev, * Each mux group will appear in 4 functions' list of groups. * This over-allocates slightly, since not all groups are mux groups. */ - pmx->group_pins = devm_kzalloc(&pdev->dev, - soc_data->ngroups * 4 * sizeof(*pmx->group_pins), + pmx->group_pins = devm_kcalloc(&pdev->dev, + soc_data->ngroups * 4, sizeof(*pmx->group_pins), GFP_KERNEL); if (!pmx->group_pins) return -ENOMEM; @@ -708,7 +708,7 @@ int tegra_pinctrl_probe(struct platform_device *pdev, } pmx->nbanks = i; - pmx->regs = devm_kzalloc(&pdev->dev, pmx->nbanks * sizeof(*pmx->regs), + pmx->regs = devm_kcalloc(&pdev->dev, pmx->nbanks, sizeof(*pmx->regs), GFP_KERNEL); if (!pmx->regs) return -ENOMEM; diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c index a8a6510183b6..8782c348ebe9 100644 --- a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c @@ -510,11 +510,11 @@ static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, goto free_map; } - pins = devm_kzalloc(iod->dev, sizeof(*pins) * rows, GFP_KERNEL); + pins = devm_kcalloc(iod->dev, rows, sizeof(*pins), GFP_KERNEL); if (!pins) goto free_group; - cfg = devm_kzalloc(iod->dev, sizeof(*cfg) * rows, GFP_KERNEL); + cfg = devm_kcalloc(iod->dev, rows, sizeof(*cfg), GFP_KERNEL); if (!cfg) { error = -ENOMEM; goto free_pins; @@ -749,7 +749,7 @@ static int ti_iodelay_alloc_pins(struct device *dev, nr_pins = ti_iodelay_offset_to_pin(iod, r->regmap_config->max_register); dev_dbg(dev, "Allocating %i pins\n", nr_pins); - iod->pa = devm_kzalloc(dev, sizeof(*iod->pa) * nr_pins, GFP_KERNEL); + iod->pa = devm_kcalloc(dev, nr_pins, sizeof(*iod->pa), GFP_KERNEL); if (!iod->pa) return -ENOMEM; diff --git a/drivers/pinctrl/zte/pinctrl-zx.c b/drivers/pinctrl/zte/pinctrl-zx.c index ded366bb6564..caa44dd2880a 100644 --- a/drivers/pinctrl/zte/pinctrl-zx.c +++ b/drivers/pinctrl/zte/pinctrl-zx.c @@ -277,7 +277,7 @@ static int zx_pinctrl_build_state(struct platform_device *pdev) /* Every single pin composes a group */ ngroups = info->npins; - groups = devm_kzalloc(&pdev->dev, ngroups * sizeof(*groups), + groups = devm_kcalloc(&pdev->dev, ngroups, sizeof(*groups), GFP_KERNEL); if (!groups) return -ENOMEM; @@ -362,8 +362,8 @@ static int zx_pinctrl_build_state(struct platform_device *pdev) func = functions + j; if (!func->group_names) { - func->group_names = devm_kzalloc(&pdev->dev, - func->num_group_names * + func->group_names = devm_kcalloc(&pdev->dev, + func->num_group_names, sizeof(*func->group_names), GFP_KERNEL); if (!func->group_names) { diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index ea9e7f4479ca..36a41ff506f0 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -217,7 +217,8 @@ static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv) } } - priv->group.attrs = devm_kzalloc(&priv->pdev->dev, num_attrs * + priv->group.attrs = devm_kcalloc(&priv->pdev->dev, + num_attrs, sizeof(struct attribute *), GFP_KERNEL); if (!priv->group.attrs) diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 2a50b4654793..faa1a67cf3d2 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1380,7 +1380,7 @@ static int charger_manager_register_sysfs(struct charger_manager *cm) snprintf(buf, 10, "charger.%d", i); str = devm_kzalloc(cm->dev, - sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); + strlen(buf) + 1, GFP_KERNEL); if (!str) return -ENOMEM; @@ -1522,8 +1522,10 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev) of_property_read_u32(np, "cm-num-chargers", &num_chgs); if (num_chgs) { /* Allocate empty bin at the tail of array */ - desc->psy_charger_stat = devm_kzalloc(dev, sizeof(char *) - * (num_chgs + 1), GFP_KERNEL); + desc->psy_charger_stat = devm_kcalloc(dev, + num_chgs + 1, + sizeof(char *), + GFP_KERNEL); if (desc->psy_charger_stat) { int i; for (i = 0; i < num_chgs; i++) @@ -1555,8 +1557,9 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev) struct charger_regulator *chg_regs; struct device_node *child; - chg_regs = devm_kzalloc(dev, sizeof(*chg_regs) - * desc->num_charger_regulators, + chg_regs = devm_kcalloc(dev, + desc->num_charger_regulators, + sizeof(*chg_regs), GFP_KERNEL); if (!chg_regs) return ERR_PTR(-ENOMEM); @@ -1573,9 +1576,10 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev) /* charger cables */ chg_regs->num_cables = of_get_child_count(child); if (chg_regs->num_cables) { - cables = devm_kzalloc(dev, sizeof(*cables) - * chg_regs->num_cables, - GFP_KERNEL); + cables = devm_kcalloc(dev, + chg_regs->num_cables, + sizeof(*cables), + GFP_KERNEL); if (!cables) { of_node_put(child); return ERR_PTR(-ENOMEM); @@ -1725,10 +1729,11 @@ static int charger_manager_probe(struct platform_device *pdev) cm->charger_psy_desc.name = cm->psy_name_buf; /* Allocate for psy properties because they may vary */ - cm->charger_psy_desc.properties = devm_kzalloc(&pdev->dev, - sizeof(enum power_supply_property) - * (ARRAY_SIZE(default_charger_props) + - NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL); + cm->charger_psy_desc.properties = + devm_kcalloc(&pdev->dev, + ARRAY_SIZE(default_charger_props) + + NUM_CHARGER_PSY_OPTIONAL, + sizeof(enum power_supply_property), GFP_KERNEL); if (!cm->charger_psy_desc.properties) return -ENOMEM; diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index f57ab0a27301..d21f478741c1 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -263,8 +263,8 @@ static int power_supply_check_supplies(struct power_supply *psy) if (!psy->supplied_from) return -ENOMEM; - *psy->supplied_from = devm_kzalloc(&psy->dev, - sizeof(char *) * (cnt - 1), + *psy->supplied_from = devm_kcalloc(&psy->dev, + cnt - 1, sizeof(char *), GFP_KERNEL); if (!*psy->supplied_from) return -ENOMEM; diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c index 52584e9962ed..15b40a8bc4fb 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -225,7 +225,7 @@ static int lp3943_pwm_parse_dt(struct device *dev, if (num_outputs == 0) continue; - output = devm_kzalloc(dev, sizeof(*output) * num_outputs, + output = devm_kcalloc(dev, num_outputs, sizeof(*output), GFP_KERNEL); if (!output) return -ENOMEM; diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c index 7652477e6a9d..21e20483bd91 100644 --- a/drivers/regulator/act8865-regulator.c +++ b/drivers/regulator/act8865-regulator.c @@ -424,9 +424,10 @@ static int act8865_pdata_from_dt(struct device *dev, if (matched <= 0) return matched; - pdata->regulators = devm_kzalloc(dev, - sizeof(struct act8865_regulator_data) * - num_matches, GFP_KERNEL); + pdata->regulators = devm_kcalloc(dev, + num_matches, + sizeof(struct act8865_regulator_data), + GFP_KERNEL); if (!pdata->regulators) return -ENOMEM; diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index 874d415d6b4f..565a71343a8e 100644 --- a/drivers/regulator/as3711-regulator.c +++ b/drivers/regulator/as3711-regulator.c @@ -239,8 +239,10 @@ static int as3711_regulator_probe(struct platform_device *pdev) } } - regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM * - sizeof(struct as3711_regulator), GFP_KERNEL); + regs = devm_kcalloc(&pdev->dev, + AS3711_REGULATOR_NUM, + sizeof(struct as3711_regulator), + GFP_KERNEL); if (!regs) return -ENOMEM; diff --git a/drivers/regulator/bcm590xx-regulator.c b/drivers/regulator/bcm590xx-regulator.c index 9dd715407b39..92d6d7b10cf7 100644 --- a/drivers/regulator/bcm590xx-regulator.c +++ b/drivers/regulator/bcm590xx-regulator.c @@ -383,8 +383,10 @@ static int bcm590xx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmu); - pmu->desc = devm_kzalloc(&pdev->dev, BCM590XX_NUM_REGS * - sizeof(struct regulator_desc), GFP_KERNEL); + pmu->desc = devm_kcalloc(&pdev->dev, + BCM590XX_NUM_REGS, + sizeof(struct regulator_desc), + GFP_KERNEL); if (!pmu->desc) return -ENOMEM; diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index 6a8f9cd69f52..2df26f36c687 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -681,8 +681,8 @@ static struct da9063_regulators_pdata *da9063_parse_regulators_dt( if (!pdata) return ERR_PTR(-ENOMEM); - pdata->regulator_data = devm_kzalloc(&pdev->dev, - num * sizeof(*pdata->regulator_data), + pdata->regulator_data = devm_kcalloc(&pdev->dev, + num, sizeof(*pdata->regulator_data), GFP_KERNEL); if (!pdata->regulator_data) return ERR_PTR(-ENOMEM); diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index a86b8997bb54..b2f5ec4f658a 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -172,8 +172,8 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np, if (ret > 0) { config->nr_gpios = ret; - config->gpios = devm_kzalloc(dev, - sizeof(struct gpio) * config->nr_gpios, + config->gpios = devm_kcalloc(dev, + config->nr_gpios, sizeof(struct gpio), GFP_KERNEL); if (!config->gpios) return ERR_PTR(-ENOMEM); @@ -214,9 +214,9 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np, return ERR_PTR(-EINVAL); } - config->states = devm_kzalloc(dev, - sizeof(struct gpio_regulator_state) - * (proplen / 2), + config->states = devm_kcalloc(dev, + proplen / 2, + sizeof(struct gpio_regulator_state), GFP_KERNEL); if (!config->states) return ERR_PTR(-ENOMEM); diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 66bbaa999433..cc52779b53f7 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -194,8 +194,10 @@ static int of_get_max1586_platform_data(struct device *dev, if (matched <= 0) return matched; - pdata->subdevs = devm_kzalloc(dev, sizeof(struct max1586_subdev_data) * - matched, GFP_KERNEL); + pdata->subdevs = devm_kcalloc(dev, + matched, + sizeof(struct max1586_subdev_data), + GFP_KERNEL); if (!pdata->subdevs) return -ENOMEM; diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index a6183425f27d..4cf6897a401f 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -351,8 +351,10 @@ static int max8660_pdata_from_dt(struct device *dev, if (matched <= 0) return matched; - pdata->subdevs = devm_kzalloc(dev, sizeof(struct max8660_subdev_data) * - matched, GFP_KERNEL); + pdata->subdevs = devm_kcalloc(dev, + matched, + sizeof(struct max8660_subdev_data), + GFP_KERNEL); if (!pdata->subdevs) return -ENOMEM; diff --git a/drivers/regulator/max8997-regulator.c b/drivers/regulator/max8997-regulator.c index 559b9ac45404..a8ea30ee18a6 100644 --- a/drivers/regulator/max8997-regulator.c +++ b/drivers/regulator/max8997-regulator.c @@ -929,8 +929,9 @@ static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, /* count the number of regulators to be supported in pmic */ pdata->num_regulators = of_get_child_count(regulators_np); - rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * - pdata->num_regulators, GFP_KERNEL); + rdata = devm_kcalloc(&pdev->dev, + pdata->num_regulators, sizeof(*rdata), + GFP_KERNEL); if (!rdata) { of_node_put(regulators_np); return -ENOMEM; diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 6a2b61c012b5..6b9f262ebbb0 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -670,8 +670,9 @@ static int max8998_pmic_dt_parse_pdata(struct max8998_dev *iodev, /* count the number of regulators to be supported in pmic */ pdata->num_regulators = of_get_child_count(regulators_np); - rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * - pdata->num_regulators, GFP_KERNEL); + rdata = devm_kcalloc(iodev->dev, + pdata->num_regulators, sizeof(*rdata), + GFP_KERNEL); if (!rdata) { of_node_put(regulators_np); return -ENOMEM; diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 41271aeea63e..da4fb9824757 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -171,7 +171,7 @@ struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt( if (!parent) return NULL; - data = devm_kzalloc(&pdev->dev, sizeof(*data) * priv->num_regulators, + data = devm_kcalloc(&pdev->dev, priv->num_regulators, sizeof(*data), GFP_KERNEL); if (!data) { of_node_put(parent); diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c index 8f782d22fdbe..92b41a6a4dc2 100644 --- a/drivers/regulator/pbias-regulator.c +++ b/drivers/regulator/pbias-regulator.c @@ -173,8 +173,9 @@ static int pbias_regulator_probe(struct platform_device *pdev) if (count < 0) return count; - drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data) - * count, GFP_KERNEL); + drvdata = devm_kcalloc(&pdev->dev, + count, sizeof(struct pbias_regulator_data), + GFP_KERNEL); if (!drvdata) return -ENOMEM; diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c index d0f1340168b1..2ec51af43673 100644 --- a/drivers/regulator/rc5t583-regulator.c +++ b/drivers/regulator/rc5t583-regulator.c @@ -132,8 +132,10 @@ static int rc5t583_regulator_probe(struct platform_device *pdev) return -ENODEV; } - regs = devm_kzalloc(&pdev->dev, RC5T583_REGULATOR_MAX * - sizeof(struct rc5t583_regulator), GFP_KERNEL); + regs = devm_kcalloc(&pdev->dev, + RC5T583_REGULATOR_MAX, + sizeof(struct rc5t583_regulator), + GFP_KERNEL); if (!regs) return -ENOMEM; diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index b8443a360646..0cbc980753c2 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -553,13 +553,15 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, /* count the number of regulators to be supported in pmic */ pdata->num_regulators = of_get_child_count(regulators_np); - rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * - pdata->num_regulators, GFP_KERNEL); + rdata = devm_kcalloc(&pdev->dev, + pdata->num_regulators, sizeof(*rdata), + GFP_KERNEL); if (!rdata) return -ENOMEM; - rmode = devm_kzalloc(&pdev->dev, sizeof(*rmode) * - pdata->num_regulators, GFP_KERNEL); + rmode = devm_kcalloc(&pdev->dev, + pdata->num_regulators, sizeof(*rmode), + GFP_KERNEL); if (!rmode) return -ENOMEM; diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index d2f994298753..cced1ffb896c 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -532,13 +532,13 @@ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, } num_entries /= num_values; - info = devm_kzalloc(dev, sizeof(*info) * num_entries, GFP_KERNEL); + info = devm_kcalloc(dev, num_entries, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; abb->info = info; - volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries, + volt_table = devm_kcalloc(dev, num_entries, sizeof(unsigned int), GFP_KERNEL); if (!volt_table) return -ENOMEM; diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 2d398fa3b720..edaef9e4dc74 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -331,8 +331,9 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( if (!tps65090_pdata) return ERR_PTR(-ENOMEM); - reg_pdata = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * - sizeof(*reg_pdata), GFP_KERNEL); + reg_pdata = devm_kcalloc(&pdev->dev, + TPS65090_REGULATOR_MAX, sizeof(*reg_pdata), + GFP_KERNEL); if (!reg_pdata) return ERR_PTR(-ENOMEM); @@ -429,8 +430,9 @@ static int tps65090_regulator_probe(struct platform_device *pdev) return tps65090_pdata ? PTR_ERR(tps65090_pdata) : -EINVAL; } - pmic = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*pmic), - GFP_KERNEL); + pmic = devm_kcalloc(&pdev->dev, + TPS65090_REGULATOR_MAX, sizeof(*pmic), + GFP_KERNEL); if (!pmic) return -ENOMEM; diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 7b12e880d1ea..fc12badf3805 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -229,8 +229,9 @@ static int tps65217_regulator_probe(struct platform_device *pdev) unsigned int val; /* Allocate memory for strobes */ - tps->strobes = devm_kzalloc(&pdev->dev, sizeof(u8) * - TPS65217_NUM_REGULATOR, GFP_KERNEL); + tps->strobes = devm_kcalloc(&pdev->dev, + TPS65217_NUM_REGULATOR, sizeof(u8), + GFP_KERNEL); platform_set_drvdata(pdev, tps); diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c index 1827185beacc..6209beee1018 100644 --- a/drivers/regulator/tps65218-regulator.c +++ b/drivers/regulator/tps65218-regulator.c @@ -324,8 +324,9 @@ static int tps65218_regulator_probe(struct platform_device *pdev) config.regmap = tps->regmap; /* Allocate memory for strobes */ - tps->strobes = devm_kzalloc(&pdev->dev, sizeof(u8) * - TPS65218_NUM_REGULATOR, GFP_KERNEL); + tps->strobes = devm_kcalloc(&pdev->dev, + TPS65218_NUM_REGULATOR, sizeof(u8), + GFP_KERNEL); if (!tps->strobes) return -ENOMEM; diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 81672a58fcc2..02ccdaa226a7 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1131,18 +1131,24 @@ static int tps65910_probe(struct platform_device *pdev) return -ENODEV; } - pmic->desc = devm_kzalloc(&pdev->dev, pmic->num_regulators * - sizeof(struct regulator_desc), GFP_KERNEL); + pmic->desc = devm_kcalloc(&pdev->dev, + pmic->num_regulators, + sizeof(struct regulator_desc), + GFP_KERNEL); if (!pmic->desc) return -ENOMEM; - pmic->info = devm_kzalloc(&pdev->dev, pmic->num_regulators * - sizeof(struct tps_info *), GFP_KERNEL); + pmic->info = devm_kcalloc(&pdev->dev, + pmic->num_regulators, + sizeof(struct tps_info *), + GFP_KERNEL); if (!pmic->info) return -ENOMEM; - pmic->rdev = devm_kzalloc(&pdev->dev, pmic->num_regulators * - sizeof(struct regulator_dev *), GFP_KERNEL); + pmic->rdev = devm_kcalloc(&pdev->dev, + pmic->num_regulators, + sizeof(struct regulator_dev *), + GFP_KERNEL); if (!pmic->rdev) return -ENOMEM; diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c index d4cc60ad18ae..1001147404c3 100644 --- a/drivers/regulator/tps80031-regulator.c +++ b/drivers/regulator/tps80031-regulator.c @@ -691,8 +691,8 @@ static int tps80031_regulator_probe(struct platform_device *pdev) return -EINVAL; } - pmic = devm_kzalloc(&pdev->dev, - TPS80031_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL); + pmic = devm_kcalloc(&pdev->dev, + TPS80031_REGULATOR_MAX, sizeof(*pmic), GFP_KERNEL); if (!pmic) return -ENOMEM; diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c index 99520b0a1329..a2635c21db7f 100644 --- a/drivers/reset/reset-ti-syscon.c +++ b/drivers/reset/reset-ti-syscon.c @@ -189,7 +189,8 @@ static int ti_syscon_reset_probe(struct platform_device *pdev) } nr_controls = (size / sizeof(*list)) / 7; - controls = devm_kzalloc(dev, nr_controls * sizeof(*controls), GFP_KERNEL); + controls = devm_kcalloc(dev, nr_controls, sizeof(*controls), + GFP_KERNEL); if (!controls) return -ENOMEM; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 05cf4daf8788..08c7b1e25fe4 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -232,14 +232,14 @@ static int isci_register_sas_ha(struct isci_host *isci_host) struct asd_sas_phy **sas_phys; struct asd_sas_port **sas_ports; - sas_phys = devm_kzalloc(&isci_host->pdev->dev, - SCI_MAX_PHYS * sizeof(void *), + sas_phys = devm_kcalloc(&isci_host->pdev->dev, + SCI_MAX_PHYS, sizeof(void *), GFP_KERNEL); if (!sas_phys) return -ENOMEM; - sas_ports = devm_kzalloc(&isci_host->pdev->dev, - SCI_MAX_PORTS * sizeof(void *), + sas_ports = devm_kcalloc(&isci_host->pdev->dev, + SCI_MAX_PORTS, sizeof(void *), GFP_KERNEL); if (!sas_ports) return -ENOMEM; diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index e82bde077296..895a9b5ac989 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -86,8 +86,8 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) goto out; } - clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq), - GFP_KERNEL); + clkfreq = devm_kcalloc(dev, sz, sizeof(*clkfreq), + GFP_KERNEL); if (!clkfreq) { ret = -ENOMEM; goto out; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 3a811c5f70ba..397081d320b1 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3357,8 +3357,8 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba) } /* Allocate memory for local reference block */ - hba->lrb = devm_kzalloc(hba->dev, - hba->nutrs * sizeof(struct ufshcd_lrb), + hba->lrb = devm_kcalloc(hba->dev, + hba->nutrs, sizeof(struct ufshcd_lrb), GFP_KERNEL); if (!hba->lrb) { dev_err(hba->dev, "LRB Memory allocation failed\n"); diff --git a/drivers/soc/bcm/raspberrypi-power.c b/drivers/soc/bcm/raspberrypi-power.c index f7ed1187518b..a78dfe0a2b50 100644 --- a/drivers/soc/bcm/raspberrypi-power.c +++ b/drivers/soc/bcm/raspberrypi-power.c @@ -165,8 +165,10 @@ static int rpi_power_probe(struct platform_device *pdev) return -ENOMEM; rpi_domains->xlate.domains = - devm_kzalloc(dev, sizeof(*rpi_domains->xlate.domains) * - RPI_POWER_DOMAIN_COUNT, GFP_KERNEL); + devm_kcalloc(dev, + RPI_POWER_DOMAIN_COUNT, + sizeof(*rpi_domains->xlate.domains), + GFP_KERNEL); if (!rpi_domains->xlate.domains) return -ENOMEM; diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index d762a46d434f..d041d9852b23 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -407,15 +407,15 @@ static struct scp *init_scp(struct platform_device *pdev, if (IS_ERR(scp->base)) return ERR_CAST(scp->base); - scp->domains = devm_kzalloc(&pdev->dev, - sizeof(*scp->domains) * num, GFP_KERNEL); + scp->domains = devm_kcalloc(&pdev->dev, + num, sizeof(*scp->domains), GFP_KERNEL); if (!scp->domains) return ERR_PTR(-ENOMEM); pd_data = &scp->pd_data; - pd_data->domains = devm_kzalloc(&pdev->dev, - sizeof(*pd_data->domains) * num, GFP_KERNEL); + pd_data->domains = devm_kcalloc(&pdev->dev, + num, sizeof(*pd_data->domains), GFP_KERNEL); if (!pd_data->domains) return ERR_PTR(-ENOMEM); diff --git a/drivers/soc/ti/knav_qmss_acc.c b/drivers/soc/ti/knav_qmss_acc.c index 3d7225f4e77f..316e82e46f6c 100644 --- a/drivers/soc/ti/knav_qmss_acc.c +++ b/drivers/soc/ti/knav_qmss_acc.c @@ -405,8 +405,8 @@ static int knav_acc_init_queue(struct knav_range_info *range, { unsigned id = kq->id - range->queue_base; - kq->descs = devm_kzalloc(range->kdev->dev, - ACC_DESCS_MAX * sizeof(u32), GFP_KERNEL); + kq->descs = devm_kcalloc(range->kdev->dev, + ACC_DESCS_MAX, sizeof(u32), GFP_KERNEL); if (!kq->descs) return -ENOMEM; @@ -552,7 +552,7 @@ int knav_init_acc_range(struct knav_device *kdev, info->list_size = list_size; mem_size = PAGE_ALIGN(list_size * 2); info->mem_size = mem_size; - range->acc = devm_kzalloc(kdev->dev, channels * sizeof(*range->acc), + range->acc = devm_kcalloc(kdev->dev, channels, sizeof(*range->acc), GFP_KERNEL); if (!range->acc) return -ENOMEM; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 60d59b003aa4..577084bb911b 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -923,9 +923,10 @@ static int davinci_spi_probe(struct platform_device *pdev) /* pdata in dspi is now updated and point pdata to that */ pdata = &dspi->pdata; - dspi->bytes_per_word = devm_kzalloc(&pdev->dev, - sizeof(*dspi->bytes_per_word) * - pdata->num_chipselect, GFP_KERNEL); + dspi->bytes_per_word = devm_kcalloc(&pdev->dev, + pdata->num_chipselect, + sizeof(*dspi->bytes_per_word), + GFP_KERNEL); if (dspi->bytes_per_word == NULL) { ret = -ENOMEM; goto free_master; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index e5cc07357746..f1526757aaf6 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -671,8 +671,8 @@ static int ep93xx_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); master->num_chipselect = info->num_chipselect; - master->cs_gpios = devm_kzalloc(&master->dev, - sizeof(int) * master->num_chipselect, + master->cs_gpios = devm_kcalloc(&master->dev, + master->num_chipselect, sizeof(int), GFP_KERNEL); if (!master->cs_gpios) { error = -ENOMEM; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index b85a93cad44a..6ae92d4dca19 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -373,8 +373,9 @@ static int spi_gpio_probe(struct platform_device *pdev) spi_gpio = spi_master_get_devdata(master); - spi_gpio->cs_gpios = devm_kzalloc(&pdev->dev, - pdata->num_chipselect * sizeof(*spi_gpio->cs_gpios), + spi_gpio->cs_gpios = devm_kcalloc(&pdev->dev, + pdata->num_chipselect, + sizeof(*spi_gpio->cs_gpios), GFP_KERNEL); if (!spi_gpio->cs_gpios) return -ENOMEM; diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 866246f21041..d3b21faf6b1f 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1511,8 +1511,9 @@ static int spi_imx_probe(struct platform_device *pdev) if (mxc_platform_info) { master->num_chipselect = mxc_platform_info->num_chipselect; if (mxc_platform_info->chipselect) { - master->cs_gpios = devm_kzalloc(&master->dev, - sizeof(int) * master->num_chipselect, GFP_KERNEL); + master->cs_gpios = devm_kcalloc(&master->dev, + master->num_chipselect, sizeof(int), + GFP_KERNEL); if (!master->cs_gpios) return -ENOMEM; diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index b5911282a611..085f580be7ec 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -213,8 +213,8 @@ static int tiny_spi_of_probe(struct platform_device *pdev) return 0; hw->gpio_cs_count = of_gpio_count(np); if (hw->gpio_cs_count > 0) { - hw->gpio_cs = devm_kzalloc(&pdev->dev, - hw->gpio_cs_count * sizeof(unsigned int), + hw->gpio_cs = devm_kcalloc(&pdev->dev, + hw->gpio_cs_count, sizeof(unsigned int), GFP_KERNEL); if (!hw->gpio_cs) return -ENOMEM; diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 4797c57f4263..1af8c96b940e 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2135,7 +2135,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->master_info = platform_info; pl022->adev = adev; pl022->vendor = id->data; - pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int), + pl022->chipselects = devm_kcalloc(dev, num_cs, sizeof(int), GFP_KERNEL); if (!pl022->chipselects) { status = -ENOMEM; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index efc624f9e490..ec395a6baf9c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2049,7 +2049,7 @@ static int of_spi_register_master(struct spi_controller *ctlr) else if (nb < 0) return nb; - cs = devm_kzalloc(&ctlr->dev, sizeof(int) * ctlr->num_chipselect, + cs = devm_kcalloc(&ctlr->dev, ctlr->num_chipselect, sizeof(int), GFP_KERNEL); ctlr->cs_gpios = cs; diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 15e57f701630..b71078339e86 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -144,7 +144,7 @@ static const char **gb_generate_enum_strings(struct gbaudio_module_info *gb, __u8 *data; items = le32_to_cpu(gbenum->items); - strings = devm_kzalloc(gb->dev, sizeof(char *) * items, GFP_KERNEL); + strings = devm_kcalloc(gb->dev, items, sizeof(char *), GFP_KERNEL); data = gbenum->names; for (i = 0; i < items; i++) { diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 289d775c4820..b0be80f05767 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -303,9 +303,9 @@ static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd) list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { entity = &sd->entity; - vdev_lists = devm_kzalloc( + vdev_lists = devm_kcalloc( imxmd->md.dev, - entity->num_pads * sizeof(*vdev_lists), + entity->num_pads, sizeof(*vdev_lists), GFP_KERNEL); if (!vdev_lists) return -ENOMEM; @@ -544,7 +544,7 @@ static int imx_media_probe(struct platform_device *pdev) goto unreg_dev; } - subdevs = devm_kzalloc(imxmd->md.dev, sizeof(*subdevs) * num_subdevs, + subdevs = devm_kcalloc(imxmd->md.dev, num_subdevs, sizeof(*subdevs), GFP_KERNEL); if (!subdevs) { ret = -ENOMEM; diff --git a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c index 04b1a0950387..0c3e498ae99c 100644 --- a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c +++ b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c @@ -287,7 +287,8 @@ static int rt2880_pinmux_index(struct rt2880_priv *p) } /* allocate the group names array needed by the gpio function */ - p->group_names = devm_kzalloc(p->dev, sizeof(char *) * p->group_count, GFP_KERNEL); + p->group_names = devm_kcalloc(p->dev, p->group_count, sizeof(char *), + GFP_KERNEL); if (!p->group_names) return -1; @@ -300,8 +301,12 @@ static int rt2880_pinmux_index(struct rt2880_priv *p) p->func_count++; /* allocate our function and group mapping index buffers */ - f = p->func = devm_kzalloc(p->dev, sizeof(struct rt2880_pmx_func) * p->func_count, GFP_KERNEL); - gpio_func.groups = devm_kzalloc(p->dev, sizeof(int) * p->group_count, GFP_KERNEL); + f = p->func = devm_kcalloc(p->dev, + p->func_count, + sizeof(struct rt2880_pmx_func), + GFP_KERNEL); + gpio_func.groups = devm_kcalloc(p->dev, p->group_count, sizeof(int), + GFP_KERNEL); if (!f || !gpio_func.groups) return -1; @@ -337,7 +342,10 @@ static int rt2880_pinmux_pins(struct rt2880_priv *p) if (!p->func[i]->pin_count) continue; - p->func[i]->pins = devm_kzalloc(p->dev, sizeof(int) * p->func[i]->pin_count, GFP_KERNEL); + p->func[i]->pins = devm_kcalloc(p->dev, + p->func[i]->pin_count, + sizeof(int), + GFP_KERNEL); for (j = 0; j < p->func[i]->pin_count; j++) p->func[i]->pins[j] = p->func[i]->pin_first + j; @@ -347,11 +355,11 @@ static int rt2880_pinmux_pins(struct rt2880_priv *p) } /* the buffer that tells us which pins are gpio */ - p->gpio = devm_kzalloc(p->dev,sizeof(uint8_t) * p->max_pins, - GFP_KERNEL); + p->gpio = devm_kcalloc(p->dev,p->max_pins, sizeof(uint8_t), + GFP_KERNEL); /* the pads needed to tell pinctrl about our pins */ - p->pads = devm_kzalloc(p->dev, - sizeof(struct pinctrl_pin_desc) * p->max_pins, + p->pads = devm_kcalloc(p->dev, + p->max_pins, sizeof(struct pinctrl_pin_desc), GFP_KERNEL); if (!p->pads || !p->gpio ) { dev_err(p->dev, "Failed to allocate gpio data\n"); diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 455b58ce2652..e1fc2b06f343 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1343,8 +1343,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) return PTR_ERR(tegra->clock_soctherm); } - tegra->calib = devm_kzalloc(&pdev->dev, - sizeof(u32) * soc->num_tsensors, + tegra->calib = devm_kcalloc(&pdev->dev, + soc->num_tsensors, sizeof(u32), GFP_KERNEL); if (!tegra->calib) return -ENOMEM; @@ -1363,8 +1363,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) return err; } - tegra->thermctl_tzs = devm_kzalloc(&pdev->dev, - sizeof(*z) * soc->num_ttgs, + tegra->thermctl_tzs = devm_kcalloc(&pdev->dev, + soc->num_ttgs, sizeof(*z), GFP_KERNEL); if (!tegra->thermctl_tzs) return -ENOMEM; diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 46d3005335c7..bf1c628d4a7a 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -87,8 +87,9 @@ static int gadc_thermal_read_linear_lookup_table(struct device *dev, return -EINVAL; } - gti->lookup_table = devm_kzalloc(dev, sizeof(*gti->lookup_table) * - ntable, GFP_KERNEL); + gti->lookup_table = devm_kcalloc(dev, + ntable, sizeof(*gti->lookup_table), + GFP_KERNEL); if (!gti->lookup_table) return -ENOMEM; diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 520b43b23543..5690c09cc041 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -774,7 +774,7 @@ static int rp2_probe(struct pci_dev *pdev, rp2_init_card(card); - ports = devm_kzalloc(&pdev->dev, sizeof(*ports) * card->n_ports, + ports = devm_kcalloc(&pdev->dev, card->n_ports, sizeof(*ports), GFP_KERNEL); if (!ports) return -ENOMEM; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index a4d99bf50f2f..17147b8c771e 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2036,7 +2036,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, udc->num_ep = usba_config_fifo_table(udc); } - eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * udc->num_ep, + eps = devm_kcalloc(&pdev->dev, udc->num_ep, sizeof(struct usba_ep), GFP_KERNEL); if (!eps) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 977ea1a02cf9..7cf98c793e04 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2427,7 +2427,8 @@ static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev, if (usb3->num_usb3_eps > USB3_MAX_NUM_PIPES) usb3->num_usb3_eps = USB3_MAX_NUM_PIPES; - usb3->usb3_ep = devm_kzalloc(dev, sizeof(*usb3_ep) * usb3->num_usb3_eps, + usb3->usb3_ep = devm_kcalloc(dev, + usb3->num_usb3_eps, sizeof(*usb3_ep), GFP_KERNEL); if (!usb3->usb3_ep) return -ENOMEM; diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index e7315bf14d60..16119bde9750 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -223,7 +223,7 @@ static int adp8860_led_probe(struct i2c_client *client) struct led_info *cur_led; int ret, i; - led = devm_kzalloc(&client->dev, sizeof(*led) * pdata->num_leds, + led = devm_kcalloc(&client->dev, pdata->num_leds, sizeof(*led), GFP_KERNEL); if (led == NULL) return -ENOMEM; diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c index 058d1def2d1f..4fec9aa92d9b 100644 --- a/drivers/video/backlight/adp8870_bl.c +++ b/drivers/video/backlight/adp8870_bl.c @@ -246,7 +246,7 @@ static int adp8870_led_probe(struct i2c_client *client) struct led_info *cur_led; int ret, i; - led = devm_kzalloc(&client->dev, pdata->num_leds * sizeof(*led), + led = devm_kcalloc(&client->dev, pdata->num_leds, sizeof(*led), GFP_KERNEL); if (led == NULL) return -ENOMEM; diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 939f057836e1..73612485ed07 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -374,7 +374,7 @@ static int lp855x_parse_dt(struct lp855x *lp) struct device_node *child; int i = 0; - rom = devm_kzalloc(dev, sizeof(*rom) * rom_length, GFP_KERNEL); + rom = devm_kcalloc(dev, rom_length, sizeof(*rom), GFP_KERNEL); if (!rom) return -ENOMEM; diff --git a/drivers/video/fbdev/au1100fb.c b/drivers/video/fbdev/au1100fb.c index 7c9a672e9811..d555a78df5c6 100644 --- a/drivers/video/fbdev/au1100fb.c +++ b/drivers/video/fbdev/au1100fb.c @@ -501,7 +501,7 @@ static int au1100fb_drv_probe(struct platform_device *dev) fbdev->info.fix = au1100fb_fix; fbdev->info.pseudo_palette = - devm_kzalloc(&dev->dev, sizeof(u32) * 16, GFP_KERNEL); + devm_kcalloc(&dev->dev, 16, sizeof(u32), GFP_KERNEL); if (!fbdev->info.pseudo_palette) return -ENOMEM; diff --git a/drivers/video/fbdev/mxsfb.c b/drivers/video/fbdev/mxsfb.c index 246bea3a7d9b..12c8bd1d24d5 100644 --- a/drivers/video/fbdev/mxsfb.c +++ b/drivers/video/fbdev/mxsfb.c @@ -931,7 +931,7 @@ static int mxsfb_probe(struct platform_device *pdev) if (IS_ERR(host->reg_lcd)) host->reg_lcd = NULL; - fb_info->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) * 16, + fb_info->pseudo_palette = devm_kcalloc(&pdev->dev, 16, sizeof(u32), GFP_KERNEL); if (!fb_info->pseudo_palette) { ret = -ENOMEM; diff --git a/drivers/video/fbdev/omap2/omapfb/vrfb.c b/drivers/video/fbdev/omap2/omapfb/vrfb.c index f346b02eee1d..f355ecfac3b1 100644 --- a/drivers/video/fbdev/omap2/omapfb/vrfb.c +++ b/drivers/video/fbdev/omap2/omapfb/vrfb.c @@ -359,8 +359,8 @@ static int __init vrfb_probe(struct platform_device *pdev) num_ctxs = pdev->num_resources - 1; - ctxs = devm_kzalloc(&pdev->dev, - sizeof(struct vrfb_ctx) * num_ctxs, + ctxs = devm_kcalloc(&pdev->dev, + num_ctxs, sizeof(struct vrfb_ctx), GFP_KERNEL); if (!ctxs) diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index fb650659c3a3..a906560d0cdd 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -339,8 +339,8 @@ static int au1xpsc_pcm_drvprobe(struct platform_device *pdev) { struct au1xpsc_audio_dmadata *dmadata; - dmadata = devm_kzalloc(&pdev->dev, - 2 * sizeof(struct au1xpsc_audio_dmadata), + dmadata = devm_kcalloc(&pdev->dev, + 2, sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL); if (!dmadata) return -ENOMEM; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 6fa11888672d..38e4a8515709 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -771,7 +771,7 @@ static int hdmi_codec_probe(struct platform_device *pdev) hcp->hcd = *hcd; mutex_init(&hcp->current_stream_lock); - hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv), + hcp->daidrv = devm_kcalloc(dev, dai_count, sizeof(*hcp->daidrv), GFP_KERNEL); if (!hcp->daidrv) return -ENOMEM; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 712384581ebf..1dc70f452c1b 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3449,8 +3449,9 @@ static int rt5645_probe(struct snd_soc_component *component) if (rt5645->pdata.long_name) component->card->long_name = rt5645->pdata.long_name; - rt5645->eq_param = devm_kzalloc(component->dev, - RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL); + rt5645->eq_param = devm_kcalloc(component->dev, + RT5645_HWEQ_NUM, sizeof(struct rt5645_eq_param_s), + GFP_KERNEL); return 0; } diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 6e9e32a07259..7fdfdf3f6e67 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3298,8 +3298,8 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) }; /* We need an array of texts for the enum API */ - wm8994->drc_texts = devm_kzalloc(wm8994->hubs.component->dev, - sizeof(char *) * pdata->num_drc_cfgs, GFP_KERNEL); + wm8994->drc_texts = devm_kcalloc(wm8994->hubs.component->dev, + pdata->num_drc_cfgs, sizeof(char *), GFP_KERNEL); if (!wm8994->drc_texts) return; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 1f96c9dbe9c4..47c0c821d325 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1868,8 +1868,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->num_serializer = pdata->num_serializer; #ifdef CONFIG_PM_SLEEP - mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev, - sizeof(u32) * mcasp->num_serializer, + mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev, + mcasp->num_serializer, sizeof(u32), GFP_KERNEL); if (!mcasp->context.xrsr_regs) { ret = -ENOMEM; @@ -2004,13 +2004,15 @@ static int davinci_mcasp_probe(struct platform_device *pdev) * bytes. */ mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list = - devm_kzalloc(mcasp->dev, sizeof(unsigned int) * - (32 + mcasp->num_serializer - 1), + devm_kcalloc(mcasp->dev, + 32 + mcasp->num_serializer - 1, + sizeof(unsigned int), GFP_KERNEL); mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list = - devm_kzalloc(mcasp->dev, sizeof(unsigned int) * - (32 + mcasp->num_serializer - 1), + devm_kcalloc(mcasp->dev, + 32 + mcasp->num_serializer - 1, + sizeof(unsigned int), GFP_KERNEL); if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 1b6164249341..d93bacacbd5b 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -296,8 +296,8 @@ static int asoc_graph_card_probe(struct platform_device *pdev) if (num == 0) return -EINVAL; - dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); - dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); if (!dai_props || !dai_link) return -ENOMEM; diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index a967aa143d51..095ef6426d42 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -348,8 +348,8 @@ static int asoc_graph_card_probe(struct platform_device *pdev) if (num == 0) return -EINVAL; - dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); - dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); if (!dai_props || !dai_link) return -ENOMEM; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 4a516c428b3d..8b374af86a6e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -340,8 +340,8 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node, if (n <= 0) return -EINVAL; - card->aux_dev = devm_kzalloc(dev, - n * sizeof(*card->aux_dev), GFP_KERNEL); + card->aux_dev = devm_kcalloc(dev, + n, sizeof(*card->aux_dev), GFP_KERNEL); if (!card->aux_dev) return -ENOMEM; @@ -435,8 +435,8 @@ static int asoc_simple_card_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); - dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); if (!dai_props || !dai_link) return -ENOMEM; diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 48606c63562a..487716559deb 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -246,8 +246,8 @@ static int asoc_simple_card_probe(struct platform_device *pdev) num = of_get_child_count(np); - dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); - dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); if (!dai_props || !dai_link) return -ENOMEM; diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index d7fbb0a0a28b..388cefd7340a 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -509,8 +509,8 @@ static int img_i2s_in_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); - i2s->suspend_ch_ctl = devm_kzalloc(dev, - sizeof(*i2s->suspend_ch_ctl) * i2s->max_i2s_chan, GFP_KERNEL); + i2s->suspend_ch_ctl = devm_kcalloc(dev, + i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL); if (!i2s->suspend_ch_ctl) { ret = -ENOMEM; goto err_suspend; diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 30a95bcef2db..fc2d1dac6333 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -479,8 +479,8 @@ static int img_i2s_out_probe(struct platform_device *pdev) return PTR_ERR(i2s->clk_ref); } - i2s->suspend_ch_ctl = devm_kzalloc(dev, - sizeof(*i2s->suspend_ch_ctl) * i2s->max_i2s_chan, GFP_KERNEL); + i2s->suspend_ch_ctl = devm_kcalloc(dev, + i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL); if (!i2s->suspend_ch_ctl) return -ENOMEM; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 2c5129782959..fcdc716754b6 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2428,8 +2428,10 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U8_DYN_IN_PIN: if (!mconfig->m_in_pin) - mconfig->m_in_pin = devm_kzalloc(dev, MAX_IN_QUEUE * - sizeof(*mconfig->m_in_pin), GFP_KERNEL); + mconfig->m_in_pin = + devm_kcalloc(dev, MAX_IN_QUEUE, + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); if (!mconfig->m_in_pin) return -ENOMEM; @@ -2439,8 +2441,10 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U8_DYN_OUT_PIN: if (!mconfig->m_out_pin) - mconfig->m_out_pin = devm_kzalloc(dev, MAX_IN_QUEUE * - sizeof(*mconfig->m_in_pin), GFP_KERNEL); + mconfig->m_out_pin = + devm_kcalloc(dev, MAX_IN_QUEUE, + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); if (!mconfig->m_out_pin) return -ENOMEM; @@ -2852,14 +2856,14 @@ static int skl_tplg_get_pvt_data_v4(struct snd_soc_tplg_dapm_widget *tplg_w, mconfig->time_slot = dfw->time_slot; mconfig->formats_config.caps_size = dfw->caps.caps_size; - mconfig->m_in_pin = devm_kzalloc(dev, - MAX_IN_QUEUE * sizeof(*mconfig->m_in_pin), + mconfig->m_in_pin = devm_kcalloc(dev, + MAX_IN_QUEUE, sizeof(*mconfig->m_in_pin), GFP_KERNEL); if (!mconfig->m_in_pin) return -ENOMEM; - mconfig->m_out_pin = devm_kzalloc(dev, - MAX_OUT_QUEUE * sizeof(*mconfig->m_out_pin), + mconfig->m_out_pin = devm_kcalloc(dev, + MAX_OUT_QUEUE, sizeof(*mconfig->m_out_pin), GFP_KERNEL); if (!mconfig->m_out_pin) return -ENOMEM; diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index 828d11c30c6a..968fba4d7533 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -1347,7 +1347,8 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) afe->dev = &pdev->dev; dev = afe->dev; - afe_priv->i2s_path = devm_kzalloc(dev, afe_priv->soc->i2s_num * + afe_priv->i2s_path = devm_kcalloc(dev, + afe_priv->soc->i2s_num, sizeof(struct mt2701_i2s_path), GFP_KERNEL); if (!afe_priv->i2s_path) diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index 7c998ea4ebee..12d4513ebe8a 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -425,8 +425,8 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev) if (priv->sspa == NULL) return -ENOMEM; - priv->dma_params = devm_kzalloc(&pdev->dev, - 2 * sizeof(struct snd_dmaengine_dai_dma_data), + priv->dma_params = devm_kcalloc(&pdev->dev, + 2, sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (priv->dma_params == NULL) return -ENOMEM; diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index f184168f9a41..f2a51ae2b674 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -462,7 +462,7 @@ static int rockchip_sound_of_parse_dais(struct device *dev, num_routes = 0; for (i = 0; i < ARRAY_SIZE(rockchip_routes); i++) num_routes += rockchip_routes[i].num_routes; - routes = devm_kzalloc(dev, num_routes * sizeof(*routes), + routes = devm_kcalloc(dev, num_routes, sizeof(*routes), GFP_KERNEL); if (!routes) return -ENOMEM; diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index 4221937ae79b..5900fb535a2b 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -155,7 +155,7 @@ int rsnd_cmd_probe(struct rsnd_priv *priv) if (!nr) return 0; - cmd = devm_kzalloc(dev, sizeof(*cmd) * nr, GFP_KERNEL); + cmd = devm_kcalloc(dev, nr, sizeof(*cmd), GFP_KERNEL); if (!cmd) return -ENOMEM; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index af04d41a4274..f237002180c0 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1110,8 +1110,8 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) if (!nr) return -EINVAL; - rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL); - rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL); + rdrv = devm_kcalloc(dev, nr, sizeof(*rdrv), GFP_KERNEL); + rdai = devm_kcalloc(dev, nr, sizeof(*rdai), GFP_KERNEL); if (!rdrv || !rdai) return -ENOMEM; diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index d201d551866d..83be7d3ae0a8 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -378,7 +378,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) goto rsnd_ctu_probe_done; } - ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL); + ctu = devm_kcalloc(dev, nr, sizeof(*ctu), GFP_KERNEL); if (!ctu) { ret = -ENOMEM; goto rsnd_ctu_probe_done; diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index dbe54f024d68..ca1780e0b830 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -344,7 +344,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) goto rsnd_dvc_probe_done; } - dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); + dvc = devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL); if (!dvc) { ret = -ENOMEM; goto rsnd_dvc_probe_done; diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 7998380766f6..1881b2de9126 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -294,7 +294,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv) goto rsnd_mix_probe_done; } - mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL); + mix = devm_kcalloc(dev, nr, sizeof(*mix), GFP_KERNEL); if (!mix) { ret = -ENOMEM; goto rsnd_mix_probe_done; diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index a727e71587b6..6c72d1a81cf5 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -575,7 +575,7 @@ int rsnd_src_probe(struct rsnd_priv *priv) goto rsnd_src_probe_done; } - src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); + src = devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL); if (!src) { ret = -ENOMEM; goto rsnd_src_probe_done; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 9538f76f8e20..6e1166ec24a0 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -1116,7 +1116,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) goto rsnd_ssi_probe_done; } - ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); + ssi = devm_kcalloc(dev, nr, sizeof(*ssi), GFP_KERNEL); if (!ssi) { ret = -ENOMEM; goto rsnd_ssi_probe_done; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 6ff8a36c2c82..47bdba9fc582 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -258,7 +258,7 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) /* same number to SSI */ nr = priv->ssi_nr; - ssiu = devm_kzalloc(dev, sizeof(*ssiu) * nr, GFP_KERNEL); + ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL); if (!ssiu) return -ENOMEM; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 61542847cb3b..4663de3cf495 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3354,7 +3354,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } - routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes), + routes = devm_kcalloc(card->dev, num_routes, sizeof(*routes), GFP_KERNEL); if (!routes) { dev_err(card->dev, @@ -3678,8 +3678,8 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev, dev_err(dev, "Bad phandle in 'sound-dai'\n"); return num_codecs; } - component = devm_kzalloc(dev, - sizeof *component * num_codecs, + component = devm_kcalloc(dev, + num_codecs, sizeof(*component), GFP_KERNEL); if (!component) return -ENOMEM; diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 80daec17be25..2d9b7dde2ffa 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -624,15 +624,17 @@ int uniphier_aio_probe(struct platform_device *pdev) return PTR_ERR(chip->rst); chip->num_aios = chip->chip_spec->num_dais; - chip->aios = devm_kzalloc(dev, - sizeof(struct uniphier_aio) * chip->num_aios, + chip->aios = devm_kcalloc(dev, + chip->num_aios, sizeof(struct uniphier_aio), GFP_KERNEL); if (!chip->aios) return -ENOMEM; chip->num_plls = chip->chip_spec->num_plls; - chip->plls = devm_kzalloc(dev, sizeof(struct uniphier_aio_pll) * - chip->num_plls, GFP_KERNEL); + chip->plls = devm_kcalloc(dev, + chip->num_plls, + sizeof(struct uniphier_aio_pll), + GFP_KERNEL); if (!chip->plls) return -ENOMEM; memcpy(chip->plls, chip->chip_spec->plls, -- cgit v1.2.3