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