Merge branch 'pci/controller/dwc-andes-qilai'

- Add Andes QiLai SoC PCIe host driver support (Randolph Lin)

* pci/controller/dwc-andes-qilai:
  PCI: qilai: Add Andes QiLai SoC PCIe host driver support
  dt-bindings: PCI: Add Andes QiLai PCIe support

# Conflicts:
#	drivers/pci/controller/dwc/Makefile
This commit is contained in:
Bjorn Helgaas
2026-04-13 12:50:15 -05:00
5 changed files with 305 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/andestech,qilai-pcie.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Andes QiLai PCIe host controller
description:
Andes QiLai PCIe host controller is based on the Synopsys DesignWare
PCI core.
maintainers:
- Randolph Lin <randolph@andestech.com>
allOf:
- $ref: /schemas/pci/snps,dw-pcie.yaml#
properties:
compatible:
const: andestech,qilai-pcie
reg:
items:
- description: Data Bus Interface (DBI) registers.
- description: APB registers.
- description: PCIe configuration space region.
reg-names:
items:
- const: dbi
- const: apb
- const: config
dma-coherent: true
ranges:
maxItems: 2
interrupts:
maxItems: 1
interrupt-names:
items:
- const: msi
required:
- reg
- reg-names
- interrupts
- interrupt-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
pcie@80000000 {
compatible = "andestech,qilai-pcie";
device_type = "pci";
reg = <0x0 0x80000000 0x0 0x20000000>,
<0x0 0x04000000 0x0 0x00001000>,
<0x0 0x00000000 0x0 0x00010000>;
reg-names = "dbi", "apb", "config";
dma-coherent;
linux,pci-domain = <0>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x02000000 0x00 0x10000000 0x00 0x10000000 0x00 0xf0000000>,
<0x43000000 0x01 0x00000000 0x01 0x00000000 0x02 0x00000000>;
#interrupt-cells = <1>;
interrupts = <0xf>;
interrupt-names = "msi";
interrupt-parent = <&plic0>;
interrupt-map-mask = <0 0 0 0>;
interrupt-map = <0 0 0 1 &plic0 0xf IRQ_TYPE_LEVEL_HIGH>,
<0 0 0 2 &plic0 0xf IRQ_TYPE_LEVEL_HIGH>,
<0 0 0 3 &plic0 0xf IRQ_TYPE_LEVEL_HIGH>,
<0 0 0 4 &plic0 0xf IRQ_TYPE_LEVEL_HIGH>;
};
};
...

View File

@@ -20128,6 +20128,13 @@ S: Supported
F: Documentation/devicetree/bindings/pci/altr,pcie-root-port.yaml
F: drivers/pci/controller/pcie-altera.c
PCI DRIVER FOR ANDES QILAI PCIE
M: Randolph Lin <randolph@andestech.com>
L: linux-pci@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/pci/andestech,qilai-pcie.yaml
F: drivers/pci/controller/dwc/pcie-andes-qilai.c
PCI DRIVER FOR APPLIEDMICRO XGENE
M: Toan Le <toan@os.amperecomputing.com>
L: linux-pci@vger.kernel.org

View File

@@ -61,6 +61,17 @@ config PCI_MESON
and therefore the driver re-uses the DesignWare core functions to
implement the driver.
config PCIE_ANDES_QILAI
tristate "Andes QiLai PCIe controller"
depends on ARCH_ANDES || COMPILE_TEST
depends on PCI_MSI
select PCIE_DW_HOST
help
Say Y here to enable PCIe controller support on Andes QiLai SoCs,
which operate in Root Complex mode. The Andes QiLai SoC PCIe
controller is based on DesignWare IP and therefore the driver
re-uses the DesignWare core functions to implement the driver.
config PCIE_ARTPEC6
bool

View File

@@ -5,6 +5,7 @@ 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_PCIE_AMD_MDB) += pcie-amd-mdb.o
obj-$(CONFIG_PCIE_ANDES_QILAI) += pcie-andes-qilai.o
obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o

View File

