PCI: rockchip: Add ACPI support

Native APCI support for PCIe need firmware to initialize HW and provide
ECAM vectors via Mcfg and _CRS tables via root port descriptors. We
implement UEFI as firmware to cover HW part, so this driver must be used
under UEFI environment.

DW IP isn't ECAM compatible so we provide RKCP0001 as HID to workaround
ECAM scan, so that we can get DBI(_CRS) and Cfg space(_CBA) from ACPI.
Cfg space should be different from  Mcfg table for mapping.

rk_pcie_ecam_ops is registered as quriks for accessing different cfg space
in ACPI pcie lib.

Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Change-Id: I6a34768add6fd7030a187269955bcd6ed764c9e8
This commit is contained in:
Shawn Lin
2022-06-14 16:30:20 +08:00
committed by Tao Huang
parent a61b80cc7d
commit 365ee62df1
4 changed files with 202 additions and 0 deletions
+5
View File
@@ -65,6 +65,11 @@ static struct mcfg_fixup mcfg_quirks[] = {
QCOM_ECAM32(6),
QCOM_ECAM32(7),
#define RKCP_ECAM(seg, table_id, ops) \
{ "RKCP ", table_id, 0x0000, seg, MCFG_BUS_ANY, ops }
RKCP_ECAM(0, "RK3588 ", &rk_pcie_ecam_ops), /* pcie3x4: Name (_SEG, Zero) */
#define HISI_QUAD_DOM(table_id, seg, ops) \
{ "HISI ", table_id, 0, (seg) + 0, MCFG_BUS_ANY, ops }, \
{ "HISI ", table_id, 0, (seg) + 1, MCFG_BUS_ANY, ops }, \
+1
View File
@@ -34,5 +34,6 @@ obj-$(CONFIG_PCIE_DW_ROCKCHIP) += pcie-dw-rockchip.o
ifdef CONFIG_PCI
obj-$(CONFIG_ARM64) += pcie-al.o
obj-$(CONFIG_ARM64) += pcie-dw-rockchip-acpi.o
obj-$(CONFIG_ARM64) += pcie-hisi.o
endif
@@ -0,0 +1,195 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ACPI PCIe host controller driver for Rockchip SoCs
*
* Copyright (C) 2022 Rockchip Electronics Co., Ltd.
* http://www.rock-chips.com
*
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/pci-ecam.h>
#include <linux/pci-acpi.h>
#include <linux/pci.h>
#include "pcie-designware.h"
#include "../../pci.h"
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
#define ECAM_RESV_SIZE SZ_16M
struct rk_pcie_acpi {
void __iomem *dbi_base;
void __iomem *cfg_base;
phys_addr_t mcfg_addr;
};
static void rk_pcie_writel_ob_unroll(void __iomem *dbi_base, u32 index, u32 reg, u32 val)
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
writel(val, dbi_base + offset + reg + DEFAULT_DBI_ATU_OFFSET);
}
static u32 rk_pcie_readl_ob_unroll(void __iomem *dbi_base, u32 index, u32 reg)
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
return readl(dbi_base + offset + reg + DEFAULT_DBI_ATU_OFFSET);
}
static void rk_pcie_prog_outbound_atu_unroll(struct device *dev, void __iomem *dbi_base, u32 index,
u32 type, u64 cpu_addr, u64 pci_addr, u32 size)
{
u32 retries, val;
dev_dbg(dev, "%s: ATU programmed with: index: %d, type: %d, cpu addr: %8llx, pci addr: %8llx, size: %8x\n",
__func__, index, type, cpu_addr, pci_addr, size);
rk_pcie_writel_ob_unroll(dbi_base, index, PCIE_ATU_UNR_LOWER_BASE, lower_32_bits(cpu_addr));
rk_pcie_writel_ob_unroll(dbi_base, index, PCIE_ATU_UNR_UPPER_BASE, upper_32_bits(cpu_addr));
rk_pcie_writel_ob_unroll(dbi_base, index, PCIE_ATU_UNR_LOWER_LIMIT, lower_32_bits(cpu_addr + size - 1));
rk_pcie_writel_ob_unroll(dbi_base, index, PCIE_ATU_UNR_UPPER_LIMIT, upper_32_bits(cpu_addr + size - 1));
rk_pcie_writel_ob_unroll(dbi_base, index, PCIE_ATU_UNR_LOWER_TARGET, lower_32_bits(pci_addr));
rk_pcie_writel_ob_unroll(dbi_base, index, PCIE_ATU_UNR_UPPER_TARGET, upper_32_bits(pci_addr));
rk_pcie_writel_ob_unroll(dbi_base, index, PCIE_ATU_UNR_REGION_CTRL1, type);
rk_pcie_writel_ob_unroll(dbi_base, 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 = rk_pcie_readl_ob_unroll(dbi_base, index, PCIE_ATU_UNR_REGION_CTRL2);
if (val & PCIE_ATU_ENABLE)
return;
mdelay(LINK_WAIT_IATU);
}
dev_err(dev, "outbound iATU is not being enabled\n");
}
static int rk_pcie_ecam_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;
phys_addr_t mcfg_addr;
struct rk_pcie_acpi *rk_pcie;
int ret;
rk_pcie = devm_kzalloc(dev, sizeof(*rk_pcie), GFP_KERNEL);
if (!rk_pcie)
return -ENOMEM;
/*
* Retrieve RC base and size from a RKCP0001 device with _UID
* matching our segment.
*/
res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
if (!res)
return -ENOMEM;
ret = acpi_get_rc_resources(dev, "RKCP0001", root->segment, res);
if (ret) {
dev_err(dev, "can't get rc base (DBI) address\n");
return -ENOMEM;
}
dev_info(dev, "DBI address is %pa\n", &res->start);
rk_pcie->dbi_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res));
if (!rk_pcie->dbi_base)
return -ENOMEM;
mcfg_addr = acpi_pci_root_get_mcfg_addr(adev->handle);
if (!mcfg_addr) {
dev_err(dev, "can't get mcfg base (cfg) address\n");
return -ENOMEM;
}
dev_info(dev, "mcfg address is %pa\n", &mcfg_addr);
rk_pcie->mcfg_addr = mcfg_addr;
rk_pcie->cfg_base = devm_pci_remap_cfgspace(dev, mcfg_addr, SZ_1M);
if (!rk_pcie->cfg_base)
return -ENOMEM;
cfg->priv = rk_pcie;
return 0;
}
static int rk_pcie_ecam_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);
/* access only one slot on each root port */
if (bus->number == cfg->busr.start && dev > 0)
return PCIBIOS_DEVICE_NOT_FOUND;
return pci_generic_config_read(bus, devfn, where, size, val);
}
static int rk_pcie_ecam_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);
/* access only one slot on each root port */
if (bus->number == cfg->busr.start && dev > 0)
return PCIBIOS_DEVICE_NOT_FOUND;
return pci_generic_config_write(bus, devfn, where, size, val);
}
static void __iomem *rk_pcie_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, int where)
{
struct pci_config_window *cfg = bus->sysdata;
struct rk_pcie_acpi *rk_pcie = cfg->priv;
u32 atu_type;
u32 busdev;
/* read RC config space */
if (bus->number == cfg->busr.start)
return rk_pcie->dbi_base + where;
if (pci_is_root_bus(bus->parent))
atu_type = PCIE_ATU_TYPE_CFG0;
else
atu_type = PCIE_ATU_TYPE_CFG1;
busdev = PCIE_ATU_BUS(bus->number) |
PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
/*
* UEFI region mapping relation:
* index0: 32-bit np memory
* index1: config
* index2: IO
* index3: 64-bit np memory
*/
rk_pcie_prog_outbound_atu_unroll(cfg->parent, rk_pcie->dbi_base, PCIE_ATU_REGION_INDEX1,
atu_type, (u64)rk_pcie->mcfg_addr, busdev, ECAM_RESV_SIZE);
dev_dbg(cfg->parent, "Read other config: 0x%p where = %d\n",
rk_pcie->cfg_base + where, where);
return rk_pcie->cfg_base + where;
}
const struct pci_ecam_ops rk_pcie_ecam_ops = {
.bus_shift = 20, /* We don't need this */
.init = rk_pcie_ecam_init,
.pci_ops = {
.map_bus = rk_pcie_ecam_map_bus,
.read = rk_pcie_ecam_rd_conf,
.write = rk_pcie_ecam_wr_conf,
}
};
#endif
+1
View File
@@ -53,6 +53,7 @@ extern const struct pci_ecam_ops pci_generic_ecam_ops;
extern const struct pci_ecam_ops pci_32b_ops; /* 32-bit accesses only */
extern const struct pci_ecam_ops pci_32b_read_ops; /* 32-bit read only */
extern const struct pci_ecam_ops hisi_pcie_ops; /* HiSilicon */
extern const struct pci_ecam_ops rk_pcie_ecam_ops; /* Rockchip */
extern const struct pci_ecam_ops thunder_pem_ecam_ops; /* Cavium ThunderX 1.x & 2.x */
extern const struct pci_ecam_ops pci_thunder_ecam_ops; /* Cavium ThunderX 1.x */
extern const struct pci_ecam_ops xgene_v1_pcie_ecam_ops; /* APM X-Gene PCIe v1 */