aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2013-11-22 11:14:41 -0500
committerBjorn Helgaas <bhelgaas@google.com>2014-05-30 13:34:49 -0400
commitce292991d88b77160f348fb8a3a2cf6e78f4b456 (patch)
treeb6ab6cb10e05db9dc03de18a595974d3361d30ac
parentc9eaa447e77efe77b7fa4c953bd62de8297fd6c5 (diff)
PCI: generic: Add generic PCI host controller driver
Add support for a generic PCI host controller, such as a firmware-initialised device with static windows or an emulation by something such as kvmtool. The controller itself has no configuration registers and has its address spaces described entirely by the device-tree (using the bindings from ePAPR). Both CAM and ECAM are supported for Config Space accesses. Add corresponding documentation for the DT binding. [bhelgaas: currently uses the ARM-specific pci_common_init_dev() interface] Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
-rw-r--r--Documentation/devicetree/bindings/pci/host-generic-pci.txt100
-rw-r--r--drivers/pci/host/Kconfig7
-rw-r--r--drivers/pci/host/Makefile1
-rw-r--r--drivers/pci/host/pci-host-generic.c388
4 files changed, 496 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/pci/host-generic-pci.txt b/Documentation/devicetree/bindings/pci/host-generic-pci.txt
new file mode 100644
index 000000000000..f0b0436807b4
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/host-generic-pci.txt
@@ -0,0 +1,100 @@
1* Generic PCI host controller
2
3Firmware-initialised PCI host controllers and PCI emulations, such as the
4virtio-pci implementations found in kvmtool and other para-virtualised
5systems, do not require driver support for complexities such as regulator
6and clock management. In fact, the controller may not even require the
7configuration of a control interface by the operating system, instead
8presenting a set of fixed windows describing a subset of IO, Memory and
9Configuration Spaces.
10
11Such a controller can be described purely in terms of the standardized device
12tree bindings communicated in pci.txt:
13
14
15Properties of the host controller node:
16
17- compatible : Must be "pci-host-cam-generic" or "pci-host-ecam-generic"
18 depending on the layout of configuration space (CAM vs
19 ECAM respectively).
20
21- device_type : Must be "pci".
22
23- ranges : As described in IEEE Std 1275-1994, but must provide
24 at least a definition of non-prefetchable memory. One
25 or both of prefetchable Memory and IO Space may also
26 be provided.
27
28- bus-range : Optional property (also described in IEEE Std 1275-1994)
29 to indicate the range of bus numbers for this controller.
30 If absent, defaults to <0 255> (i.e. all buses).
31
32- #address-cells : Must be 3.
33
34- #size-cells : Must be 2.
35
36- reg : The Configuration Space base address and size, as accessed
37 from the parent bus.
38
39
40Properties of the /chosen node:
41
42- linux,pci-probe-only
43 : Optional property which takes a single-cell argument.
44 If '0', then Linux will assign devices in its usual manner,
45 otherwise it will not try to assign devices and instead use
46 them as they are configured already.
47
48Configuration Space is assumed to be memory-mapped (as opposed to being
49accessed via an ioport) and laid out with a direct correspondence to the
50geography of a PCI bus address by concatenating the various components to
51form an offset.
52
53For CAM, this 24-bit offset is:
54
55 cfg_offset(bus, device, function, register) =
56 bus << 16 | device << 11 | function << 8 | register
57
58Whilst ECAM extends this by 4 bits to accomodate 4k of function space:
59
60 cfg_offset(bus, device, function, register) =
61 bus << 20 | device << 15 | function << 12 | register
62
63Interrupt mapping is exactly as described in `Open Firmware Recommended
64Practice: Interrupt Mapping' and requires the following properties:
65
66- #interrupt-cells : Must be 1
67
68- interrupt-map : <see aforementioned specification>
69
70- interrupt-map-mask : <see aforementioned specification>
71
72
73Example:
74
75pci {
76 compatible = "pci-host-cam-generic"
77 device_type = "pci";
78 #address-cells = <3>;
79 #size-cells = <2>;
80 bus-range = <0x0 0x1>;
81
82 // CPU_PHYSICAL(2) SIZE(2)
83 reg = <0x0 0x40000000 0x0 0x1000000>;
84
85 // BUS_ADDRESS(3) CPU_PHYSICAL(2) SIZE(2)
86 ranges = <0x01000000 0x0 0x01000000 0x0 0x01000000 0x0 0x00010000>,
87 <0x02000000 0x0 0x41000000 0x0 0x41000000 0x0 0x3f000000>;
88
89
90 #interrupt-cells = <0x1>;
91
92 // PCI_DEVICE(3) INT#(1) CONTROLLER(PHANDLE) CONTROLLER_DATA(3)
93 interrupt-map = < 0x0 0x0 0x0 0x1 &gic 0x0 0x4 0x1
94 0x800 0x0 0x0 0x1 &gic 0x0 0x5 0x1
95 0x1000 0x0 0x0 0x1 &gic 0x0 0x6 0x1
96 0x1800 0x0 0x0 0x1 &gic 0x0 0x7 0x1>;
97
98 // PCI_DEVICE(3) INT#(1)
99 interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
100}
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index a6f67ec8882f..32d446effbb3 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -33,4 +33,11 @@ config PCI_RCAR_GEN2
33 There are 3 internal PCI controllers available with a single 33 There are 3 internal PCI controllers available with a single
34 built-in EHCI/OHCI host controller present on each one. 34 built-in EHCI/OHCI host controller present on each one.
35 35
36config PCI_HOST_GENERIC
37 bool "Generic PCI host controller"
38 depends on ARM && OF
39 help
40 Say Y here if you want to support a simple generic PCI host
41 controller, such as the one emulated by kvmtool.
42
36endmenu 43endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 13fb3333aa05..bd1bf1ab4ac8 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
4obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o 4obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
5obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o 5obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
6obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o 6obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
7obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c
new file mode 100644
index 000000000000..44fe6aa6a43f
--- /dev/null
+++ b/drivers/pci/host/pci-host-generic.c
@@ -0,0 +1,388 @@
1/*
2 * Simple, generic PCI host controller driver targetting firmware-initialised
3 * systems and virtual machines (e.g. the PCI emulation provided by kvmtool).
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Copyright (C) 2014 ARM Limited
18 *
19 * Author: Will Deacon <will.deacon@arm.com>
20 */
21
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/of_address.h>
25#include <linux/of_pci.h>
26#include <linux/platform_device.h>
27
28struct gen_pci_cfg_bus_ops {
29 u32 bus_shift;
30 void __iomem *(*map_bus)(struct pci_bus *, unsigned int, int);
31};
32
33struct gen_pci_cfg_windows {
34 struct resource res;
35 struct resource bus_range;
36 void __iomem **win;
37
38 const struct gen_pci_cfg_bus_ops *ops;
39};
40
41struct gen_pci {
42 struct pci_host_bridge host;
43 struct gen_pci_cfg_windows cfg;
44 struct list_head resources;
45};
46
47static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
48 unsigned int devfn,
49 int where)
50{
51 struct pci_sys_data *sys = bus->sysdata;
52 struct gen_pci *pci = sys->private_data;
53 resource_size_t idx = bus->number - pci->cfg.bus_range.start;
54
55 return pci->cfg.win[idx] + ((devfn << 8) | where);
56}
57
58static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = {
59 .bus_shift = 16,
60 .map_bus = gen_pci_map_cfg_bus_cam,
61};
62
63static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
64 unsigned int devfn,
65 int where)
66{
67 struct pci_sys_data *sys = bus->sysdata;
68 struct gen_pci *pci = sys->private_data;
69 resource_size_t idx = bus->number - pci->cfg.bus_range.start;
70
71 return pci->cfg.win[idx] + ((devfn << 12) | where);
72}
73
74static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
75 .bus_shift = 20,
76 .map_bus = gen_pci_map_cfg_bus_ecam,
77};
78
79static int gen_pci_config_read(struct pci_bus *bus, unsigned int devfn,
80 int where, int size, u32 *val)
81{
82 void __iomem *addr;
83 struct pci_sys_data *sys = bus->sysdata;
84 struct gen_pci *pci = sys->private_data;
85
86 addr = pci->cfg.ops->map_bus(bus, devfn, where);
87
88 switch (size) {
89 case 1:
90 *val = readb(addr);
91 break;
92 case 2:
93 *val = readw(addr);
94 break;
95 default:
96 *val = readl(addr);
97 }
98
99 return PCIBIOS_SUCCESSFUL;
100}
101
102static int gen_pci_config_write(struct pci_bus *bus, unsigned int devfn,
103 int where, int size, u32 val)
104{
105 void __iomem *addr;
106 struct pci_sys_data *sys = bus->sysdata;
107 struct gen_pci *pci = sys->private_data;
108
109 addr = pci->cfg.ops->map_bus(bus, devfn, where);
110
111 switch (size) {
112 case 1:
113 writeb(val, addr);
114 break;
115 case 2:
116 writew(val, addr);
117 break;
118 default:
119 writel(val, addr);
120 }
121
122 return PCIBIOS_SUCCESSFUL;
123}
124
125static struct pci_ops gen_pci_ops = {
126 .read = gen_pci_config_read,
127 .write = gen_pci_config_write,
128};
129
130static const struct of_device_id gen_pci_of_match[] = {
131 { .compatible = "pci-host-cam-generic",
132 .data = &gen_pci_cfg_cam_bus_ops },
133
134 { .compatible = "pci-host-ecam-generic",
135 .data = &gen_pci_cfg_ecam_bus_ops },
136
137 { },
138};
139MODULE_DEVICE_TABLE(of, gen_pci_of_match);
140
141static int gen_pci_calc_io_offset(struct device *dev,
142 struct of_pci_range *range,
143 struct resource *res,
144 resource_size_t *offset)
145{
146 static atomic_t wins = ATOMIC_INIT(0);
147 int err, idx, max_win;
148 unsigned int window;
149
150 if (!PAGE_ALIGNED(range->cpu_addr))
151 return -EINVAL;
152
153 max_win = (IO_SPACE_LIMIT + 1) / SZ_64K;
154 idx = atomic_inc_return(&wins);
155 if (idx > max_win)
156 return -ENOSPC;
157
158 window = (idx - 1) * SZ_64K;
159 err = pci_ioremap_io(window, range->cpu_addr);
160 if (err)
161 return err;
162
163 of_pci_range_to_resource(range, dev->of_node, res);
164 res->start = window;
165 res->end = res->start + range->size - 1;
166 *offset = window - range->pci_addr;
167 return 0;
168}
169
170static int gen_pci_calc_mem_offset(struct device *dev,
171 struct of_pci_range *range,
172 struct resource *res,
173 resource_size_t *offset)
174{
175 of_pci_range_to_resource(range, dev->of_node, res);
176 *offset = range->cpu_addr - range->pci_addr;
177 return 0;
178}
179
180static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
181{
182 struct pci_host_bridge_window *win;
183
184 list_for_each_entry(win, &pci->resources, list)
185 release_resource(win->res);
186
187 pci_free_resource_list(&pci->resources);
188}
189
190static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
191{
192 struct of_pci_range range;
193 struct of_pci_range_parser parser;
194 int err, res_valid = 0;
195 struct device *dev = pci->host.dev.parent;
196 struct device_node *np = dev->of_node;
197
198 if (of_pci_range_parser_init(&parser, np)) {
199 dev_err(dev, "missing \"ranges\" property\n");
200 return -EINVAL;
201 }
202
203 for_each_of_pci_range(&parser, &range) {
204 struct resource *parent, *res;
205 resource_size_t offset;
206 u32 restype = range.flags & IORESOURCE_TYPE_BITS;
207
208 res = devm_kmalloc(dev, sizeof(*res), GFP_KERNEL);
209 if (!res) {
210 err = -ENOMEM;
211 goto out_release_res;
212 }
213
214 switch (restype) {
215 case IORESOURCE_IO:
216 parent = &ioport_resource;
217 err = gen_pci_calc_io_offset(dev, &range, res, &offset);
218 break;
219 case IORESOURCE_MEM:
220 parent = &iomem_resource;
221 err = gen_pci_calc_mem_offset(dev, &range, res, &offset);
222 res_valid |= !(res->flags & IORESOURCE_PREFETCH || err);
223 break;
224 default:
225 err = -EINVAL;
226 continue;
227 }
228
229 if (err) {
230 dev_warn(dev,
231 "error %d: failed to add resource [type 0x%x, %lld bytes]\n",
232 err, restype, range.size);
233 continue;
234 }
235
236 err = request_resource(parent, res);
237 if (err)
238 goto out_release_res;
239
240 pci_add_resource_offset(&pci->resources, res, offset);
241 }
242
243 if (!res_valid) {
244 dev_err(dev, "non-prefetchable memory resource required\n");
245 err = -EINVAL;
246 goto out_release_res;
247 }
248
249 return 0;
250
251out_release_res:
252 gen_pci_release_of_pci_ranges(pci);
253 return err;
254}
255
256static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
257{
258 int err;
259 u8 bus_max;
260 resource_size_t busn;
261 struct resource *bus_range;
262 struct device *dev = pci->host.dev.parent;
263 struct device_node *np = dev->of_node;
264
265 if (of_pci_parse_bus_range(np, &pci->cfg.bus_range))
266 pci->cfg.bus_range = (struct resource) {
267 .name = np->name,
268 .start = 0,
269 .end = 0xff,
270 .flags = IORESOURCE_BUS,
271 };
272
273 err = of_address_to_resource(np, 0, &pci->cfg.res);
274 if (err) {
275 dev_err(dev, "missing \"reg\" property\n");
276 return err;
277 }
278
279 pci->cfg.win = devm_kcalloc(dev, resource_size(&pci->cfg.bus_range),
280 sizeof(*pci->cfg.win), GFP_KERNEL);
281 if (!pci->cfg.win)
282 return -ENOMEM;
283
284 /* Limit the bus-range to fit within reg */
285 bus_max = pci->cfg.bus_range.start +
286 (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
287 pci->cfg.bus_range.end = min_t(resource_size_t, pci->cfg.bus_range.end,
288 bus_max);
289
290 /* Map our Configuration Space windows */
291 if (!devm_request_mem_region(dev, pci->cfg.res.start,
292 resource_size(&pci->cfg.res),
293 "Configuration Space"))
294 return -ENOMEM;
295
296 bus_range = &pci->cfg.bus_range;
297 for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
298 u32 idx = busn - bus_range->start;
299 u32 sz = 1 << pci->cfg.ops->bus_shift;
300
301 pci->cfg.win[idx] = devm_ioremap(dev,
302 pci->cfg.res.start + busn * sz,
303 sz);
304 if (!pci->cfg.win[idx])
305 return -ENOMEM;
306 }
307
308 /* Register bus resource */
309 pci_add_resource(&pci->resources, bus_range);
310 return 0;
311}
312
313static int gen_pci_setup(int nr, struct pci_sys_data *sys)
314{
315 struct gen_pci *pci = sys->private_data;
316 list_splice_init(&pci->resources, &sys->resources);
317 return 1;
318}
319
320static int gen_pci_probe(struct platform_device *pdev)
321{
322 int err;
323 const char *type;
324 const struct of_device_id *of_id;
325 const int *prop;
326 struct device *dev = &pdev->dev;
327 struct device_node *np = dev->of_node;
328 struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
329 struct hw_pci hw = {
330 .nr_controllers = 1,
331 .private_data = (void **)&pci,
332 .setup = gen_pci_setup,
333 .map_irq = of_irq_parse_and_map_pci,
334 .ops = &gen_pci_ops,
335 };
336
337 if (!pci)
338 return -ENOMEM;
339
340 type = of_get_property(np, "device_type", NULL);
341 if (!type || strcmp(type, "pci")) {
342 dev_err(dev, "invalid \"device_type\" %s\n", type);
343 return -EINVAL;
344 }
345
346 prop = of_get_property(of_chosen, "linux,pci-probe-only", NULL);
347 if (prop) {
348 if (*prop)
349 pci_add_flags(PCI_PROBE_ONLY);
350 else
351 pci_clear_flags(PCI_PROBE_ONLY);
352 }
353
354 of_id = of_match_node(gen_pci_of_match, np);
355 pci->cfg.ops = of_id->data;
356 pci->host.dev.parent = dev;
357 INIT_LIST_HEAD(&pci->host.windows);
358 INIT_LIST_HEAD(&pci->resources);
359
360 /* Parse our PCI ranges and request their resources */
361 err = gen_pci_parse_request_of_pci_ranges(pci);
362 if (err)
363 return err;
364
365 /* Parse and map our Configuration Space windows */
366 err = gen_pci_parse_map_cfg_windows(pci);
367 if (err) {
368 gen_pci_release_of_pci_ranges(pci);
369 return err;
370 }
371
372 pci_common_init_dev(dev, &hw);
373 return 0;
374}
375
376static struct platform_driver gen_pci_driver = {
377 .driver = {
378 .name = "pci-host-generic",
379 .owner = THIS_MODULE,
380 .of_match_table = gen_pci_of_match,
381 },
382 .probe = gen_pci_probe,
383};
384module_platform_driver(gen_pci_driver);
385
386MODULE_DESCRIPTION("Generic PCI host driver");
387MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
388MODULE_LICENSE("GPLv2");