@@ -0,0 +1,197 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the PCIe Controller in QiLai from Andes
*
* Copyright (C) 2026 Andes Technology Corporation
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include "pcie-designware.h"
#define PCIE_INTR_CONTROL1 0x15c
#define PCIE_MSI_CTRL_INT_EN BIT(28)
#define PCIE_LOGIC_COHERENCY_CONTROL3 0x8e8
/*
* Refer to Table A4-5 (Memory type encoding) in the
* AMBA AXI and ACE Protocol Specification.
*
* The selected value corresponds to the Memory type field:
* "Write-back, Read and Write-allocate".
*
* The last three rows in the table A4-5 in
* AMBA AXI and ACE Protocol Specification:
* ARCACHE AWCACHE Memory type
* ------------------------------------------------------------------
* 1111 (0111) 0111 Write-back Read-allocate
* 1011 1111 (1011) Write-back Write-allocate
* 1111 1111 Write-back Read and Write-allocate (selected)
*/
#define IOCP_ARCACHE 0b1111
#define IOCP_AWCACHE 0b1111
#define PCIE_CFG_MSTR_ARCACHE_MODE GENMASK(6, 3)
#define PCIE_CFG_MSTR_AWCACHE_MODE GENMASK(14, 11)
#define PCIE_CFG_MSTR_ARCACHE_VALUE GENMASK(22, 19)
#define PCIE_CFG_MSTR_AWCACHE_VALUE GENMASK(30, 27)
#define PCIE_GEN_CONTROL2 0x54
#define PCIE_CFG_LTSSM_EN BIT(0)
#define PCIE_REGS_PCIE_SII_PM_STATE 0xc0
#define SMLH_LINK_UP BIT(6)
#define RDLH_LINK_UP BIT(7)
struct qilai_pcie {
struct dw_pcie pci;
void __iomem *apb_base;
};
#define to_qilai_pcie(_pci) container_of(_pci, struct qilai_pcie, pci)
static bool qilai_pcie_link_up(struct dw_pcie *pci)
{
struct qilai_pcie *pcie = to_qilai_pcie(pci);
u32 val;
val = readl(pcie->apb_base + PCIE_REGS_PCIE_SII_PM_STATE);
return FIELD_GET(SMLH_LINK_UP, val) && FIELD_GET(RDLH_LINK_UP, val);
}
static int qilai_pcie_start_link(struct dw_pcie *pci)
{
struct qilai_pcie *pcie = to_qilai_pcie(pci);
u32 val;
val = readl(pcie->apb_base + PCIE_GEN_CONTROL2);
val |= PCIE_CFG_LTSSM_EN;
writel(val, pcie->apb_base + PCIE_GEN_CONTROL2);
return 0;
}
static const struct dw_pcie_ops qilai_pcie_ops = {
.link_up = qilai_pcie_link_up,
.start_link = qilai_pcie_start_link,
};
/*
* Set up the QiLai PCIe IOCP (IO Coherence Port) Read/Write Behaviors to the
* Write-Back, Read and Write Allocate mode.
*
* The IOCP HW target is SoC last-level cache (L2 Cache), which serves as the
* system cache. The IOCP HW helps maintain cache monitoring, ensuring that
* the device can snoop data from/to the cache.
*/
static void qilai_pcie_iocp_cache_setup(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
u32 val;
dw_pcie_dbi_ro_wr_en(pci);
val = dw_pcie_readl_dbi(pci, PCIE_LOGIC_COHERENCY_CONTROL3);
FIELD_MODIFY(PCIE_CFG_MSTR_ARCACHE_MODE, &val, IOCP_ARCACHE);
FIELD_MODIFY(PCIE_CFG_MSTR_AWCACHE_MODE, &val, IOCP_AWCACHE);
FIELD_MODIFY(PCIE_CFG_MSTR_ARCACHE_VALUE, &val, IOCP_ARCACHE);
FIELD_MODIFY(PCIE_CFG_MSTR_AWCACHE_VALUE, &val, IOCP_AWCACHE);
dw_pcie_writel_dbi(pci, PCIE_LOGIC_COHERENCY_CONTROL3, val);
dw_pcie_dbi_ro_wr_dis(pci);
}
static void qilai_pcie_enable_msi(struct qilai_pcie *pcie)
{
u32 val;
val = readl(pcie->apb_base + PCIE_INTR_CONTROL1);
val |= PCIE_MSI_CTRL_INT_EN;
writel(val, pcie->apb_base + PCIE_INTR_CONTROL1);
}
static int qilai_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct qilai_pcie *pcie = to_qilai_pcie(pci);
qilai_pcie_enable_msi(pcie);
return 0;
}
static void qilai_pcie_host_post_init(struct dw_pcie_rp *pp)
{
qilai_pcie_iocp_cache_setup(pp);
}
static const struct dw_pcie_host_ops qilai_pcie_host_ops = {
.init = qilai_pcie_host_init,
.post_init = qilai_pcie_host_post_init,
};
static int qilai_pcie_probe(struct platform_device *pdev)
{
struct qilai_pcie *pcie;
struct dw_pcie *pci;
struct device *dev = &pdev->dev;
int ret;
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
platform_set_drvdata(pdev, pcie);
pci = &pcie->pci;
pcie->pci.dev = dev;
pcie->pci.ops = &qilai_pcie_ops;
pcie->pci.pp.ops = &qilai_pcie_host_ops;
pci->use_parent_dt_ranges = true;
dw_pcie_cap_set(&pcie->pci, REQ_RES);
pcie->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb");
if (IS_ERR(pcie->apb_base))
return PTR_ERR(pcie->apb_base);
pm_runtime_set_active(dev);
pm_runtime_no_callbacks(dev);
devm_pm_runtime_enable(dev);
ret = dw_pcie_host_init(&pcie->pci.pp);
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize PCIe host\n");
return 0;
}
static const struct of_device_id qilai_pcie_of_match[] = {
{ .compatible = "andestech,qilai-pcie" },
{},
};
MODULE_DEVICE_TABLE(of, qilai_pcie_of_match);
static struct platform_driver qilai_pcie_driver = {
.probe = qilai_pcie_probe,
.driver = {
.name = "qilai-pcie",
.of_match_table = qilai_pcie_of_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
builtin_platform_driver(qilai_pcie_driver);
MODULE_AUTHOR("Randolph Lin <randolph@andestech.com>");
MODULE_DESCRIPTION("Andes QiLai PCIe driver");
MODULE_LICENSE("GPL");