162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ACPI quirks for Tegra194 PCIe host controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2021 NVIDIA Corporation.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Vidya Sagar <vidyas@nvidia.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/pci.h>
1162306a36Sopenharmony_ci#include <linux/pci-acpi.h>
1262306a36Sopenharmony_ci#include <linux/pci-ecam.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "pcie-designware.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct tegra194_pcie_ecam  {
1762306a36Sopenharmony_ci	void __iomem *config_base;
1862306a36Sopenharmony_ci	void __iomem *iatu_base;
1962306a36Sopenharmony_ci	void __iomem *dbi_base;
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int tegra194_acpi_init(struct pci_config_window *cfg)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct device *dev = cfg->parent;
2562306a36Sopenharmony_ci	struct tegra194_pcie_ecam *pcie_ecam;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	pcie_ecam = devm_kzalloc(dev, sizeof(*pcie_ecam), GFP_KERNEL);
2862306a36Sopenharmony_ci	if (!pcie_ecam)
2962306a36Sopenharmony_ci		return -ENOMEM;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	pcie_ecam->config_base = cfg->win;
3262306a36Sopenharmony_ci	pcie_ecam->iatu_base = cfg->win + SZ_256K;
3362306a36Sopenharmony_ci	pcie_ecam->dbi_base = cfg->win + SZ_512K;
3462306a36Sopenharmony_ci	cfg->priv = pcie_ecam;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return 0;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void atu_reg_write(struct tegra194_pcie_ecam *pcie_ecam, int index,
4062306a36Sopenharmony_ci			  u32 val, u32 reg)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	u32 offset = PCIE_ATU_UNROLL_BASE(PCIE_ATU_REGION_DIR_OB, index) +
4362306a36Sopenharmony_ci		     PCIE_ATU_VIEWPORT_BASE;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	writel(val, pcie_ecam->iatu_base + offset + reg);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void program_outbound_atu(struct tegra194_pcie_ecam *pcie_ecam,
4962306a36Sopenharmony_ci				 int index, int type, u64 cpu_addr,
5062306a36Sopenharmony_ci				 u64 pci_addr, u64 size)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	atu_reg_write(pcie_ecam, index, lower_32_bits(cpu_addr),
5362306a36Sopenharmony_ci		      PCIE_ATU_LOWER_BASE);
5462306a36Sopenharmony_ci	atu_reg_write(pcie_ecam, index, upper_32_bits(cpu_addr),
5562306a36Sopenharmony_ci		      PCIE_ATU_UPPER_BASE);
5662306a36Sopenharmony_ci	atu_reg_write(pcie_ecam, index, lower_32_bits(pci_addr),
5762306a36Sopenharmony_ci		      PCIE_ATU_LOWER_TARGET);
5862306a36Sopenharmony_ci	atu_reg_write(pcie_ecam, index, lower_32_bits(cpu_addr + size - 1),
5962306a36Sopenharmony_ci		      PCIE_ATU_LIMIT);
6062306a36Sopenharmony_ci	atu_reg_write(pcie_ecam, index, upper_32_bits(pci_addr),
6162306a36Sopenharmony_ci		      PCIE_ATU_UPPER_TARGET);
6262306a36Sopenharmony_ci	atu_reg_write(pcie_ecam, index, type, PCIE_ATU_REGION_CTRL1);
6362306a36Sopenharmony_ci	atu_reg_write(pcie_ecam, index, PCIE_ATU_ENABLE, PCIE_ATU_REGION_CTRL2);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void __iomem *tegra194_map_bus(struct pci_bus *bus,
6762306a36Sopenharmony_ci				      unsigned int devfn, int where)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct pci_config_window *cfg = bus->sysdata;
7062306a36Sopenharmony_ci	struct tegra194_pcie_ecam *pcie_ecam = cfg->priv;
7162306a36Sopenharmony_ci	u32 busdev;
7262306a36Sopenharmony_ci	int type;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (bus->number < cfg->busr.start || bus->number > cfg->busr.end)
7562306a36Sopenharmony_ci		return NULL;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (bus->number == cfg->busr.start) {
7862306a36Sopenharmony_ci		if (PCI_SLOT(devfn) == 0)
7962306a36Sopenharmony_ci			return pcie_ecam->dbi_base + where;
8062306a36Sopenharmony_ci		else
8162306a36Sopenharmony_ci			return NULL;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
8562306a36Sopenharmony_ci		 PCIE_ATU_FUNC(PCI_FUNC(devfn));
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (bus->parent->number == cfg->busr.start) {
8862306a36Sopenharmony_ci		if (PCI_SLOT(devfn) == 0)
8962306a36Sopenharmony_ci			type = PCIE_ATU_TYPE_CFG0;
9062306a36Sopenharmony_ci		else
9162306a36Sopenharmony_ci			return NULL;
9262306a36Sopenharmony_ci	} else {
9362306a36Sopenharmony_ci		type = PCIE_ATU_TYPE_CFG1;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	program_outbound_atu(pcie_ecam, 0, type, cfg->res.start, busdev,
9762306a36Sopenharmony_ci			     SZ_256K);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return pcie_ecam->config_base + where;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ciconst struct pci_ecam_ops tegra194_pcie_ops = {
10362306a36Sopenharmony_ci	.init		= tegra194_acpi_init,
10462306a36Sopenharmony_ci	.pci_ops	= {
10562306a36Sopenharmony_ci		.map_bus	= tegra194_map_bus,
10662306a36Sopenharmony_ci		.read		= pci_generic_config_read,
10762306a36Sopenharmony_ci		.write		= pci_generic_config_write,
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci};
110