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 .bus_shift = 20, 104 .init = hisi_pcie_init, 105 .pci_ops = { 106 .map_bus = hisi_pcie_map_bus, 107 .read = hisi_pcie_rd_conf, 108 .write = hisi_pcie_wr_conf, 109 } 110 }; 111 112 #endif 113 114 #ifdef CONFIG_PCI_HISI 115 116 static int hisi_pcie_platform_init(struct pci_config_window *cfg) 117 { 118 struct device *dev = cfg->parent; 119 struct platform_device *pdev = to_platform_device(dev); 120 struct resource *res; 121 void __iomem *reg_base; 122 123 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 124 if (!res) { 125 dev_err(dev, "missing \"reg[1]\"property\n"); 126 return -EINVAL; 127 } 128 129 reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res)); 130 if (!reg_base) 131 return -ENOMEM; 132 133 cfg->priv = reg_base; 134 return 0; 135 } 136 137 static const struct pci_ecam_ops hisi_pcie_platform_ops = { 138 .bus_shift = 20, 139 .init = hisi_pcie_platform_init, 140 .pci_ops = { 141 .map_bus = hisi_pcie_map_bus, 142 .read = hisi_pcie_rd_conf, 143 .write = hisi_pcie_wr_conf, 144 } 145 }; 146 147 static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { 148 { 149 .compatible = "hisilicon,hip06-pcie-ecam", 150 .data = &hisi_pcie_platform_ops, 151 }, 152 { 153 .compatible = "hisilicon,hip07-pcie-ecam", 154 .data = &hisi_pcie_platform_ops, 155 }, 156 {}, 157 }; 158 159 static struct platform_driver hisi_pcie_almost_ecam_driver = { 160 .probe = pci_host_common_probe, 161 .driver = { 162 .name = "hisi-pcie-almost-ecam", 163 .of_match_table = hisi_pcie_almost_ecam_of_match, 164 .suppress_bind_attrs = true, 165 }, 166 }; 167 builtin_platform_driver(hisi_pcie_almost_ecam_driver); 168 169 #endif 170 #endif 171