aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/controller/pci-versatile.c
diff options
context:
space:
mode:
authorShawn Lin <shawn.lin@rock-chips.com>2018-05-30 21:12:37 -0400
committerBjorn Helgaas <bhelgaas@google.com>2018-06-08 08:50:11 -0400
commit6e0832fa432ec99c94caee733c8f5851cf85560b (patch)
treec4326f9e2d8ff1a6cb17e959fc5268c9e577ca94 /drivers/pci/controller/pci-versatile.c
parent3a3869f1c443383ef8354ffa0e5fb8df65d8b549 (diff)
PCI: Collect all native drivers under drivers/pci/controller/
Native PCI drivers for root complex devices were originally all in drivers/pci/host/. Some of these devices can also be operated in endpoint mode. Drivers for endpoint mode didn't seem to fit in the "host" directory, so we put both the root complex and endpoint drivers in per-device directories, e.g., drivers/pci/dwc/, drivers/pci/cadence/, etc. These per-device directories contain trivial Kconfig and Makefiles and clutter drivers/pci/. Make a new drivers/pci/controllers/ directory and collect all the device-specific drivers there. No functional change intended. Link: https://lkml.kernel.org/r/1520304202-232891-1-git-send-email-shawn.lin@rock-chips.com Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com> [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/controller/pci-versatile.c')
-rw-r--r--drivers/pci/controller/pci-versatile.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c
new file mode 100644
index 000000000000..994f32061b32
--- /dev/null
+++ b/drivers/pci/controller/pci-versatile.c
@@ -0,0 +1,239 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2004 Koninklijke Philips Electronics NV
4 *
5 * Conversion to platform driver and DT:
6 * Copyright 2014 Linaro Ltd.
7 *
8 * 14/04/2005 Initial version, colin.king@philips.com
9 */
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/of_address.h>
13#include <linux/of_pci.h>
14#include <linux/of_platform.h>
15#include <linux/pci.h>
16#include <linux/platform_device.h>
17
18#include "../pci.h"
19
20static void __iomem *versatile_pci_base;
21static void __iomem *versatile_cfg_base[2];
22
23#define PCI_IMAP(m) (versatile_pci_base + ((m) * 4))
24#define PCI_SMAP(m) (versatile_pci_base + 0x14 + ((m) * 4))
25#define PCI_SELFID (versatile_pci_base + 0xc)
26
27#define VP_PCI_DEVICE_ID 0x030010ee
28#define VP_PCI_CLASS_ID 0x0b400000
29
30static u32 pci_slot_ignore;
31
32static int __init versatile_pci_slot_ignore(char *str)
33{
34 int retval;
35 int slot;
36
37 while ((retval = get_option(&str, &slot))) {
38 if ((slot < 0) || (slot > 31))
39 pr_err("Illegal slot value: %d\n", slot);
40 else
41 pci_slot_ignore |= (1 << slot);
42 }
43 return 1;
44}
45__setup("pci_slot_ignore=", versatile_pci_slot_ignore);
46
47
48static void __iomem *versatile_map_bus(struct pci_bus *bus,
49 unsigned int devfn, int offset)
50{
51 unsigned int busnr = bus->number;
52
53 if (pci_slot_ignore & (1 << PCI_SLOT(devfn)))
54 return NULL;
55
56 return versatile_cfg_base[1] + ((busnr << 16) | (devfn << 8) | offset);
57}
58
59static struct pci_ops pci_versatile_ops = {
60 .map_bus = versatile_map_bus,
61 .read = pci_generic_config_read32,
62 .write = pci_generic_config_write,
63};
64
65static int versatile_pci_parse_request_of_pci_ranges(struct device *dev,
66 struct list_head *res)
67{
68 int err, mem = 1, res_valid = 0;
69 resource_size_t iobase;
70 struct resource_entry *win, *tmp;
71
72 err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, res, &iobase);
73 if (err)
74 return err;
75
76 err = devm_request_pci_bus_resources(dev, res);
77 if (err)
78 goto out_release_res;
79
80 resource_list_for_each_entry_safe(win, tmp, res) {
81 struct resource *res = win->res;
82
83 switch (resource_type(res)) {
84 case IORESOURCE_IO:
85 err = pci_remap_iospace(res, iobase);
86 if (err) {
87 dev_warn(dev, "error %d: failed to map resource %pR\n",
88 err, res);
89 resource_list_destroy_entry(win);
90 }
91 break;
92 case IORESOURCE_MEM:
93 res_valid |= !(res->flags & IORESOURCE_PREFETCH);
94
95 writel(res->start >> 28, PCI_IMAP(mem));
96 writel(PHYS_OFFSET >> 28, PCI_SMAP(mem));
97 mem++;
98
99 break;
100 }
101 }
102
103 if (res_valid)
104 return 0;
105
106 dev_err(dev, "non-prefetchable memory resource required\n");
107 err = -EINVAL;
108
109out_release_res:
110 pci_free_resource_list(res);
111 return err;
112}
113
114static int versatile_pci_probe(struct platform_device *pdev)
115{
116 struct device *dev = &pdev->dev;
117 struct resource *res;
118 int ret, i, myslot = -1;
119 u32 val;
120 void __iomem *local_pci_cfg_base;
121 struct pci_bus *bus, *child;
122 struct pci_host_bridge *bridge;
123 LIST_HEAD(pci_res);
124
125 bridge = devm_pci_alloc_host_bridge(dev, 0);
126 if (!bridge)
127 return -ENOMEM;
128
129 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
130 versatile_pci_base = devm_ioremap_resource(dev, res);
131 if (IS_ERR(versatile_pci_base))
132 return PTR_ERR(versatile_pci_base);
133
134 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
135 versatile_cfg_base[0] = devm_ioremap_resource(dev, res);
136 if (IS_ERR(versatile_cfg_base[0]))
137 return PTR_ERR(versatile_cfg_base[0]);
138
139 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
140 versatile_cfg_base[1] = devm_pci_remap_cfg_resource(dev, res);
141 if (IS_ERR(versatile_cfg_base[1]))
142 return PTR_ERR(versatile_cfg_base[1]);
143
144 ret = versatile_pci_parse_request_of_pci_ranges(dev, &pci_res);
145 if (ret)
146 return ret;
147
148 /*
149 * We need to discover the PCI core first to configure itself
150 * before the main PCI probing is performed
151 */
152 for (i = 0; i < 32; i++) {
153 if ((readl(versatile_cfg_base[0] + (i << 11) + PCI_VENDOR_ID) == VP_PCI_DEVICE_ID) &&
154 (readl(versatile_cfg_base[0] + (i << 11) + PCI_CLASS_REVISION) == VP_PCI_CLASS_ID)) {
155 myslot = i;
156 break;
157 }
158 }
159 if (myslot == -1) {
160 dev_err(dev, "Cannot find PCI core!\n");
161 return -EIO;
162 }
163 /*
164 * Do not to map Versatile FPGA PCI device into memory space
165 */
166 pci_slot_ignore |= (1 << myslot);
167
168 dev_info(dev, "PCI core found (slot %d)\n", myslot);
169
170 writel(myslot, PCI_SELFID);
171 local_pci_cfg_base = versatile_cfg_base[1] + (myslot << 11);
172
173 val = readl(local_pci_cfg_base + PCI_COMMAND);
174 val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
175 writel(val, local_pci_cfg_base + PCI_COMMAND);
176
177 /*
178 * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM
179 */
180 writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0);
181 writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1);
182 writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2);
183
184 /*
185 * For many years the kernel and QEMU were symbiotically buggy
186 * in that they both assumed the same broken IRQ mapping.
187 * QEMU therefore attempts to auto-detect old broken kernels
188 * so that they still work on newer QEMU as they did on old
189 * QEMU. Since we now use the correct (ie matching-hardware)
190 * IRQ mapping we write a definitely different value to a
191 * PCI_INTERRUPT_LINE register to tell QEMU that we expect
192 * real hardware behaviour and it need not be backwards
193 * compatible for us. This write is harmless on real hardware.
194 */
195 writel(0, versatile_cfg_base[0] + PCI_INTERRUPT_LINE);
196
197 pci_add_flags(PCI_ENABLE_PROC_DOMAINS);
198 pci_add_flags(PCI_REASSIGN_ALL_BUS);
199
200 list_splice_init(&pci_res, &bridge->windows);
201 bridge->dev.parent = dev;
202 bridge->sysdata = NULL;
203 bridge->busnr = 0;
204 bridge->ops = &pci_versatile_ops;
205 bridge->map_irq = of_irq_parse_and_map_pci;
206 bridge->swizzle_irq = pci_common_swizzle;
207
208 ret = pci_scan_root_bus_bridge(bridge);
209 if (ret < 0)
210 return ret;
211
212 bus = bridge->bus;
213
214 pci_assign_unassigned_bus_resources(bus);
215 list_for_each_entry(child, &bus->children, node)
216 pcie_bus_configure_settings(child);
217 pci_bus_add_devices(bus);
218
219 return 0;
220}
221
222static const struct of_device_id versatile_pci_of_match[] = {
223 { .compatible = "arm,versatile-pci", },
224 { },
225};
226MODULE_DEVICE_TABLE(of, versatile_pci_of_match);
227
228static struct platform_driver versatile_pci_driver = {
229 .driver = {
230 .name = "versatile-pci",
231 .of_match_table = versatile_pci_of_match,
232 .suppress_bind_attrs = true,
233 },
234 .probe = versatile_pci_probe,
235};
236module_platform_driver(versatile_pci_driver);
237
238MODULE_DESCRIPTION("Versatile PCI driver");
239MODULE_LICENSE("GPL v2");