18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCIe host controller driver for HiSilicon SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Zhou Wang <wangzhou1@hisilicon.com> 88c2ecf20Sopenharmony_ci * Dacai Zhu <zhudacai@hisilicon.com> 98c2ecf20Sopenharmony_ci * Gabriele Paoloni <gabriele.paoloni@huawei.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/pci-acpi.h> 168c2ecf20Sopenharmony_ci#include <linux/pci-ecam.h> 178c2ecf20Sopenharmony_ci#include "../../pci.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 228c2ecf20Sopenharmony_ci int size, u32 *val) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct pci_config_window *cfg = bus->sysdata; 258c2ecf20Sopenharmony_ci int dev = PCI_SLOT(devfn); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (bus->number == cfg->busr.start) { 288c2ecf20Sopenharmony_ci /* access only one slot on each root port */ 298c2ecf20Sopenharmony_ci if (dev > 0) 308c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 318c2ecf20Sopenharmony_ci else 328c2ecf20Sopenharmony_ci return pci_generic_config_read32(bus, devfn, where, 338c2ecf20Sopenharmony_ci size, val); 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci return pci_generic_config_read(bus, devfn, where, size, val); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn, 408c2ecf20Sopenharmony_ci int where, int size, u32 val) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct pci_config_window *cfg = bus->sysdata; 438c2ecf20Sopenharmony_ci int dev = PCI_SLOT(devfn); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (bus->number == cfg->busr.start) { 468c2ecf20Sopenharmony_ci /* access only one slot on each root port */ 478c2ecf20Sopenharmony_ci if (dev > 0) 488c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 498c2ecf20Sopenharmony_ci else 508c2ecf20Sopenharmony_ci return pci_generic_config_write32(bus, devfn, where, 518c2ecf20Sopenharmony_ci size, val); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return pci_generic_config_write(bus, devfn, where, size, val); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, 588c2ecf20Sopenharmony_ci int where) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct pci_config_window *cfg = bus->sysdata; 618c2ecf20Sopenharmony_ci void __iomem *reg_base = cfg->priv; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (bus->number == cfg->busr.start) 648c2ecf20Sopenharmony_ci return reg_base + where; 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci return pci_ecam_map_bus(bus, devfn, where); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int hisi_pcie_init(struct pci_config_window *cfg) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct device *dev = cfg->parent; 748c2ecf20Sopenharmony_ci struct acpi_device *adev = to_acpi_device(dev); 758c2ecf20Sopenharmony_ci struct acpi_pci_root *root = acpi_driver_data(adev); 768c2ecf20Sopenharmony_ci struct resource *res; 778c2ecf20Sopenharmony_ci void __iomem *reg_base; 788c2ecf20Sopenharmony_ci int ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* 818c2ecf20Sopenharmony_ci * Retrieve RC base and size from a HISI0081 device with _UID 828c2ecf20Sopenharmony_ci * matching our segment. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); 858c2ecf20Sopenharmony_ci if (!res) 868c2ecf20Sopenharmony_ci return -ENOMEM; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res); 898c2ecf20Sopenharmony_ci if (ret) { 908c2ecf20Sopenharmony_ci dev_err(dev, "can't get rc base address\n"); 918c2ecf20Sopenharmony_ci return -ENOMEM; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); 958c2ecf20Sopenharmony_ci if (!reg_base) 968c2ecf20Sopenharmony_ci return -ENOMEM; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci cfg->priv = reg_base; 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ciconst struct pci_ecam_ops hisi_pcie_ops = { 1038c2ecf20Sopenharmony_ci .bus_shift = 20, 1048c2ecf20Sopenharmony_ci .init = hisi_pcie_init, 1058c2ecf20Sopenharmony_ci .pci_ops = { 1068c2ecf20Sopenharmony_ci .map_bus = hisi_pcie_map_bus, 1078c2ecf20Sopenharmony_ci .read = hisi_pcie_rd_conf, 1088c2ecf20Sopenharmony_ci .write = hisi_pcie_wr_conf, 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#endif 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_HISI 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int hisi_pcie_platform_init(struct pci_config_window *cfg) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct device *dev = cfg->parent; 1198c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 1208c2ecf20Sopenharmony_ci struct resource *res; 1218c2ecf20Sopenharmony_ci void __iomem *reg_base; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 1248c2ecf20Sopenharmony_ci if (!res) { 1258c2ecf20Sopenharmony_ci dev_err(dev, "missing \"reg[1]\"property\n"); 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); 1308c2ecf20Sopenharmony_ci if (!reg_base) 1318c2ecf20Sopenharmony_ci return -ENOMEM; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci cfg->priv = reg_base; 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct pci_ecam_ops hisi_pcie_platform_ops = { 1388c2ecf20Sopenharmony_ci .bus_shift = 20, 1398c2ecf20Sopenharmony_ci .init = hisi_pcie_platform_init, 1408c2ecf20Sopenharmony_ci .pci_ops = { 1418c2ecf20Sopenharmony_ci .map_bus = hisi_pcie_map_bus, 1428c2ecf20Sopenharmony_ci .read = hisi_pcie_rd_conf, 1438c2ecf20Sopenharmony_ci .write = hisi_pcie_wr_conf, 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { 1488c2ecf20Sopenharmony_ci { 1498c2ecf20Sopenharmony_ci .compatible = "hisilicon,hip06-pcie-ecam", 1508c2ecf20Sopenharmony_ci .data = &hisi_pcie_platform_ops, 1518c2ecf20Sopenharmony_ci }, 1528c2ecf20Sopenharmony_ci { 1538c2ecf20Sopenharmony_ci .compatible = "hisilicon,hip07-pcie-ecam", 1548c2ecf20Sopenharmony_ci .data = &hisi_pcie_platform_ops, 1558c2ecf20Sopenharmony_ci }, 1568c2ecf20Sopenharmony_ci {}, 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic struct platform_driver hisi_pcie_almost_ecam_driver = { 1608c2ecf20Sopenharmony_ci .probe = pci_host_common_probe, 1618c2ecf20Sopenharmony_ci .driver = { 1628c2ecf20Sopenharmony_ci .name = "hisi-pcie-almost-ecam", 1638c2ecf20Sopenharmony_ci .of_match_table = hisi_pcie_almost_ecam_of_match, 1648c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 1658c2ecf20Sopenharmony_ci }, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_cibuiltin_platform_driver(hisi_pcie_almost_ecam_driver); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci#endif 1708c2ecf20Sopenharmony_ci#endif 171