From nobody Mon Dec 1 22:37:06 2025 Received: from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3D33428750C; Mon, 1 Dec 2025 06:29:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764570580; cv=none; b=bgmEbRh5+Jy3NO9YCZ+ZgpDmt5+jq0oYm80tHi78t4fgSjxayMGFJlP/S7whTke51ep89P4vvnnOixBfAmzcVhazT/jLDtpDU4T8lN42mimkQVgP2hlwncJbfq89svZBFHt7LtIB7IJqP+BnTBal6qCei2xLJiw8ve4rL/92UN8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764570580; c=relaxed/simple; bh=7+tkGR0cuVTr7xmSym8kVr4c6ZMKrUoxvW2GxX8ay6g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=BMxklKNlwqEpfRtzvgMQWuEQ+HKwMyrKf5NV6yrRxWlwudEFjczCR2qBK613Yay8oZ308/daK117txeB6R7XUdnkrUa9tMFd1OQoM1L8exTe1m0JCDt5RJDCSEQGrnPTMja5bTS+56Yndq0Ef+bGATEw/kclihIyHIcv+vcPbuE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Mon, 1 Dec 2025 14:29:17 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Mon, 1 Dec 2025 14:29:17 +0800 From: Jacky Chou Date: Mon, 1 Dec 2025 14:29:16 +0800 Subject: [PATCH v6 6/7] PCI: aspeed: Add ASPEED PCIe RC driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20251201-upstream_pcie_rc-v6-6-8c8800c56b16@aspeedtech.com> References: <20251201-upstream_pcie_rc-v6-0-8c8800c56b16@aspeedtech.com> In-Reply-To: <20251201-upstream_pcie_rc-v6-0-8c8800c56b16@aspeedtech.com> To: Vinod Koul , Kishon Vijay Abraham I , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , "Andrew Jeffery" , Bjorn Helgaas , Lorenzo Pieralisi , =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= , "Manivannan Sadhasivam" , Linus Walleij , Philipp Zabel CC: , , , , , , Andrew Jeffery , , , Jacky Chou X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1764570556; l=36332; i=jacky_chou@aspeedtech.com; s=20251031; h=from:subject:message-id; bh=7+tkGR0cuVTr7xmSym8kVr4c6ZMKrUoxvW2GxX8ay6g=; b=S3DQNZyqUYc2ogkTor4GGafEuhtc208er33VvpN3P+H8QkmzzoNoQTeVkPwMIowWRAhDHbij5 9/V9JXG3AGECUm9aMKjGIFVKr9rRk+6yZ1eN0mwAlEq7mWBIvFEM6qB X-Developer-Key: i=jacky_chou@aspeedtech.com; a=ed25519; pk=8XBx7KFM1drEsfCXTH9QC2lbMlGU4XwJTA6Jt9Mabdo= Introduce PCIe Root Complex driver for ASPEED SoCs. Support RC initialization, reset, clock, IRQ domain, and MSI domain setup. Implement platform-specific setup and register configuration for ASPEED. And provide PCI config space read/write and INTx/MSI interrupt handling. Signed-off-by: Jacky Chou --- drivers/pci/controller/Kconfig | 16 + drivers/pci/controller/Makefile | 1 + drivers/pci/controller/pcie-aspeed.c | 1117 ++++++++++++++++++++++++++++++= ++++ 3 files changed, 1134 insertions(+) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 41748d083b93..270e332f79ae 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -58,6 +58,22 @@ config PCI_VERSATILE bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE || COMPILE_TEST =20 +config PCIE_ASPEED + bool "ASPEED PCIe controller" + depends on ARCH_ASPEED || COMPILE_TEST + depends on OF + depends on PCI_MSI + select IRQ_MSI_LIB + help + Enable this option to support the PCIe controller found on ASPEED + SoCs. + + This driver provides initialization and management for PCIe + Root Complex functionality, including INTx and MSI support. + + Select Y if your platform uses an ASPEED SoC and requires PCIe + connectivity. + config PCIE_BRCMSTB tristate "Broadcom Brcmstb PCIe controller" depends on ARCH_BRCMSTB || ARCH_BCM2835 || ARCH_BCMBCA || \ diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makef= ile index 038ccbd9e3ba..1339f88e153d 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_LOONGSON) +=3D pci-loongson.o obj-$(CONFIG_PCIE_HISI_ERR) +=3D pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) +=3D pcie-apple.o obj-$(CONFIG_PCIE_MT7621) +=3D pcie-mt7621.o +obj-$(CONFIG_PCIE_ASPEED) +=3D pcie-aspeed.o =20 # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y +=3D dwc/ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/= pcie-aspeed.c new file mode 100644 index 000000000000..147be4d3cb93 --- /dev/null +++ b/drivers/pci/controller/pcie-aspeed.c @@ -0,0 +1,1117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 Aspeed Technology Inc. + */ +#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 MAX_MSI_HOST_IRQS 64 +#define ASPEED_RESET_RC_WAIT_MS 10 + +/* AST2600 AHBC Registers */ +#define ASPEED_AHBC_KEY 0x00 +#define ASPEED_AHBC_UNLOCK_KEY 0xaeed1a03 +#define ASPEED_AHBC_UNLOCK 0x01 +#define ASPEED_AHBC_ADDR_MAPPING 0x8c +#define ASPEED_PCIE_RC_MEMORY_EN BIT(5) + +/* AST2600 H2X Controller Registers */ +#define ASPEED_H2X_INT_STS 0x08 +#define ASPEED_PCIE_TX_IDLE_CLEAR BIT(0) +#define ASPEED_PCIE_INTX_STS GENMASK(3, 0) +#define ASPEED_H2X_HOST_RX_DESC_DATA 0x0c +#define ASPEED_H2X_TX_DESC0 0x10 +#define ASPEED_H2X_TX_DESC1 0x14 +#define ASPEED_H2X_TX_DESC2 0x18 +#define ASPEED_H2X_TX_DESC3 0x1c +#define ASPEED_H2X_TX_DESC_DATA 0x20 +#define ASPEED_H2X_STS 0x24 +#define ASPEED_PCIE_TX_IDLE BIT(31) +#define ASPEED_PCIE_STATUS_OF_TX GENMASK(25, 24) +#define ASPEED_PCIE_RC_H_TX_COMPLETE BIT(25) +#define ASPEED_PCIE_TRIGGER_TX BIT(0) +#define ASPEED_H2X_AHB_ADDR_CONFIG0 0x60 +#define ASPEED_AHB_REMAP_LO_ADDR(x) FIELD_PREP(GENMASK(15, 4), x) +#define ASPEED_AHB_MASK_LO_ADDR(x) FIELD_PREP(GENMASK(31, 20), x) +#define ASPEED_H2X_AHB_ADDR_CONFIG1 0x64 +#define ASPEED_AHB_REMAP_HI_ADDR(x) (x) +#define ASPEED_H2X_AHB_ADDR_CONFIG2 0x68 +#define ASPEED_AHB_MASK_HI_ADDR(x) (x) +#define ASPEED_H2X_DEV_CTRL 0xc0 +#define ASPEED_PCIE_RX_DMA_EN BIT(9) +#define ASPEED_PCIE_RX_LINEAR BIT(8) +#define ASPEED_PCIE_RX_MSI_SEL BIT(7) +#define ASPEED_PCIE_RX_MSI_EN BIT(6) +#define ASPEED_PCIE_UNLOCK_RX_BUFF BIT(4) +#define ASPEED_PCIE_WAIT_RX_TLP_CLR BIT(2) +#define ASPEED_PCIE_RC_RX_ENABLE BIT(1) +#define ASPEED_PCIE_RC_ENABLE BIT(0) +#define ASPEED_H2X_DEV_STS 0xc8 +#define ASPEED_PCIE_RC_RX_DONE_ISR BIT(4) +#define ASPEED_H2X_DEV_RX_DESC_DATA 0xcc +#define ASPEED_H2X_DEV_RX_DESC1 0xd4 +#define ASPEED_H2X_DEV_TX_TAG 0xfc +#define ASPEED_RC_TLP_TX_TAG_NUM 0x28 + +/* AST2700 H2X */ +#define ASPEED_H2X_CTRL 0x00 +#define ASPEED_H2X_BRIDGE_EN BIT(0) +#define ASPEED_H2X_BRIDGE_DIRECT_EN BIT(1) +#define ASPEED_H2X_CFGE_INT_STS 0x08 +#define ASPEED_CFGE_TX_IDLE BIT(0) +#define ASPEED_CFGE_RX_BUSY BIT(1) +#define ASPEED_H2X_CFGI_TLP 0x20 +#define ASPEED_CFGI_BYTE_EN_MASK GENMASK(19, 16) +#define ASPEED_CFGI_BYTE_EN(x) \ + FIELD_PREP(ASPEED_CFGI_BYTE_EN_MASK, (x)) +#define ASPEED_H2X_CFGI_WR_DATA 0x24 +#define ASPEED_CFGI_WRITE BIT(20) +#define ASPEED_H2X_CFGI_CTRL 0x28 +#define ASPEED_CFGI_TLP_FIRE BIT(0) +#define ASPEED_H2X_CFGI_RET_DATA 0x2c +#define ASPEED_H2X_CFGE_TLP_1ST 0x30 +#define ASPEED_H2X_CFGE_TLP_NEXT 0x34 +#define ASPEED_H2X_CFGE_CTRL 0x38 +#define ASPEED_CFGE_TLP_FIRE BIT(0) +#define ASPEED_H2X_CFGE_RET_DATA 0x3c +#define ASPEED_H2X_REMAP_PREF_ADDR 0x70 +#define ASPEED_REMAP_PREF_ADDR_63_32(x) (x) +#define ASPEED_H2X_REMAP_PCI_ADDR_HI 0x74 +#define ASPEED_REMAP_PCI_ADDR_63_32(x) (((x) >> 32) & GENMASK(31, 0)) +#define ASPEED_H2X_REMAP_PCI_ADDR_LO 0x78 +#define ASPEED_REMAP_PCI_ADDR_31_12(x) ((x) & GENMASK(31, 12)) + +/* AST2700 SCU */ +#define ASPEED_SCU_60 0x60 +#define ASPEED_RC_E2M_PATH_EN BIT(0) +#define ASPEED_RC_H2XS_PATH_EN BIT(16) +#define ASPEED_RC_H2XD_PATH_EN BIT(17) +#define ASPEED_RC_H2XX_PATH_EN BIT(18) +#define ASPEED_RC_UPSTREAM_MEM_EN BIT(19) +#define ASPEED_SCU_64 0x64 +#define ASPEED_RC0_DECODE_DMA_BASE(x) FIELD_PREP(GENMASK(7, 0), x) +#define ASPEED_RC0_DECODE_DMA_LIMIT(x) FIELD_PREP(GENMASK(15, 8), x) +#define ASPEED_RC1_DECODE_DMA_BASE(x) FIELD_PREP(GENMASK(23, 16), x) +#define ASPEED_RC1_DECODE_DMA_LIMIT(x) FIELD_PREP(GENMASK(31, 24), x) +#define ASPEED_SCU_70 0x70 +#define ASPEED_DISABLE_EP_FUNC 0 + +/* Macro to combine Fmt and Type into the 8-bit field */ +#define ASPEED_TLP_FMT_TYPE(fmt, type) ((((fmt) & 0x7) << 5) | ((type) & 0= x1f)) +#define ASPEED_TLP_COMMON_FIELDS GENMASK(31, 24) + +/* Completion status */ +#define CPL_STS(x) FIELD_GET(GENMASK(15, 13), (x)) +/* TLP configuration type 0 and type 1 */ +#define CFG0_READ_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \ + PCIE_TLP_TYPE_CFG0_RD)) +#define CFG0_WRITE_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ + PCIE_TLP_TYPE_CFG0_WR)) +#define CFG1_READ_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \ + PCIE_TLP_TYPE_CFG1_RD)) +#define CFG1_WRITE_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ + PCIE_TLP_TYPE_CFG1_WR)) +#define CFG_PAYLOAD_SIZE 0x01 /* 1 DWORD */ +#define TLP_HEADER_BYTE_EN(x, y) ((GENMASK((x) - 1, 0) << ((y) % 4))) +#define TLP_GET_VALUE(x, y, z) \ + (((x) >> ((((z) % 4)) * 8)) & GENMASK((8 * (y)) - 1, 0)) +#define TLP_SET_VALUE(x, y, z) \ + ((((x) & GENMASK((8 * (y)) - 1, 0)) << ((((z) % 4)) * 8))) +#define AST2600_TX_DESC1_VALUE 0x00002000 +#define AST2700_TX_DESC1_VALUE 0x00401000 + +/** + * struct aspeed_pcie_port - PCIe port information + * @list: port list + * @pcie: pointer to PCIe host info + * @clk: pointer to the port clock gate + * @phy: pointer to PCIe PHY + * @perst: pointer to port reset control + * @slot: port slot + */ +struct aspeed_pcie_port { + struct list_head list; + struct aspeed_pcie *pcie; + struct clk *clk; + struct phy *phy; + struct reset_control *perst; + u32 slot; +}; + +/** + * struct aspeed_pcie - PCIe RC information + * @host: pointer to PCIe host bridge + * @dev: pointer to device structure + * @reg: PCIe host register base address + * @ahbc: pointer to AHHC register map + * @cfg: pointer to Aspeed PCIe configuration register map + * @platform: platform specific information + * @ports: list of PCIe ports + * @tx_tag: current TX tag for the port + * @host_bus_num: bus number of the host bridge + * @h2xrst: pointer to H2X reset control + * @intx_domain: IRQ domain for INTx interrupts + * @msi_domain: IRQ domain for MSI interrupts + * @lock: mutex to protect MSI bitmap variable + * @msi_irq_in_use: bitmap to track used MSI host IRQs + * @clear_msi_twice: AST2700 workaround to clear MSI status twice + */ +struct aspeed_pcie { + struct pci_host_bridge *host; + struct device *dev; + void __iomem *reg; + struct regmap *ahbc; + struct regmap *cfg; + const struct aspeed_pcie_rc_platform *platform; + struct list_head ports; + + u8 tx_tag; + int host_bus_num; + + struct reset_control *h2xrst; + + struct irq_domain *intx_domain; + struct irq_domain *msi_domain; + struct mutex lock; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_HOST_IRQS); + + bool clear_msi_twice; /* AST2700 workaround */ +}; + +/** + * struct aspeed_pcie_rc_platform - Platform information + * @setup: initialization function + * @reg_intx_en: INTx enable register offset + * @reg_intx_sts: INTx status register offset + * @reg_msi_en: MSI enable register offset + * @reg_msi_sts: MSI enable register offset + * @msi_address: HW fixed MSI address + */ +struct aspeed_pcie_rc_platform { + int (*setup)(struct platform_device *pdev); + int reg_intx_en; + int reg_intx_sts; + int reg_msi_en; + int reg_msi_sts; + u32 msi_address; +}; + +static void aspeed_pcie_intx_irq_ack(struct irq_data *d) +{ + struct aspeed_pcie *pcie =3D irq_data_get_irq_chip_data(d); + int intx_en =3D pcie->platform->reg_intx_en; + u32 en; + + en =3D readl(pcie->reg + intx_en); + en |=3D BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static void aspeed_pcie_intx_irq_mask(struct irq_data *d) +{ + struct aspeed_pcie *pcie =3D irq_data_get_irq_chip_data(d); + int intx_en =3D pcie->platform->reg_intx_en; + u32 en; + + en =3D readl(pcie->reg + intx_en); + en &=3D ~BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static void aspeed_pcie_intx_irq_unmask(struct irq_data *d) +{ + struct aspeed_pcie *pcie =3D irq_data_get_irq_chip_data(d); + int intx_en =3D pcie->platform->reg_intx_en; + u32 en; + + en =3D readl(pcie->reg + intx_en); + en |=3D BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static struct irq_chip aspeed_intx_irq_chip =3D { + .name =3D "INTx", + .irq_ack =3D aspeed_pcie_intx_irq_ack, + .irq_mask =3D aspeed_pcie_intx_irq_mask, + .irq_unmask =3D aspeed_pcie_intx_irq_unmask, +}; + +static int aspeed_pcie_intx_map(struct irq_domain *domain, unsigned int ir= q, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &aspeed_intx_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 aspeed_intx_domain_ops =3D { + .map =3D aspeed_pcie_intx_map, +}; + +static irqreturn_t aspeed_pcie_intr_handler(int irq, void *dev_id) +{ + struct aspeed_pcie *pcie =3D dev_id; + const struct aspeed_pcie_rc_platform *platform =3D pcie->platform; + unsigned long status; + unsigned long intx; + u32 bit; + int i; + + intx =3D FIELD_GET(ASPEED_PCIE_INTX_STS, + readl(pcie->reg + platform->reg_intx_sts)); + for_each_set_bit(bit, &intx, PCI_NUM_INTX) + generic_handle_domain_irq(pcie->intx_domain, bit); + + for (i =3D 0; i < 2; i++) { + int msi_sts_reg =3D platform->reg_msi_sts + (i * 4); + + status =3D readl(pcie->reg + msi_sts_reg); + writel(status, pcie->reg + msi_sts_reg); + + /* + * AST2700 workaround: + * The MSI status needs to clear one more time. + */ + if (pcie->clear_msi_twice) + writel(status, pcie->reg + msi_sts_reg); + + for_each_set_bit(bit, &status, 32) { + bit +=3D (i * 32); + generic_handle_domain_irq(pcie->msi_domain, bit); + } + } + + return IRQ_HANDLED; +} + +static u32 aspeed_pcie_get_bdf_offset(struct pci_bus *bus, unsigned int de= vfn, + int where) +{ + return ((bus->number) << 24) | (PCI_SLOT(devfn) << 19) | + (PCI_FUNC(devfn) << 16) | (where & ~3); +} + +static int aspeed_ast2600_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val, u32 fmt_type, + bool write) +{ + struct aspeed_pcie *pcie =3D bus->sysdata; + u32 bdf_offset, cfg_val, isr; + int ret; + + bdf_offset =3D aspeed_pcie_get_bdf_offset(bus, devfn, where); + + /* Driver may set unlock RX buffer before triggering next TX config */ + cfg_val =3D readl(pcie->reg + ASPEED_H2X_DEV_CTRL); + writel(ASPEED_PCIE_UNLOCK_RX_BUFF | cfg_val, + pcie->reg + ASPEED_H2X_DEV_CTRL); + + cfg_val =3D fmt_type | CFG_PAYLOAD_SIZE; + writel(cfg_val, pcie->reg + ASPEED_H2X_TX_DESC0); + + cfg_val =3D AST2600_TX_DESC1_VALUE | + FIELD_PREP(GENMASK(11, 8), pcie->tx_tag) | + TLP_HEADER_BYTE_EN(size, where); + writel(cfg_val, pcie->reg + ASPEED_H2X_TX_DESC1); + + writel(bdf_offset, pcie->reg + ASPEED_H2X_TX_DESC2); + writel(0, pcie->reg + ASPEED_H2X_TX_DESC3); + if (write) + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_TX_DESC_DATA); + + cfg_val =3D readl(pcie->reg + ASPEED_H2X_STS); + cfg_val |=3D ASPEED_PCIE_TRIGGER_TX; + writel(cfg_val, pcie->reg + ASPEED_H2X_STS); + + ret =3D readl_poll_timeout(pcie->reg + ASPEED_H2X_STS, cfg_val, + (cfg_val & ASPEED_PCIE_TX_IDLE), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR tx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), cfg_val); + ret =3D PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + + cfg_val =3D readl(pcie->reg + ASPEED_H2X_INT_STS); + cfg_val |=3D ASPEED_PCIE_TX_IDLE_CLEAR; + writel(cfg_val, pcie->reg + ASPEED_H2X_INT_STS); + + cfg_val =3D readl(pcie->reg + ASPEED_H2X_STS); + switch (cfg_val & ASPEED_PCIE_STATUS_OF_TX) { + case ASPEED_PCIE_RC_H_TX_COMPLETE: + ret =3D readl_poll_timeout(pcie->reg + ASPEED_H2X_DEV_STS, isr, + (isr & ASPEED_PCIE_RC_RX_DONE_ISR), 0, + 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR rx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), isr); + ret =3D PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + if (!write) { + cfg_val =3D readl(pcie->reg + ASPEED_H2X_DEV_RX_DESC1); + if (CPL_STS(cfg_val) !=3D PCIE_CPL_STS_SUCCESS) { + ret =3D PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } else { + *val =3D readl(pcie->reg + + ASPEED_H2X_DEV_RX_DESC_DATA); + } + } + break; + case ASPEED_PCIE_STATUS_OF_TX: + ret =3D PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + default: + *val =3D readl(pcie->reg + ASPEED_H2X_HOST_RX_DESC_DATA); + break; + } + + cfg_val =3D readl(pcie->reg + ASPEED_H2X_DEV_CTRL); + cfg_val |=3D ASPEED_PCIE_UNLOCK_RX_BUFF; + writel(cfg_val, pcie->reg + ASPEED_H2X_DEV_CTRL); + + *val =3D TLP_GET_VALUE(*val, size, where); + + ret =3D PCIBIOS_SUCCESSFUL; +out: + cfg_val =3D readl(pcie->reg + ASPEED_H2X_DEV_STS); + writel(cfg_val, pcie->reg + ASPEED_H2X_DEV_STS); + pcie->tx_tag =3D (pcie->tx_tag + 1) % 0x8; + return ret; +} + +static int aspeed_ast2600_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + /* + * AST2600 has only one Root Port on the root bus. + */ + if (PCI_SLOT(devfn) !=3D 8) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2600_conf(bus, devfn, where, size, val, + CFG0_READ_FMTTYPE, false); +} + +static int aspeed_ast2600_child_rd_conf(struct pci_bus *bus, unsigned int = devfn, + int where, int size, u32 *val) +{ + return aspeed_ast2600_conf(bus, devfn, where, size, val, + CFG1_READ_FMTTYPE, false); +} + +static int aspeed_ast2600_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + /* + * AST2600 has only one Root Port on the root bus. + */ + if (PCI_SLOT(devfn) !=3D 8) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2600_conf(bus, devfn, where, size, &val, + CFG0_WRITE_FMTTYPE, true); +} + +static int aspeed_ast2600_child_wr_conf(struct pci_bus *bus, unsigned int = devfn, + int where, int size, u32 val) +{ + return aspeed_ast2600_conf(bus, devfn, where, size, &val, + CFG1_WRITE_FMTTYPE, true); +} + +static int aspeed_ast2700_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val, bool write) +{ + struct aspeed_pcie *pcie =3D bus->sysdata; + u32 cfg_val; + + cfg_val =3D ASPEED_CFGI_BYTE_EN(TLP_HEADER_BYTE_EN(size, where)) | + (where & ~3); + if (write) + cfg_val |=3D ASPEED_CFGI_WRITE; + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGI_TLP); + + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_CFGI_WR_DATA); + writel(ASPEED_CFGI_TLP_FIRE, pcie->reg + ASPEED_H2X_CFGI_CTRL); + *val =3D readl(pcie->reg + ASPEED_H2X_CFGI_RET_DATA); + *val =3D TLP_GET_VALUE(*val, size, where); + + return PCIBIOS_SUCCESSFUL; +} + +static int aspeed_ast2700_child_config(struct pci_bus *bus, unsigned int d= evfn, + int where, int size, u32 *val, + bool write) +{ + struct aspeed_pcie *pcie =3D bus->sysdata; + u32 bdf_offset, status, cfg_val; + int ret; + + bdf_offset =3D aspeed_pcie_get_bdf_offset(bus, devfn, where); + + cfg_val =3D CFG_PAYLOAD_SIZE; + if (write) + cfg_val |=3D (bus->number =3D=3D (pcie->host_bus_num + 1)) ? + CFG0_WRITE_FMTTYPE : + CFG1_WRITE_FMTTYPE; + else + cfg_val |=3D (bus->number =3D=3D (pcie->host_bus_num + 1)) ? + CFG0_READ_FMTTYPE : + CFG1_READ_FMTTYPE; + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGE_TLP_1ST); + + cfg_val =3D AST2700_TX_DESC1_VALUE | + FIELD_PREP(GENMASK(11, 8), pcie->tx_tag) | + TLP_HEADER_BYTE_EN(size, where); + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + + writel(bdf_offset, pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + if (write) + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + writel(ASPEED_CFGE_TX_IDLE | ASPEED_CFGE_RX_BUSY, + pcie->reg + ASPEED_H2X_CFGE_INT_STS); + writel(ASPEED_CFGE_TLP_FIRE, pcie->reg + ASPEED_H2X_CFGE_CTRL); + + ret =3D readl_poll_timeout(pcie->reg + ASPEED_H2X_CFGE_INT_STS, status, + (status & ASPEED_CFGE_TX_IDLE), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR tx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), status); + ret =3D PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + + ret =3D readl_poll_timeout(pcie->reg + ASPEED_H2X_CFGE_INT_STS, status, + (status & ASPEED_CFGE_RX_BUSY), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR rx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), status); + ret =3D PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + *val =3D readl(pcie->reg + ASPEED_H2X_CFGE_RET_DATA); + *val =3D TLP_GET_VALUE(*val, size, where); + + ret =3D PCIBIOS_SUCCESSFUL; +out: + writel(status, pcie->reg + ASPEED_H2X_CFGE_INT_STS); + pcie->tx_tag =3D (pcie->tx_tag + 1) % 0xf; + return ret; +} + +static int aspeed_ast2700_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + /* + * AST2700 has only one Root Port on the root bus. + */ + if (devfn !=3D 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2700_config(bus, devfn, where, size, val, false); +} + +static int aspeed_ast2700_child_rd_conf(struct pci_bus *bus, unsigned int = devfn, + int where, int size, u32 *val) +{ + return aspeed_ast2700_child_config(bus, devfn, where, size, val, false); +} + +static int aspeed_ast2700_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + /* + * AST2700 has only one Root Port on the root bus. + */ + if (devfn !=3D 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2700_config(bus, devfn, where, size, &val, true); +} + +static int aspeed_ast2700_child_wr_conf(struct pci_bus *bus, unsigned int = devfn, + int where, int size, u32 val) +{ + return aspeed_ast2700_child_config(bus, devfn, where, size, &val, true); +} + +static struct pci_ops aspeed_ast2600_pcie_ops =3D { + .read =3D aspeed_ast2600_rd_conf, + .write =3D aspeed_ast2600_wr_conf, +}; + +static struct pci_ops aspeed_ast2600_pcie_child_ops =3D { + .read =3D aspeed_ast2600_child_rd_conf, + .write =3D aspeed_ast2600_child_wr_conf, +}; + +static struct pci_ops aspeed_ast2700_pcie_ops =3D { + .read =3D aspeed_ast2700_rd_conf, + .write =3D aspeed_ast2700_wr_conf, +}; + +static struct pci_ops aspeed_ast2700_pcie_child_ops =3D { + .read =3D aspeed_ast2700_child_rd_conf, + .write =3D aspeed_ast2700_child_wr_conf, +}; + +static void aspeed_irq_compose_msi_msg(struct irq_data *data, + struct msi_msg *msg) +{ + struct aspeed_pcie *pcie =3D irq_data_get_irq_chip_data(data); + + msg->address_hi =3D 0; + msg->address_lo =3D pcie->platform->msi_address; + msg->data =3D data->hwirq; +} + +static struct irq_chip aspeed_msi_bottom_irq_chip =3D { + .name =3D "ASPEED MSI", + .irq_compose_msi_msg =3D aspeed_irq_compose_msi_msg, +}; + +static int aspeed_irq_msi_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) +{ + struct aspeed_pcie *pcie =3D domain->host_data; + int bit; + int i; + + guard(mutex)(&pcie->lock); + + bit =3D bitmap_find_free_region(pcie->msi_irq_in_use, MAX_MSI_HOST_IRQS, + get_count_order(nr_irqs)); + + if (bit < 0) + return -ENOSPC; + + for (i =3D 0; i < nr_irqs; i++) { + irq_domain_set_info(domain, virq + i, bit + i, + &aspeed_msi_bottom_irq_chip, + domain->host_data, handle_simple_irq, NULL, + NULL); + } + + return 0; +} + +static void aspeed_irq_msi_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *data =3D irq_domain_get_irq_data(domain, virq); + struct aspeed_pcie *pcie =3D irq_data_get_irq_chip_data(data); + + guard(mutex)(&pcie->lock); + + bitmap_release_region(pcie->msi_irq_in_use, data->hwirq, + get_count_order(nr_irqs)); +} + +static const struct irq_domain_ops aspeed_msi_domain_ops =3D { + .alloc =3D aspeed_irq_msi_domain_alloc, + .free =3D aspeed_irq_msi_domain_free, +}; + +#define ASPEED_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_NO_AFFINITY) + +#define ASPEED_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_MULTI_PCI_MSI | \ + MSI_FLAG_PCI_MSIX) + +static const struct msi_parent_ops aspeed_msi_parent_ops =3D { + .required_flags =3D ASPEED_MSI_FLAGS_REQUIRED, + .supported_flags =3D ASPEED_MSI_FLAGS_SUPPORTED, + .bus_select_token =3D DOMAIN_BUS_PCI_MSI, + .chip_flags =3D MSI_CHIP_FLAG_SET_ACK, + .prefix =3D "ASPEED-", + .init_dev_msi_info =3D msi_lib_init_dev_msi_info, +}; + +static int aspeed_pcie_msi_init(struct aspeed_pcie *pcie) +{ + writel(~0, pcie->reg + pcie->platform->reg_msi_en); + writel(~0, pcie->reg + pcie->platform->reg_msi_en + 0x04); + writel(~0, pcie->reg + pcie->platform->reg_msi_sts); + writel(~0, pcie->reg + pcie->platform->reg_msi_sts + 0x04); + + struct irq_domain_info info =3D { + .fwnode =3D dev_fwnode(pcie->dev), + .ops =3D &aspeed_msi_domain_ops, + .host_data =3D pcie, + .size =3D MAX_MSI_HOST_IRQS, + }; + + pcie->msi_domain =3D msi_create_parent_irq_domain(&info, + &aspeed_msi_parent_ops); + if (!pcie->msi_domain) + return dev_err_probe(pcie->dev, -ENOMEM, + "failed to create MSI domain\n"); + + return 0; +} + +static void aspeed_pcie_msi_free(struct aspeed_pcie *pcie) +{ + if (pcie->msi_domain) { + irq_domain_remove(pcie->msi_domain); + pcie->msi_domain =3D NULL; + } +} + +static void aspeed_pcie_irq_domain_free(void *d) +{ + struct aspeed_pcie *pcie =3D d; + + if (pcie->intx_domain) { + irq_domain_remove(pcie->intx_domain); + pcie->intx_domain =3D NULL; + } + aspeed_pcie_msi_free(pcie); +} + +static int aspeed_pcie_init_irq_domain(struct aspeed_pcie *pcie) +{ + int ret; + + pcie->intx_domain =3D irq_domain_add_linear(pcie->dev->of_node, + PCI_NUM_INTX, + &aspeed_intx_domain_ops, + pcie); + if (!pcie->intx_domain) { + ret =3D dev_err_probe(pcie->dev, -ENOMEM, + "failed to get INTx IRQ domain\n"); + goto err; + } + + writel(0, pcie->reg + pcie->platform->reg_intx_en); + writel(~0, pcie->reg + pcie->platform->reg_intx_sts); + + ret =3D aspeed_pcie_msi_init(pcie); + if (ret) + goto err; + + return 0; +err: + aspeed_pcie_irq_domain_free(pcie); + return ret; +} + +static int aspeed_pcie_port_init(struct aspeed_pcie_port *port) +{ + struct aspeed_pcie *pcie =3D port->pcie; + struct device *dev =3D pcie->dev; + int ret; + + ret =3D clk_prepare_enable(port->clk); + if (ret) + return dev_err_probe(dev, ret, + "failed to set clock for slot (%d)\n", + port->slot); + + ret =3D phy_init(port->phy); + if (ret) + return dev_err_probe(dev, ret, + "failed to init phy pcie for slot (%d)\n", + port->slot); + + ret =3D phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); + if (ret) + return dev_err_probe(dev, ret, + "failed to set phy mode for slot (%d)\n", + port->slot); + + reset_control_deassert(port->perst); + mdelay(PCIE_RESET_CONFIG_WAIT_MS); + + return 0; +} + +static int aspeed_pcie_init_ports(struct aspeed_pcie *pcie) +{ + struct aspeed_pcie_port *port, *tmp; + u8 num_enabled =3D 0; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + int ret; + + ret =3D aspeed_pcie_port_init(port); + if (ret) + list_del(&port->list); + else + num_enabled++; + } + + return (num_enabled > 0) ? 0 : -ENODEV; +} + +static void aspeed_host_reset(struct aspeed_pcie *pcie) +{ + reset_control_assert(pcie->h2xrst); + mdelay(ASPEED_RESET_RC_WAIT_MS); + reset_control_deassert(pcie->h2xrst); +} + +static int aspeed_ast2600_setup(struct platform_device *pdev) +{ + struct aspeed_pcie *pcie =3D platform_get_drvdata(pdev); + struct device *dev =3D pcie->dev; + + pcie->ahbc =3D syscon_regmap_lookup_by_phandle(dev->of_node, + "aspeed,ahbc"); + if (IS_ERR(pcie->ahbc)) + return dev_err_probe(dev, PTR_ERR(pcie->ahbc), + "failed to map ahbc base\n"); + + aspeed_host_reset(pcie); + + regmap_write(pcie->ahbc, ASPEED_AHBC_KEY, ASPEED_AHBC_UNLOCK_KEY); + regmap_update_bits(pcie->ahbc, ASPEED_AHBC_ADDR_MAPPING, + ASPEED_PCIE_RC_MEMORY_EN, ASPEED_PCIE_RC_MEMORY_EN); + regmap_write(pcie->ahbc, ASPEED_AHBC_KEY, ASPEED_AHBC_UNLOCK); + + /* Due to the BAR assignment is fixed mapping on 0x6000_0000.*/ + writel(ASPEED_AHB_REMAP_LO_ADDR(0x600) | ASPEED_AHB_MASK_LO_ADDR(0xe00), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG0); + writel(ASPEED_AHB_REMAP_HI_ADDR(0), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG1); + writel(ASPEED_AHB_MASK_HI_ADDR(~0), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG2); + writel(ASPEED_H2X_BRIDGE_EN, pcie->reg + ASPEED_H2X_CTRL); + + writel(ASPEED_PCIE_RX_DMA_EN | ASPEED_PCIE_RX_LINEAR | + ASPEED_PCIE_RX_MSI_SEL | ASPEED_PCIE_RX_MSI_EN | + ASPEED_PCIE_WAIT_RX_TLP_CLR | ASPEED_PCIE_RC_RX_ENABLE | + ASPEED_PCIE_RC_ENABLE, + pcie->reg + ASPEED_H2X_DEV_CTRL); + + writel(ASPEED_RC_TLP_TX_TAG_NUM, pcie->reg + ASPEED_H2X_DEV_TX_TAG); + + pcie->host->ops =3D &aspeed_ast2600_pcie_ops; + pcie->host->child_ops =3D &aspeed_ast2600_pcie_child_ops; + + return 0; +} + +static int aspeed_ast2700_remap_pci_addr(struct aspeed_pcie *pcie) +{ + struct device_node *dev_node =3D pcie->dev->of_node; + struct of_pci_range range; + struct of_pci_range_parser parser; + int ret; + + ret =3D of_pci_range_parser_init(&parser, dev_node); + if (ret) + return ret; + + for_each_of_pci_range(&parser, &range) { + if ((range.flags & IORESOURCE_TYPE_BITS) =3D=3D IORESOURCE_MEM) { + writel(ASPEED_REMAP_PCI_ADDR_31_12(range.pci_addr), + pcie->reg + ASPEED_H2X_REMAP_PCI_ADDR_LO); + writel(ASPEED_REMAP_PCI_ADDR_63_32(range.pci_addr), + pcie->reg + ASPEED_H2X_REMAP_PCI_ADDR_HI); + return 0; + } + } + + return -ENODEV; +} + +static int aspeed_ast2700_setup(struct platform_device *pdev) +{ + struct aspeed_pcie *pcie =3D platform_get_drvdata(pdev); + struct device *dev =3D pcie->dev; + int ret; + + pcie->cfg =3D syscon_regmap_lookup_by_phandle(dev->of_node, + "aspeed,pciecfg"); + if (IS_ERR(pcie->cfg)) + return dev_err_probe(dev, PTR_ERR(pcie->cfg), + "failed to map pciecfg base\n"); + + regmap_update_bits(pcie->cfg, ASPEED_SCU_60, + ASPEED_RC_E2M_PATH_EN | ASPEED_RC_H2XS_PATH_EN | + ASPEED_RC_H2XD_PATH_EN | ASPEED_RC_H2XX_PATH_EN | + ASPEED_RC_UPSTREAM_MEM_EN, + ASPEED_RC_E2M_PATH_EN | ASPEED_RC_H2XS_PATH_EN | + ASPEED_RC_H2XD_PATH_EN | ASPEED_RC_H2XX_PATH_EN | + ASPEED_RC_UPSTREAM_MEM_EN); + regmap_write(pcie->cfg, ASPEED_SCU_64, + ASPEED_RC0_DECODE_DMA_BASE(0) | + ASPEED_RC0_DECODE_DMA_LIMIT(0xff) | + ASPEED_RC1_DECODE_DMA_BASE(0) | + ASPEED_RC1_DECODE_DMA_LIMIT(0xff)); + regmap_write(pcie->cfg, ASPEED_SCU_70, ASPEED_DISABLE_EP_FUNC); + + aspeed_host_reset(pcie); + + writel(0, pcie->reg + ASPEED_H2X_CTRL); + writel(ASPEED_H2X_BRIDGE_EN | ASPEED_H2X_BRIDGE_DIRECT_EN, + pcie->reg + ASPEED_H2X_CTRL); + + ret =3D aspeed_ast2700_remap_pci_addr(pcie); + if (ret) + return dev_err_probe(dev, ret, "failed to remap PCI address\n"); + + /* Prepare for 64-bit BAR pref */ + writel(ASPEED_REMAP_PREF_ADDR_63_32(0x3), + pcie->reg + ASPEED_H2X_REMAP_PREF_ADDR); + + pcie->host->ops =3D &aspeed_ast2700_pcie_ops; + pcie->host->child_ops =3D &aspeed_ast2700_pcie_child_ops; + pcie->clear_msi_twice =3D true; + + return 0; +} + +static void aspeed_pcie_reset_release(void *d) +{ + struct reset_control *perst =3D d; + + if (!perst) + return; + + reset_control_put(perst); +} + +static int aspeed_pcie_parse_port(struct aspeed_pcie *pcie, + struct device_node *node, + int slot) +{ + struct aspeed_pcie_port *port; + struct device *dev =3D pcie->dev; + int ret; + + port =3D devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->clk =3D devm_get_clk_from_child(dev, node, NULL); + if (IS_ERR(port->clk)) + return dev_err_probe(dev, PTR_ERR(port->clk), + "failed to get pcie%d clock\n", slot); + + port->phy =3D devm_of_phy_get(dev, node, NULL); + if (IS_ERR(port->phy)) + return dev_err_probe(dev, PTR_ERR(port->phy), + "failed to get phy pcie%d\n", slot); + + port->perst =3D of_reset_control_get_exclusive(node, "perst"); + if (IS_ERR(port->perst)) + return dev_err_probe(dev, PTR_ERR(port->perst), + "failed to get pcie%d reset control\n", + slot); + ret =3D devm_add_action_or_reset(dev, aspeed_pcie_reset_release, + port->perst); + if (ret) + return ret; + reset_control_assert(port->perst); + + port->slot =3D slot; + port->pcie =3D pcie; + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &pcie->ports); + + return 0; +} + +static int aspeed_pcie_parse_dt(struct aspeed_pcie *pcie) +{ + struct device *dev =3D pcie->dev; + struct device_node *node =3D dev->of_node; + int ret; + + for_each_available_child_of_node_scoped(node, child) { + int slot; + const char *type; + + ret =3D of_property_read_string(child, "device_type", &type); + if (ret || strcmp(type, "pci")) + continue; + + ret =3D of_pci_get_devfn(child); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to parse devfn\n"); + + slot =3D PCI_SLOT(ret); + + ret =3D aspeed_pcie_parse_port(pcie, child, slot); + if (ret) + return ret; + } + + if (list_empty(&pcie->ports)) + return dev_err_probe(dev, -ENODEV, + "No PCIe port found in DT\n"); + + return 0; +} + +static int aspeed_pcie_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct pci_host_bridge *host; + struct aspeed_pcie *pcie; + struct resource_entry *entry; + const struct aspeed_pcie_rc_platform *md; + int irq, ret; + + md =3D of_device_get_match_data(dev); + if (!md) + return -ENODEV; + + host =3D devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!host) + return -ENOMEM; + + pcie =3D pci_host_bridge_priv(host); + pcie->dev =3D dev; + pcie->tx_tag =3D 0; + platform_set_drvdata(pdev, pcie); + + pcie->platform =3D md; + pcie->host =3D host; + INIT_LIST_HEAD(&pcie->ports); + + /* Get root bus num for cfg command to decide tlp type 0 or type 1 */ + entry =3D resource_list_first_type(&host->windows, IORESOURCE_BUS); + if (entry) + pcie->host_bus_num =3D entry->res->start; + + pcie->reg =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pcie->reg)) + return PTR_ERR(pcie->reg); + + pcie->h2xrst =3D devm_reset_control_get_exclusive(dev, "h2x"); + if (IS_ERR(pcie->h2xrst)) + return dev_err_probe(dev, PTR_ERR(pcie->h2xrst), + "failed to get h2x reset\n"); + + ret =3D devm_mutex_init(dev, &pcie->lock); + if (ret) + return dev_err_probe(dev, ret, "failed to init mutex\n"); + + ret =3D pcie->platform->setup(pdev); + if (ret) + return dev_err_probe(dev, ret, "failed to setup PCIe RC\n"); + + ret =3D aspeed_pcie_parse_dt(pcie); + if (ret) + return ret; + + ret =3D aspeed_pcie_init_ports(pcie); + if (ret) + return ret; + + host->sysdata =3D pcie; + + ret =3D aspeed_pcie_init_irq_domain(pcie); + if (ret) + return ret; + + irq =3D platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret =3D devm_add_action_or_reset(dev, aspeed_pcie_irq_domain_free, pcie); + if (ret) + return ret; + + ret =3D devm_request_irq(dev, irq, aspeed_pcie_intr_handler, IRQF_SHARED, + dev_name(dev), pcie); + if (ret) + return ret; + + return pci_host_probe(host); +} + +static const struct aspeed_pcie_rc_platform pcie_rc_ast2600 =3D { + .setup =3D aspeed_ast2600_setup, + .reg_intx_en =3D 0xc4, + .reg_intx_sts =3D 0xc8, + .reg_msi_en =3D 0xe0, + .reg_msi_sts =3D 0xe8, + .msi_address =3D 0x1e77005c, +}; + +static const struct aspeed_pcie_rc_platform pcie_rc_ast2700 =3D { + .setup =3D aspeed_ast2700_setup, + .reg_intx_en =3D 0x40, + .reg_intx_sts =3D 0x48, + .reg_msi_en =3D 0x50, + .reg_msi_sts =3D 0x58, + .msi_address =3D 0x000000f0, +}; + +static const struct of_device_id aspeed_pcie_of_match[] =3D { + { .compatible =3D "aspeed,ast2600-pcie", .data =3D &pcie_rc_ast2600 }, + { .compatible =3D "aspeed,ast2700-pcie", .data =3D &pcie_rc_ast2700 }, + {} +}; + +static struct platform_driver aspeed_pcie_driver =3D { + .driver =3D { + .name =3D "aspeed-pcie", + .of_match_table =3D aspeed_pcie_of_match, + .suppress_bind_attrs =3D true, + .probe_type =3D PROBE_PREFER_ASYNCHRONOUS, + }, + .probe =3D aspeed_pcie_probe, +}; + +builtin_platform_driver(aspeed_pcie_driver); + +MODULE_AUTHOR("Jacky Chou "); +MODULE_DESCRIPTION("ASPEED PCIe Root Complex"); +MODULE_LICENSE("GPL"); --=20 2.34.1