162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCIe host controller driver for HiSilicon SoCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Zhou Wang <wangzhou1@hisilicon.com> 862306a36Sopenharmony_ci * Dacai Zhu <zhudacai@hisilicon.com> 962306a36Sopenharmony_ci * Gabriele Paoloni <gabriele.paoloni@huawei.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/pci-acpi.h> 1662306a36Sopenharmony_ci#include <linux/pci-ecam.h> 1762306a36Sopenharmony_ci#include "../../pci.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct hisi_pcie { 2262306a36Sopenharmony_ci void __iomem *reg_base; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 2662306a36Sopenharmony_ci int size, u32 *val) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct pci_config_window *cfg = bus->sysdata; 2962306a36Sopenharmony_ci int dev = PCI_SLOT(devfn); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (bus->number == cfg->busr.start) { 3262306a36Sopenharmony_ci /* access only one slot on each root port */ 3362306a36Sopenharmony_ci if (dev > 0) 3462306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3562306a36Sopenharmony_ci else 3662306a36Sopenharmony_ci return pci_generic_config_read32(bus, devfn, where, 3762306a36Sopenharmony_ci size, val); 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return pci_generic_config_read(bus, devfn, where, size, val); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn, 4462306a36Sopenharmony_ci int where, int size, u32 val) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct pci_config_window *cfg = bus->sysdata; 4762306a36Sopenharmony_ci int dev = PCI_SLOT(devfn); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (bus->number == cfg->busr.start) { 5062306a36Sopenharmony_ci /* access only one slot on each root port */ 5162306a36Sopenharmony_ci if (dev > 0) 5262306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 5362306a36Sopenharmony_ci else 5462306a36Sopenharmony_ci return pci_generic_config_write32(bus, devfn, where, 5562306a36Sopenharmony_ci size, val); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return pci_generic_config_write(bus, devfn, where, size, val); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, 6262306a36Sopenharmony_ci int where) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct pci_config_window *cfg = bus->sysdata; 6562306a36Sopenharmony_ci struct hisi_pcie *pcie = cfg->priv; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (bus->number == cfg->busr.start) 6862306a36Sopenharmony_ci return pcie->reg_base + where; 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci return pci_ecam_map_bus(bus, devfn, where); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int hisi_pcie_init(struct pci_config_window *cfg) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct device *dev = cfg->parent; 7862306a36Sopenharmony_ci struct hisi_pcie *pcie; 7962306a36Sopenharmony_ci struct acpi_device *adev = to_acpi_device(dev); 8062306a36Sopenharmony_ci struct acpi_pci_root *root = acpi_driver_data(adev); 8162306a36Sopenharmony_ci struct resource *res; 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); 8562306a36Sopenharmony_ci if (!pcie) 8662306a36Sopenharmony_ci return -ENOMEM; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* 8962306a36Sopenharmony_ci * Retrieve RC base and size from a HISI0081 device with _UID 9062306a36Sopenharmony_ci * matching our segment. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); 9362306a36Sopenharmony_ci if (!res) 9462306a36Sopenharmony_ci return -ENOMEM; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res); 9762306a36Sopenharmony_ci if (ret) { 9862306a36Sopenharmony_ci dev_err(dev, "can't get rc base address\n"); 9962306a36Sopenharmony_ci return -ENOMEM; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci pcie->reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); 10362306a36Sopenharmony_ci if (!pcie->reg_base) 10462306a36Sopenharmony_ci return -ENOMEM; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci cfg->priv = pcie; 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciconst struct pci_ecam_ops hisi_pcie_ops = { 11162306a36Sopenharmony_ci .init = hisi_pcie_init, 11262306a36Sopenharmony_ci .pci_ops = { 11362306a36Sopenharmony_ci .map_bus = hisi_pcie_map_bus, 11462306a36Sopenharmony_ci .read = hisi_pcie_rd_conf, 11562306a36Sopenharmony_ci .write = hisi_pcie_wr_conf, 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#endif 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#ifdef CONFIG_PCI_HISI 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int hisi_pcie_platform_init(struct pci_config_window *cfg) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct device *dev = cfg->parent; 12662306a36Sopenharmony_ci struct hisi_pcie *pcie; 12762306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 12862306a36Sopenharmony_ci struct resource *res; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); 13162306a36Sopenharmony_ci if (!pcie) 13262306a36Sopenharmony_ci return -ENOMEM; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 13562306a36Sopenharmony_ci if (!res) { 13662306a36Sopenharmony_ci dev_err(dev, "missing \"reg[1]\"property\n"); 13762306a36Sopenharmony_ci return -EINVAL; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci pcie->reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); 14162306a36Sopenharmony_ci if (!pcie->reg_base) 14262306a36Sopenharmony_ci return -ENOMEM; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci cfg->priv = pcie; 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic const struct pci_ecam_ops hisi_pcie_platform_ops = { 14962306a36Sopenharmony_ci .init = hisi_pcie_platform_init, 15062306a36Sopenharmony_ci .pci_ops = { 15162306a36Sopenharmony_ci .map_bus = hisi_pcie_map_bus, 15262306a36Sopenharmony_ci .read = hisi_pcie_rd_conf, 15362306a36Sopenharmony_ci .write = hisi_pcie_wr_conf, 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { 15862306a36Sopenharmony_ci { 15962306a36Sopenharmony_ci .compatible = "hisilicon,hip06-pcie-ecam", 16062306a36Sopenharmony_ci .data = &hisi_pcie_platform_ops, 16162306a36Sopenharmony_ci }, 16262306a36Sopenharmony_ci { 16362306a36Sopenharmony_ci .compatible = "hisilicon,hip07-pcie-ecam", 16462306a36Sopenharmony_ci .data = &hisi_pcie_platform_ops, 16562306a36Sopenharmony_ci }, 16662306a36Sopenharmony_ci {}, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct platform_driver hisi_pcie_almost_ecam_driver = { 17062306a36Sopenharmony_ci .probe = pci_host_common_probe, 17162306a36Sopenharmony_ci .driver = { 17262306a36Sopenharmony_ci .name = "hisi-pcie-almost-ecam", 17362306a36Sopenharmony_ci .of_match_table = hisi_pcie_almost_ecam_of_match, 17462306a36Sopenharmony_ci .suppress_bind_attrs = true, 17562306a36Sopenharmony_ci }, 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_cibuiltin_platform_driver(hisi_pcie_almost_ecam_driver); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#endif 18062306a36Sopenharmony_ci#endif 181