]>
Commit | Line | Data |
---|---|---|
d3c68e0a LW |
1 | /* |
2 | * Support for Faraday Technology FTPC100 PCI Controller | |
3 | * | |
4 | * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> | |
5 | * | |
6 | * Based on the out-of-tree OpenWRT patch for Cortina Gemini: | |
7 | * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com> | |
8 | * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> | |
9 | * Based on SL2312 PCI controller code | |
10 | * Storlink (C) 2003 | |
11 | */ | |
12 | ||
13 | #include <linux/init.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/of_address.h> | |
18 | #include <linux/of_device.h> | |
19 | #include <linux/of_irq.h> | |
20 | #include <linux/of_pci.h> | |
21 | #include <linux/pci.h> | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/irqdomain.h> | |
25 | #include <linux/irqchip/chained_irq.h> | |
26 | #include <linux/bitops.h> | |
27 | #include <linux/irq.h> | |
28 | ||
29 | /* | |
30 | * Special configuration registers directly in the first few words | |
31 | * in I/O space. | |
32 | */ | |
33 | #define PCI_IOSIZE 0x00 | |
34 | #define PCI_PROT 0x04 /* AHB protection */ | |
35 | #define PCI_CTRL 0x08 /* PCI control signal */ | |
36 | #define PCI_SOFTRST 0x10 /* Soft reset counter and response error enable */ | |
37 | #define PCI_CONFIG 0x28 /* PCI configuration command register */ | |
38 | #define PCI_DATA 0x2C | |
39 | ||
40 | #define FARADAY_PCI_PMC 0x40 /* Power management control */ | |
41 | #define FARADAY_PCI_PMCSR 0x44 /* Power management status */ | |
42 | #define FARADAY_PCI_CTRL1 0x48 /* Control register 1 */ | |
43 | #define FARADAY_PCI_CTRL2 0x4C /* Control register 2 */ | |
44 | #define FARADAY_PCI_MEM1_BASE_SIZE 0x50 /* Memory base and size #1 */ | |
45 | #define FARADAY_PCI_MEM2_BASE_SIZE 0x54 /* Memory base and size #2 */ | |
46 | #define FARADAY_PCI_MEM3_BASE_SIZE 0x58 /* Memory base and size #3 */ | |
47 | ||
48 | /* Bits 31..28 gives INTD..INTA status */ | |
49 | #define PCI_CTRL2_INTSTS_SHIFT 28 | |
50 | #define PCI_CTRL2_INTMASK_CMDERR BIT(27) | |
51 | #define PCI_CTRL2_INTMASK_PARERR BIT(26) | |
52 | /* Bits 25..22 masks INTD..INTA */ | |
53 | #define PCI_CTRL2_INTMASK_SHIFT 22 | |
54 | #define PCI_CTRL2_INTMASK_MABRT_RX BIT(21) | |
55 | #define PCI_CTRL2_INTMASK_TABRT_RX BIT(20) | |
56 | #define PCI_CTRL2_INTMASK_TABRT_TX BIT(19) | |
57 | #define PCI_CTRL2_INTMASK_RETRY4 BIT(18) | |
58 | #define PCI_CTRL2_INTMASK_SERR_RX BIT(17) | |
59 | #define PCI_CTRL2_INTMASK_PERR_RX BIT(16) | |
60 | /* Bit 15 reserved */ | |
61 | #define PCI_CTRL2_MSTPRI_REQ6 BIT(14) | |
62 | #define PCI_CTRL2_MSTPRI_REQ5 BIT(13) | |
63 | #define PCI_CTRL2_MSTPRI_REQ4 BIT(12) | |
64 | #define PCI_CTRL2_MSTPRI_REQ3 BIT(11) | |
65 | #define PCI_CTRL2_MSTPRI_REQ2 BIT(10) | |
66 | #define PCI_CTRL2_MSTPRI_REQ1 BIT(9) | |
67 | #define PCI_CTRL2_MSTPRI_REQ0 BIT(8) | |
68 | /* Bits 7..4 reserved */ | |
69 | /* Bits 3..0 TRDYW */ | |
70 | ||
71 | /* | |
72 | * Memory configs: | |
73 | * Bit 31..20 defines the PCI side memory base | |
74 | * Bit 19..16 (4 bits) defines the size per below | |
75 | */ | |
76 | #define FARADAY_PCI_MEMBASE_MASK 0xfff00000 | |
77 | #define FARADAY_PCI_MEMSIZE_1MB 0x0 | |
78 | #define FARADAY_PCI_MEMSIZE_2MB 0x1 | |
79 | #define FARADAY_PCI_MEMSIZE_4MB 0x2 | |
80 | #define FARADAY_PCI_MEMSIZE_8MB 0x3 | |
81 | #define FARADAY_PCI_MEMSIZE_16MB 0x4 | |
82 | #define FARADAY_PCI_MEMSIZE_32MB 0x5 | |
83 | #define FARADAY_PCI_MEMSIZE_64MB 0x6 | |
84 | #define FARADAY_PCI_MEMSIZE_128MB 0x7 | |
85 | #define FARADAY_PCI_MEMSIZE_256MB 0x8 | |
86 | #define FARADAY_PCI_MEMSIZE_512MB 0x9 | |
87 | #define FARADAY_PCI_MEMSIZE_1GB 0xa | |
88 | #define FARADAY_PCI_MEMSIZE_2GB 0xb | |
89 | #define FARADAY_PCI_MEMSIZE_SHIFT 16 | |
90 | ||
91 | /* | |
92 | * The DMA base is set to 0x0 for all memory segments, it reflects the | |
93 | * fact that the memory of the host system starts at 0x0. | |
94 | */ | |
95 | #define FARADAY_PCI_DMA_MEM1_BASE 0x00000000 | |
96 | #define FARADAY_PCI_DMA_MEM2_BASE 0x00000000 | |
97 | #define FARADAY_PCI_DMA_MEM3_BASE 0x00000000 | |
98 | ||
99 | /* Defines for PCI configuration command register */ | |
100 | #define PCI_CONF_ENABLE BIT(31) | |
101 | #define PCI_CONF_WHERE(r) ((r) & 0xFC) | |
102 | #define PCI_CONF_BUS(b) (((b) & 0xFF) << 16) | |
103 | #define PCI_CONF_DEVICE(d) (((d) & 0x1F) << 11) | |
104 | #define PCI_CONF_FUNCTION(f) (((f) & 0x07) << 8) | |
105 | ||
106 | /** | |
107 | * struct faraday_pci_variant - encodes IP block differences | |
108 | * @cascaded_irq: this host has cascaded IRQs from an interrupt controller | |
109 | * embedded in the host bridge. | |
110 | */ | |
111 | struct faraday_pci_variant { | |
112 | bool cascaded_irq; | |
113 | }; | |
114 | ||
115 | struct faraday_pci { | |
116 | struct device *dev; | |
117 | void __iomem *base; | |
118 | struct irq_domain *irqdomain; | |
119 | struct pci_bus *bus; | |
120 | }; | |
121 | ||
122 | static int faraday_res_to_memcfg(resource_size_t mem_base, | |
123 | resource_size_t mem_size, u32 *val) | |
124 | { | |
125 | u32 outval; | |
126 | ||
127 | switch (mem_size) { | |
128 | case SZ_1M: | |
129 | outval = FARADAY_PCI_MEMSIZE_1MB; | |
130 | break; | |
131 | case SZ_2M: | |
132 | outval = FARADAY_PCI_MEMSIZE_2MB; | |
133 | break; | |
134 | case SZ_4M: | |
135 | outval = FARADAY_PCI_MEMSIZE_4MB; | |
136 | break; | |
137 | case SZ_8M: | |
138 | outval = FARADAY_PCI_MEMSIZE_8MB; | |
139 | break; | |
140 | case SZ_16M: | |
141 | outval = FARADAY_PCI_MEMSIZE_16MB; | |
142 | break; | |
143 | case SZ_32M: | |
144 | outval = FARADAY_PCI_MEMSIZE_32MB; | |
145 | break; | |
146 | case SZ_64M: | |
147 | outval = FARADAY_PCI_MEMSIZE_64MB; | |
148 | break; | |
149 | case SZ_128M: | |
150 | outval = FARADAY_PCI_MEMSIZE_128MB; | |
151 | break; | |
152 | case SZ_256M: | |
153 | outval = FARADAY_PCI_MEMSIZE_256MB; | |
154 | break; | |
155 | case SZ_512M: | |
156 | outval = FARADAY_PCI_MEMSIZE_512MB; | |
157 | break; | |
158 | case SZ_1G: | |
159 | outval = FARADAY_PCI_MEMSIZE_1GB; | |
160 | break; | |
161 | case SZ_2G: | |
162 | outval = FARADAY_PCI_MEMSIZE_2GB; | |
163 | break; | |
164 | default: | |
165 | return -EINVAL; | |
166 | } | |
167 | outval <<= FARADAY_PCI_MEMSIZE_SHIFT; | |
168 | ||
169 | /* This is probably not good */ | |
170 | if (mem_base & ~(FARADAY_PCI_MEMBASE_MASK)) | |
171 | pr_warn("truncated PCI memory base\n"); | |
172 | /* Translate to bridge side address space */ | |
173 | outval |= (mem_base & FARADAY_PCI_MEMBASE_MASK); | |
174 | pr_debug("Translated pci base @%pap, size %pap to config %08x\n", | |
175 | &mem_base, &mem_size, outval); | |
176 | ||
177 | *val = outval; | |
178 | return 0; | |
179 | } | |
180 | ||
f1e8bd21 LP |
181 | static int faraday_raw_pci_read_config(struct faraday_pci *p, int bus_number, |
182 | unsigned int fn, int config, int size, | |
183 | u32 *value) | |
d3c68e0a | 184 | { |
f1e8bd21 | 185 | writel(PCI_CONF_BUS(bus_number) | |
d3c68e0a LW |
186 | PCI_CONF_DEVICE(PCI_SLOT(fn)) | |
187 | PCI_CONF_FUNCTION(PCI_FUNC(fn)) | | |
188 | PCI_CONF_WHERE(config) | | |
189 | PCI_CONF_ENABLE, | |
190 | p->base + PCI_CONFIG); | |
191 | ||
192 | *value = readl(p->base + PCI_DATA); | |
193 | ||
194 | if (size == 1) | |
195 | *value = (*value >> (8 * (config & 3))) & 0xFF; | |
196 | else if (size == 2) | |
197 | *value = (*value >> (8 * (config & 3))) & 0xFFFF; | |
198 | ||
f1e8bd21 LP |
199 | return PCIBIOS_SUCCESSFUL; |
200 | } | |
201 | ||
202 | static int faraday_pci_read_config(struct pci_bus *bus, unsigned int fn, | |
203 | int config, int size, u32 *value) | |
204 | { | |
205 | struct faraday_pci *p = bus->sysdata; | |
206 | ||
d3c68e0a LW |
207 | dev_dbg(&bus->dev, |
208 | "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", | |
209 | PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); | |
210 | ||
f1e8bd21 | 211 | return faraday_raw_pci_read_config(p, bus->number, fn, config, size, value); |
d3c68e0a LW |
212 | } |
213 | ||
f1e8bd21 LP |
214 | static int faraday_raw_pci_write_config(struct faraday_pci *p, int bus_number, |
215 | unsigned int fn, int config, int size, | |
216 | u32 value) | |
d3c68e0a | 217 | { |
d3c68e0a LW |
218 | int ret = PCIBIOS_SUCCESSFUL; |
219 | ||
f1e8bd21 | 220 | writel(PCI_CONF_BUS(bus_number) | |
d3c68e0a LW |
221 | PCI_CONF_DEVICE(PCI_SLOT(fn)) | |
222 | PCI_CONF_FUNCTION(PCI_FUNC(fn)) | | |
223 | PCI_CONF_WHERE(config) | | |
224 | PCI_CONF_ENABLE, | |
225 | p->base + PCI_CONFIG); | |
226 | ||
227 | switch (size) { | |
228 | case 4: | |
229 | writel(value, p->base + PCI_DATA); | |
230 | break; | |
231 | case 2: | |
232 | writew(value, p->base + PCI_DATA + (config & 3)); | |
233 | break; | |
234 | case 1: | |
235 | writeb(value, p->base + PCI_DATA + (config & 3)); | |
236 | break; | |
237 | default: | |
238 | ret = PCIBIOS_BAD_REGISTER_NUMBER; | |
239 | } | |
240 | ||
241 | return ret; | |
242 | } | |
243 | ||
f1e8bd21 LP |
244 | static int faraday_pci_write_config(struct pci_bus *bus, unsigned int fn, |
245 | int config, int size, u32 value) | |
246 | { | |
247 | struct faraday_pci *p = bus->sysdata; | |
248 | ||
249 | dev_dbg(&bus->dev, | |
250 | "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", | |
251 | PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); | |
252 | ||
253 | return faraday_raw_pci_write_config(p, bus->number, fn, config, size, | |
254 | value); | |
255 | } | |
256 | ||
d3c68e0a LW |
257 | static struct pci_ops faraday_pci_ops = { |
258 | .read = faraday_pci_read_config, | |
259 | .write = faraday_pci_write_config, | |
260 | }; | |
261 | ||
262 | static void faraday_pci_ack_irq(struct irq_data *d) | |
263 | { | |
264 | struct faraday_pci *p = irq_data_get_irq_chip_data(d); | |
265 | unsigned int reg; | |
266 | ||
f1e8bd21 | 267 | faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); |
d3c68e0a LW |
268 | reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); |
269 | reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTSTS_SHIFT); | |
f1e8bd21 | 270 | faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); |
d3c68e0a LW |
271 | } |
272 | ||
273 | static void faraday_pci_mask_irq(struct irq_data *d) | |
274 | { | |
275 | struct faraday_pci *p = irq_data_get_irq_chip_data(d); | |
276 | unsigned int reg; | |
277 | ||
f1e8bd21 | 278 | faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); |
d3c68e0a LW |
279 | reg &= ~((0xF << PCI_CTRL2_INTSTS_SHIFT) |
280 | | BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT)); | |
f1e8bd21 | 281 | faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); |
d3c68e0a LW |
282 | } |
283 | ||
284 | static void faraday_pci_unmask_irq(struct irq_data *d) | |
285 | { | |
286 | struct faraday_pci *p = irq_data_get_irq_chip_data(d); | |
287 | unsigned int reg; | |
288 | ||
f1e8bd21 | 289 | faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); |
d3c68e0a LW |
290 | reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); |
291 | reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT); | |
f1e8bd21 | 292 | faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); |
d3c68e0a LW |
293 | } |
294 | ||
295 | static void faraday_pci_irq_handler(struct irq_desc *desc) | |
296 | { | |
297 | struct faraday_pci *p = irq_desc_get_handler_data(desc); | |
298 | struct irq_chip *irqchip = irq_desc_get_chip(desc); | |
299 | unsigned int irq_stat, reg, i; | |
300 | ||
f1e8bd21 | 301 | faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); |
d3c68e0a LW |
302 | irq_stat = reg >> PCI_CTRL2_INTSTS_SHIFT; |
303 | ||
304 | chained_irq_enter(irqchip, desc); | |
305 | ||
306 | for (i = 0; i < 4; i++) { | |
307 | if ((irq_stat & BIT(i)) == 0) | |
308 | continue; | |
309 | generic_handle_irq(irq_find_mapping(p->irqdomain, i)); | |
310 | } | |
311 | ||
312 | chained_irq_exit(irqchip, desc); | |
313 | } | |
314 | ||
315 | static struct irq_chip faraday_pci_irq_chip = { | |
316 | .name = "PCI", | |
317 | .irq_ack = faraday_pci_ack_irq, | |
318 | .irq_mask = faraday_pci_mask_irq, | |
319 | .irq_unmask = faraday_pci_unmask_irq, | |
320 | }; | |
321 | ||
322 | static int faraday_pci_irq_map(struct irq_domain *domain, unsigned int irq, | |
323 | irq_hw_number_t hwirq) | |
324 | { | |
325 | irq_set_chip_and_handler(irq, &faraday_pci_irq_chip, handle_level_irq); | |
326 | irq_set_chip_data(irq, domain->host_data); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | static const struct irq_domain_ops faraday_pci_irqdomain_ops = { | |
332 | .map = faraday_pci_irq_map, | |
333 | }; | |
334 | ||
335 | static int faraday_pci_setup_cascaded_irq(struct faraday_pci *p) | |
336 | { | |
337 | struct device_node *intc = of_get_next_child(p->dev->of_node, NULL); | |
338 | int irq; | |
339 | int i; | |
340 | ||
341 | if (!intc) { | |
342 | dev_err(p->dev, "missing child interrupt-controller node\n"); | |
343 | return -EINVAL; | |
344 | } | |
345 | ||
346 | /* All PCI IRQs cascade off this one */ | |
347 | irq = of_irq_get(intc, 0); | |
348 | if (!irq) { | |
349 | dev_err(p->dev, "failed to get parent IRQ\n"); | |
350 | return -EINVAL; | |
351 | } | |
352 | ||
353 | p->irqdomain = irq_domain_add_linear(intc, 4, | |
354 | &faraday_pci_irqdomain_ops, p); | |
355 | if (!p->irqdomain) { | |
356 | dev_err(p->dev, "failed to create Gemini PCI IRQ domain\n"); | |
357 | return -EINVAL; | |
358 | } | |
359 | ||
360 | irq_set_chained_handler_and_data(irq, faraday_pci_irq_handler, p); | |
361 | ||
362 | for (i = 0; i < 4; i++) | |
363 | irq_create_mapping(p->irqdomain, i); | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, | |
369 | struct device_node *node) | |
370 | { | |
371 | const int na = 3, ns = 2; | |
372 | int rlen; | |
373 | ||
374 | parser->node = node; | |
375 | parser->pna = of_n_addr_cells(node); | |
376 | parser->np = parser->pna + na + ns; | |
377 | ||
378 | parser->range = of_get_property(node, "dma-ranges", &rlen); | |
379 | if (!parser->range) | |
380 | return -ENOENT; | |
381 | parser->end = parser->range + rlen / sizeof(__be32); | |
382 | ||
383 | return 0; | |
384 | } | |
385 | ||
386 | static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, | |
387 | struct device_node *np) | |
388 | { | |
389 | struct of_pci_range range; | |
390 | struct of_pci_range_parser parser; | |
391 | struct device *dev = p->dev; | |
392 | u32 confreg[3] = { | |
393 | FARADAY_PCI_MEM1_BASE_SIZE, | |
394 | FARADAY_PCI_MEM2_BASE_SIZE, | |
395 | FARADAY_PCI_MEM3_BASE_SIZE, | |
396 | }; | |
397 | int i = 0; | |
398 | u32 val; | |
399 | ||
400 | if (pci_dma_range_parser_init(&parser, np)) { | |
401 | dev_err(dev, "missing dma-ranges property\n"); | |
402 | return -EINVAL; | |
403 | } | |
404 | ||
405 | /* | |
406 | * Get the dma-ranges from the device tree | |
407 | */ | |
408 | for_each_of_pci_range(&parser, &range) { | |
409 | u64 end = range.pci_addr + range.size - 1; | |
410 | int ret; | |
411 | ||
412 | ret = faraday_res_to_memcfg(range.pci_addr, range.size, &val); | |
413 | if (ret) { | |
414 | dev_err(dev, | |
415 | "DMA range %d: illegal MEM resource size\n", i); | |
416 | return -EINVAL; | |
417 | } | |
418 | ||
419 | dev_info(dev, "DMA MEM%d BASE: 0x%016llx -> 0x%016llx config %08x\n", | |
420 | i + 1, range.pci_addr, end, val); | |
421 | if (i <= 2) { | |
f1e8bd21 LP |
422 | faraday_raw_pci_write_config(p, 0, 0, confreg[i], |
423 | 4, val); | |
d3c68e0a LW |
424 | } else { |
425 | dev_err(dev, "ignore extraneous dma-range %d\n", i); | |
426 | break; | |
427 | } | |
428 | ||
429 | i++; | |
430 | } | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | static int faraday_pci_probe(struct platform_device *pdev) | |
436 | { | |
437 | struct device *dev = &pdev->dev; | |
438 | const struct faraday_pci_variant *variant = | |
439 | of_device_get_match_data(dev); | |
440 | struct resource *regs; | |
441 | resource_size_t io_base; | |
442 | struct resource_entry *win; | |
443 | struct faraday_pci *p; | |
444 | struct resource *mem; | |
445 | struct resource *io; | |
446 | struct pci_host_bridge *host; | |
447 | int ret; | |
448 | u32 val; | |
449 | LIST_HEAD(res); | |
450 | ||
9aa17a77 | 451 | host = devm_pci_alloc_host_bridge(dev, sizeof(*p)); |
d3c68e0a LW |
452 | if (!host) |
453 | return -ENOMEM; | |
454 | ||
455 | host->dev.parent = dev; | |
456 | host->ops = &faraday_pci_ops; | |
457 | host->busnr = 0; | |
458 | host->msi = NULL; | |
459 | p = pci_host_bridge_priv(host); | |
460 | host->sysdata = p; | |
461 | p->dev = dev; | |
462 | ||
463 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
464 | p->base = devm_ioremap_resource(dev, regs); | |
465 | if (IS_ERR(p->base)) | |
466 | return PTR_ERR(p->base); | |
467 | ||
468 | ret = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff, | |
469 | &res, &io_base); | |
470 | if (ret) | |
471 | return ret; | |
472 | ||
473 | ret = devm_request_pci_bus_resources(dev, &res); | |
474 | if (ret) | |
475 | return ret; | |
476 | ||
477 | /* Get the I/O and memory ranges from DT */ | |
478 | resource_list_for_each_entry(win, &res) { | |
479 | switch (resource_type(win->res)) { | |
480 | case IORESOURCE_IO: | |
481 | io = win->res; | |
482 | io->name = "Gemini PCI I/O"; | |
483 | if (!faraday_res_to_memcfg(io->start - win->offset, | |
484 | resource_size(io), &val)) { | |
485 | /* setup I/O space size */ | |
486 | writel(val, p->base + PCI_IOSIZE); | |
487 | } else { | |
488 | dev_err(dev, "illegal IO mem size\n"); | |
489 | return -EINVAL; | |
490 | } | |
491 | ret = pci_remap_iospace(io, io_base); | |
492 | if (ret) { | |
493 | dev_warn(dev, "error %d: failed to map resource %pR\n", | |
494 | ret, io); | |
495 | continue; | |
496 | } | |
497 | break; | |
498 | case IORESOURCE_MEM: | |
499 | mem = win->res; | |
500 | mem->name = "Gemini PCI MEM"; | |
501 | break; | |
502 | case IORESOURCE_BUS: | |
503 | break; | |
504 | default: | |
505 | break; | |
506 | } | |
507 | } | |
508 | ||
509 | /* Setup hostbridge */ | |
510 | val = readl(p->base + PCI_CTRL); | |
511 | val |= PCI_COMMAND_IO; | |
512 | val |= PCI_COMMAND_MEMORY; | |
513 | val |= PCI_COMMAND_MASTER; | |
514 | writel(val, p->base + PCI_CTRL); | |
d3c68e0a | 515 | /* Mask and clear all interrupts */ |
f1e8bd21 | 516 | faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2 + 2, 2, 0xF000); |
d3c68e0a LW |
517 | if (variant->cascaded_irq) { |
518 | ret = faraday_pci_setup_cascaded_irq(p); | |
519 | if (ret) { | |
520 | dev_err(dev, "failed to setup cascaded IRQ\n"); | |
521 | return ret; | |
522 | } | |
523 | } | |
524 | ||
525 | ret = faraday_pci_parse_map_dma_ranges(p, dev->of_node); | |
526 | if (ret) | |
527 | return ret; | |
528 | ||
f1e8bd21 LP |
529 | list_splice_init(&res, &host->windows); |
530 | ret = pci_register_host_bridge(host); | |
531 | if (ret) { | |
532 | dev_err(dev, "failed to register host: %d\n", ret); | |
533 | return ret; | |
534 | } | |
535 | p->bus = host->bus; | |
536 | ||
d3c68e0a LW |
537 | pci_scan_child_bus(p->bus); |
538 | pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); | |
539 | pci_bus_assign_resources(p->bus); | |
540 | pci_bus_add_devices(p->bus); | |
541 | pci_free_resource_list(&res); | |
542 | ||
543 | return 0; | |
544 | } | |
545 | ||
546 | /* | |
547 | * We encode bridge variants here, we have at least two so it doesn't | |
548 | * hurt to have infrastructure to encompass future variants as well. | |
549 | */ | |
550 | const struct faraday_pci_variant faraday_regular = { | |
551 | .cascaded_irq = true, | |
552 | }; | |
553 | ||
554 | const struct faraday_pci_variant faraday_dual = { | |
555 | .cascaded_irq = false, | |
556 | }; | |
557 | ||
558 | static const struct of_device_id faraday_pci_of_match[] = { | |
559 | { | |
560 | .compatible = "faraday,ftpci100", | |
561 | .data = &faraday_regular, | |
562 | }, | |
563 | { | |
564 | .compatible = "faraday,ftpci100-dual", | |
565 | .data = &faraday_dual, | |
566 | }, | |
567 | {}, | |
568 | }; | |
569 | ||
570 | static struct platform_driver faraday_pci_driver = { | |
571 | .driver = { | |
572 | .name = "ftpci100", | |
573 | .of_match_table = of_match_ptr(faraday_pci_of_match), | |
a5f40e80 | 574 | .suppress_bind_attrs = true, |
d3c68e0a LW |
575 | }, |
576 | .probe = faraday_pci_probe, | |
577 | }; | |
578 | builtin_platform_driver(faraday_pci_driver); |