1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * PCIe host controller driver for HiSilicon SoCs 4 * 5 * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com 6 * 7 * Authors: Zhou Wang <wangzhou1@hisilicon.com> 8 * Dacai Zhu <zhudacai@hisilicon.com> 9 * Gabriele Paoloni <gabriele.paoloni@huawei.com> 10 */ 11 #include <linux/interrupt.h> 12 #include <linux/init.h> 13 #include <linux/platform_device.h> 14 #include <linux/pci.h> 15 #include <linux/pci-acpi.h> 16 #include <linux/pci-ecam.h> 17 #include "../../pci.h" 18 19 #if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) 20 21 static int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, 22 int size, u32 *val) 23 { 24 struct pci_config_window *cfg = bus->sysdata; 25 int dev = PCI_SLOT(devfn); 26 27 if (bus->number == cfg->busr.start) { 28 /* access only one slot on each root port */ 29 if (dev > 0) 30 return PCIBIOS_DEVICE_NOT_FOUND; 31 else 32 return pci_generic_config_read32(bus, devfn, where, 33 size, val); 34 } 35 36 return pci_generic_config_read(bus, devfn, where, size, val); 37 } 38 39 static int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn, 40 int where, int size, u32 val) 41 { 42 struct pci_config_window *cfg = bus->sysdata; 43 int dev = PCI_SLOT(devfn); 44 45 if (bus->number == cfg->busr.start) { 46 /* access only one slot on each root port */ 47 if (dev > 0) 48 return PCIBIOS_DEVICE_NOT_FOUND; 49 else 50 return pci_generic_config_write32(bus, devfn, where, 51 size, val); 52 } 53 54 return pci_generic_config_write(bus, devfn, where, size, val); 55 } 56 57 static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, 58 int where) 59 { 60 struct pci_config_window *cfg = bus->sysdata; 61 void __iomem *reg_base = cfg->priv; 62 63 if (bus->number == cfg->busr.start) 64 return reg_base + where; 65 else 66 return pci_ecam_map_bus(bus, devfn, where); 67 } 68 69 #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) 70 71 static int hisi_pcie_init(struct pci_config_window *cfg) 72 { 73 struct device *dev = cfg->parent; 74 struct acpi_device *adev = to_acpi_device(dev); 75 struct acpi_pci_root *root = acpi_driver_data(adev); 76 struct resource *res; 77 void __iomem *reg_base; 78 int ret; 79 80 /* 81 * Retrieve RC base and size from a HISI0081 device with _UID 82 * matching our segment. 83 */ 84 res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); 85 if (!res) 86 return -ENOMEM; 87 88 ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res); 89 if (ret) { 90 dev_err(dev, "can't get rc base address\n"); 91 return -ENOMEM; 92 } 93 94 reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); 95 if (!reg_base) 96 return -ENOMEM; 97 98 cfg->priv = reg_base; 99 return 0; 100 } 101 102 const struct pci_ecam_ops hisi_pcie_ops = { 103 .init = hisi_pcie_init, 104 .pci_ops = { 105 .map_bus = hisi_pcie_map_bus, 106 .read = hisi_pcie_rd_conf, 107 .write = hisi_pcie_wr_conf, 108 } 109 }; 110 111 #endif 112 113 #ifdef CONFIG_PCI_HISI 114 115 static int hisi_pcie_platform_init(struct pci_config_window *cfg) 116 { 117 struct device *dev = cfg->parent; 118 struct platform_device *pdev = to_platform_device(dev); 119 struct resource *res; 120 void __iomem *reg_base; 121 122 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 123 if (!res) { 124 dev_err(dev, "missing \"reg[1]\"property\n"); 125 return -EINVAL; 126 } 127 128 reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); 129 if (!reg_base) 130 return -ENOMEM; 131 132 cfg->priv = reg_base; 133 return 0; 134 } 135 136 static const struct pci_ecam_ops hisi_pcie_platform_ops = { 137 .init = hisi_pcie_platform_init, 138 .pci_ops = { 139 .map_bus = hisi_pcie_map_bus, 140 .read = hisi_pcie_rd_conf, 141 .write = hisi_pcie_wr_conf, 142 } 143 }; 144 145 static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { 146 { 147 .compatible = "hisilicon,hip06-pcie-ecam", 148 .data = &hisi_pcie_platform_ops, 149 }, 150 { 151 .compatible = "hisilicon,hip07-pcie-ecam", 152 .data = &hisi_pcie_platform_ops, 153 }, 154 {}, 155 }; 156 157 static struct platform_driver hisi_pcie_almost_ecam_driver = { 158 .probe = pci_host_common_probe, 159 .driver = { 160 .name = "hisi-pcie-almost-ecam", 161 .of_match_table = hisi_pcie_almost_ecam_of_match, 162 .suppress_bind_attrs = true, 163 }, 164 }; 165 builtin_platform_driver(hisi_pcie_almost_ecam_driver); 166 167 #endif 168 #endif 169