1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Support for Faraday Technology FTPC100 PCI Controller |
4 | * |
5 | * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> |
6 | * |
7 | * Based on the out-of-tree OpenWRT patch for Cortina Gemini: |
8 | * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com> |
9 | * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> |
10 | * Based on SL2312 PCI controller code |
11 | * Storlink (C) 2003 |
12 | */ |
13 | |
14 | #include <linux/init.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/io.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/of.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 | #include <linux/clk.h> |
29 | |
30 | #include "../pci.h" |
31 | |
32 | /* |
33 | * Special configuration registers directly in the first few words |
34 | * in I/O space. |
35 | */ |
36 | #define FTPCI_IOSIZE 0x00 |
37 | #define FTPCI_PROT 0x04 /* AHB protection */ |
38 | #define FTPCI_CTRL 0x08 /* PCI control signal */ |
39 | #define FTPCI_SOFTRST 0x10 /* Soft reset counter and response error enable */ |
40 | #define FTPCI_CONFIG 0x28 /* PCI configuration command register */ |
41 | #define FTPCI_DATA 0x2C |
42 | |
43 | #define FARADAY_PCI_STATUS_CMD 0x04 /* Status and command */ |
44 | #define FARADAY_PCI_PMC 0x40 /* Power management control */ |
45 | #define FARADAY_PCI_PMCSR 0x44 /* Power management status */ |
46 | #define FARADAY_PCI_CTRL1 0x48 /* Control register 1 */ |
47 | #define FARADAY_PCI_CTRL2 0x4C /* Control register 2 */ |
48 | #define FARADAY_PCI_MEM1_BASE_SIZE 0x50 /* Memory base and size #1 */ |
49 | #define FARADAY_PCI_MEM2_BASE_SIZE 0x54 /* Memory base and size #2 */ |
50 | #define FARADAY_PCI_MEM3_BASE_SIZE 0x58 /* Memory base and size #3 */ |
51 | |
52 | #define PCI_STATUS_66MHZ_CAPABLE BIT(21) |
53 | |
54 | /* Bits 31..28 gives INTD..INTA status */ |
55 | #define PCI_CTRL2_INTSTS_SHIFT 28 |
56 | #define PCI_CTRL2_INTMASK_CMDERR BIT(27) |
57 | #define PCI_CTRL2_INTMASK_PARERR BIT(26) |
58 | /* Bits 25..22 masks INTD..INTA */ |
59 | #define PCI_CTRL2_INTMASK_SHIFT 22 |
60 | #define PCI_CTRL2_INTMASK_MABRT_RX BIT(21) |
61 | #define PCI_CTRL2_INTMASK_TABRT_RX BIT(20) |
62 | #define PCI_CTRL2_INTMASK_TABRT_TX BIT(19) |
63 | #define PCI_CTRL2_INTMASK_RETRY4 BIT(18) |
64 | #define PCI_CTRL2_INTMASK_SERR_RX BIT(17) |
65 | #define PCI_CTRL2_INTMASK_PERR_RX BIT(16) |
66 | /* Bit 15 reserved */ |
67 | #define PCI_CTRL2_MSTPRI_REQ6 BIT(14) |
68 | #define PCI_CTRL2_MSTPRI_REQ5 BIT(13) |
69 | #define PCI_CTRL2_MSTPRI_REQ4 BIT(12) |
70 | #define PCI_CTRL2_MSTPRI_REQ3 BIT(11) |
71 | #define PCI_CTRL2_MSTPRI_REQ2 BIT(10) |
72 | #define PCI_CTRL2_MSTPRI_REQ1 BIT(9) |
73 | #define PCI_CTRL2_MSTPRI_REQ0 BIT(8) |
74 | /* Bits 7..4 reserved */ |
75 | /* Bits 3..0 TRDYW */ |
76 | |
77 | /* |
78 | * Memory configs: |
79 | * Bit 31..20 defines the PCI side memory base |
80 | * Bit 19..16 (4 bits) defines the size per below |
81 | */ |
82 | #define FARADAY_PCI_MEMBASE_MASK 0xfff00000 |
83 | #define FARADAY_PCI_MEMSIZE_1MB 0x0 |
84 | #define FARADAY_PCI_MEMSIZE_2MB 0x1 |
85 | #define FARADAY_PCI_MEMSIZE_4MB 0x2 |
86 | #define FARADAY_PCI_MEMSIZE_8MB 0x3 |
87 | #define FARADAY_PCI_MEMSIZE_16MB 0x4 |
88 | #define FARADAY_PCI_MEMSIZE_32MB 0x5 |
89 | #define FARADAY_PCI_MEMSIZE_64MB 0x6 |
90 | #define FARADAY_PCI_MEMSIZE_128MB 0x7 |
91 | #define FARADAY_PCI_MEMSIZE_256MB 0x8 |
92 | #define FARADAY_PCI_MEMSIZE_512MB 0x9 |
93 | #define FARADAY_PCI_MEMSIZE_1GB 0xa |
94 | #define FARADAY_PCI_MEMSIZE_2GB 0xb |
95 | #define FARADAY_PCI_MEMSIZE_SHIFT 16 |
96 | |
97 | /* |
98 | * The DMA base is set to 0x0 for all memory segments, it reflects the |
99 | * fact that the memory of the host system starts at 0x0. |
100 | */ |
101 | #define FARADAY_PCI_DMA_MEM1_BASE 0x00000000 |
102 | #define FARADAY_PCI_DMA_MEM2_BASE 0x00000000 |
103 | #define FARADAY_PCI_DMA_MEM3_BASE 0x00000000 |
104 | |
105 | /** |
106 | * struct faraday_pci_variant - encodes IP block differences |
107 | * @cascaded_irq: this host has cascaded IRQs from an interrupt controller |
108 | * embedded in the host bridge. |
109 | */ |
110 | struct faraday_pci_variant { |
111 | bool cascaded_irq; |
112 | }; |
113 | |
114 | struct faraday_pci { |
115 | struct device *dev; |
116 | void __iomem *base; |
117 | struct irq_domain *irqdomain; |
118 | struct pci_bus *bus; |
119 | struct clk *bus_clk; |
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 | |
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) |
184 | { |
185 | writel(PCI_CONF1_ADDRESS(bus_number, PCI_SLOT(fn), |
186 | PCI_FUNC(fn), config), |
187 | addr: p->base + FTPCI_CONFIG); |
188 | |
189 | *value = readl(addr: p->base + FTPCI_DATA); |
190 | |
191 | if (size == 1) |
192 | *value = (*value >> (8 * (config & 3))) & 0xFF; |
193 | else if (size == 2) |
194 | *value = (*value >> (8 * (config & 3))) & 0xFFFF; |
195 | |
196 | return PCIBIOS_SUCCESSFUL; |
197 | } |
198 | |
199 | static int faraday_pci_read_config(struct pci_bus *bus, unsigned int fn, |
200 | int config, int size, u32 *value) |
201 | { |
202 | struct faraday_pci *p = bus->sysdata; |
203 | |
204 | dev_dbg(&bus->dev, |
205 | "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n" , |
206 | PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); |
207 | |
208 | return faraday_raw_pci_read_config(p, bus_number: bus->number, fn, config, size, value); |
209 | } |
210 | |
211 | static int faraday_raw_pci_write_config(struct faraday_pci *p, int bus_number, |
212 | unsigned int fn, int config, int size, |
213 | u32 value) |
214 | { |
215 | int ret = PCIBIOS_SUCCESSFUL; |
216 | |
217 | writel(PCI_CONF1_ADDRESS(bus_number, PCI_SLOT(fn), |
218 | PCI_FUNC(fn), config), |
219 | addr: p->base + FTPCI_CONFIG); |
220 | |
221 | switch (size) { |
222 | case 4: |
223 | writel(val: value, addr: p->base + FTPCI_DATA); |
224 | break; |
225 | case 2: |
226 | writew(val: value, addr: p->base + FTPCI_DATA + (config & 3)); |
227 | break; |
228 | case 1: |
229 | writeb(val: value, addr: p->base + FTPCI_DATA + (config & 3)); |
230 | break; |
231 | default: |
232 | ret = PCIBIOS_BAD_REGISTER_NUMBER; |
233 | } |
234 | |
235 | return ret; |
236 | } |
237 | |
238 | static int faraday_pci_write_config(struct pci_bus *bus, unsigned int fn, |
239 | int config, int size, u32 value) |
240 | { |
241 | struct faraday_pci *p = bus->sysdata; |
242 | |
243 | dev_dbg(&bus->dev, |
244 | "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n" , |
245 | PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); |
246 | |
247 | return faraday_raw_pci_write_config(p, bus_number: bus->number, fn, config, size, |
248 | value); |
249 | } |
250 | |
251 | static struct pci_ops faraday_pci_ops = { |
252 | .read = faraday_pci_read_config, |
253 | .write = faraday_pci_write_config, |
254 | }; |
255 | |
256 | static void faraday_pci_ack_irq(struct irq_data *d) |
257 | { |
258 | struct faraday_pci *p = irq_data_get_irq_chip_data(d); |
259 | unsigned int reg; |
260 | |
261 | faraday_raw_pci_read_config(p, bus_number: 0, fn: 0, FARADAY_PCI_CTRL2, size: 4, value: ®); |
262 | reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); |
263 | reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTSTS_SHIFT); |
264 | faraday_raw_pci_write_config(p, bus_number: 0, fn: 0, FARADAY_PCI_CTRL2, size: 4, value: reg); |
265 | } |
266 | |
267 | static void faraday_pci_mask_irq(struct irq_data *d) |
268 | { |
269 | struct faraday_pci *p = irq_data_get_irq_chip_data(d); |
270 | unsigned int reg; |
271 | |
272 | faraday_raw_pci_read_config(p, bus_number: 0, fn: 0, FARADAY_PCI_CTRL2, size: 4, value: ®); |
273 | reg &= ~((0xF << PCI_CTRL2_INTSTS_SHIFT) |
274 | | BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT)); |
275 | faraday_raw_pci_write_config(p, bus_number: 0, fn: 0, FARADAY_PCI_CTRL2, size: 4, value: reg); |
276 | } |
277 | |
278 | static void faraday_pci_unmask_irq(struct irq_data *d) |
279 | { |
280 | struct faraday_pci *p = irq_data_get_irq_chip_data(d); |
281 | unsigned int reg; |
282 | |
283 | faraday_raw_pci_read_config(p, bus_number: 0, fn: 0, FARADAY_PCI_CTRL2, size: 4, value: ®); |
284 | reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); |
285 | reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT); |
286 | faraday_raw_pci_write_config(p, bus_number: 0, fn: 0, FARADAY_PCI_CTRL2, size: 4, value: reg); |
287 | } |
288 | |
289 | static void faraday_pci_irq_handler(struct irq_desc *desc) |
290 | { |
291 | struct faraday_pci *p = irq_desc_get_handler_data(desc); |
292 | struct irq_chip *irqchip = irq_desc_get_chip(desc); |
293 | unsigned int irq_stat, reg, i; |
294 | |
295 | faraday_raw_pci_read_config(p, bus_number: 0, fn: 0, FARADAY_PCI_CTRL2, size: 4, value: ®); |
296 | irq_stat = reg >> PCI_CTRL2_INTSTS_SHIFT; |
297 | |
298 | chained_irq_enter(chip: irqchip, desc); |
299 | |
300 | for (i = 0; i < 4; i++) { |
301 | if ((irq_stat & BIT(i)) == 0) |
302 | continue; |
303 | generic_handle_domain_irq(domain: p->irqdomain, hwirq: i); |
304 | } |
305 | |
306 | chained_irq_exit(chip: irqchip, desc); |
307 | } |
308 | |
309 | static struct irq_chip faraday_pci_irq_chip = { |
310 | .name = "PCI" , |
311 | .irq_ack = faraday_pci_ack_irq, |
312 | .irq_mask = faraday_pci_mask_irq, |
313 | .irq_unmask = faraday_pci_unmask_irq, |
314 | }; |
315 | |
316 | static int faraday_pci_irq_map(struct irq_domain *domain, unsigned int irq, |
317 | irq_hw_number_t hwirq) |
318 | { |
319 | irq_set_chip_and_handler(irq, chip: &faraday_pci_irq_chip, handle: handle_level_irq); |
320 | irq_set_chip_data(irq, data: domain->host_data); |
321 | |
322 | return 0; |
323 | } |
324 | |
325 | static const struct irq_domain_ops faraday_pci_irqdomain_ops = { |
326 | .map = faraday_pci_irq_map, |
327 | }; |
328 | |
329 | static int faraday_pci_setup_cascaded_irq(struct faraday_pci *p) |
330 | { |
331 | struct device_node *intc = of_get_next_child(node: p->dev->of_node, NULL); |
332 | int irq; |
333 | int i; |
334 | |
335 | if (!intc) { |
336 | dev_err(p->dev, "missing child interrupt-controller node\n" ); |
337 | return -EINVAL; |
338 | } |
339 | |
340 | /* All PCI IRQs cascade off this one */ |
341 | irq = of_irq_get(dev: intc, index: 0); |
342 | if (irq <= 0) { |
343 | dev_err(p->dev, "failed to get parent IRQ\n" ); |
344 | of_node_put(node: intc); |
345 | return irq ?: -EINVAL; |
346 | } |
347 | |
348 | p->irqdomain = irq_domain_add_linear(of_node: intc, PCI_NUM_INTX, |
349 | ops: &faraday_pci_irqdomain_ops, host_data: p); |
350 | of_node_put(node: intc); |
351 | if (!p->irqdomain) { |
352 | dev_err(p->dev, "failed to create Gemini PCI IRQ domain\n" ); |
353 | return -EINVAL; |
354 | } |
355 | |
356 | irq_set_chained_handler_and_data(irq, handle: faraday_pci_irq_handler, data: p); |
357 | |
358 | for (i = 0; i < 4; i++) |
359 | irq_create_mapping(host: p->irqdomain, hwirq: i); |
360 | |
361 | return 0; |
362 | } |
363 | |
364 | static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p) |
365 | { |
366 | struct device *dev = p->dev; |
367 | struct pci_host_bridge *bridge = pci_host_bridge_from_priv(priv: p); |
368 | struct resource_entry *entry; |
369 | u32 confreg[3] = { |
370 | FARADAY_PCI_MEM1_BASE_SIZE, |
371 | FARADAY_PCI_MEM2_BASE_SIZE, |
372 | FARADAY_PCI_MEM3_BASE_SIZE, |
373 | }; |
374 | int i = 0; |
375 | u32 val; |
376 | |
377 | resource_list_for_each_entry(entry, &bridge->dma_ranges) { |
378 | u64 pci_addr = entry->res->start - entry->offset; |
379 | u64 end = entry->res->end - entry->offset; |
380 | int ret; |
381 | |
382 | ret = faraday_res_to_memcfg(mem_base: pci_addr, |
383 | mem_size: resource_size(res: entry->res), val: &val); |
384 | if (ret) { |
385 | dev_err(dev, |
386 | "DMA range %d: illegal MEM resource size\n" , i); |
387 | return -EINVAL; |
388 | } |
389 | |
390 | dev_info(dev, "DMA MEM%d BASE: 0x%016llx -> 0x%016llx config %08x\n" , |
391 | i + 1, pci_addr, end, val); |
392 | if (i <= 2) { |
393 | faraday_raw_pci_write_config(p, bus_number: 0, fn: 0, config: confreg[i], |
394 | size: 4, value: val); |
395 | } else { |
396 | dev_err(dev, "ignore extraneous dma-range %d\n" , i); |
397 | break; |
398 | } |
399 | |
400 | i++; |
401 | } |
402 | |
403 | return 0; |
404 | } |
405 | |
406 | static int faraday_pci_probe(struct platform_device *pdev) |
407 | { |
408 | struct device *dev = &pdev->dev; |
409 | const struct faraday_pci_variant *variant = |
410 | of_device_get_match_data(dev); |
411 | struct resource_entry *win; |
412 | struct faraday_pci *p; |
413 | struct resource *io; |
414 | struct pci_host_bridge *host; |
415 | struct clk *clk; |
416 | unsigned char max_bus_speed = PCI_SPEED_33MHz; |
417 | unsigned char cur_bus_speed = PCI_SPEED_33MHz; |
418 | int ret; |
419 | u32 val; |
420 | |
421 | host = devm_pci_alloc_host_bridge(dev, priv: sizeof(*p)); |
422 | if (!host) |
423 | return -ENOMEM; |
424 | |
425 | host->ops = &faraday_pci_ops; |
426 | p = pci_host_bridge_priv(bridge: host); |
427 | host->sysdata = p; |
428 | p->dev = dev; |
429 | |
430 | /* Retrieve and enable optional clocks */ |
431 | clk = devm_clk_get_enabled(dev, id: "PCLK" ); |
432 | if (IS_ERR(ptr: clk)) |
433 | return PTR_ERR(ptr: clk); |
434 | p->bus_clk = devm_clk_get_enabled(dev, id: "PCICLK" ); |
435 | if (IS_ERR(ptr: p->bus_clk)) |
436 | return PTR_ERR(ptr: p->bus_clk); |
437 | |
438 | p->base = devm_platform_ioremap_resource(pdev, index: 0); |
439 | if (IS_ERR(ptr: p->base)) |
440 | return PTR_ERR(ptr: p->base); |
441 | |
442 | win = resource_list_first_type(list: &host->windows, IORESOURCE_IO); |
443 | if (win) { |
444 | io = win->res; |
445 | if (!faraday_res_to_memcfg(mem_base: io->start - win->offset, |
446 | mem_size: resource_size(res: io), val: &val)) { |
447 | /* setup I/O space size */ |
448 | writel(val, addr: p->base + FTPCI_IOSIZE); |
449 | } else { |
450 | dev_err(dev, "illegal IO mem size\n" ); |
451 | return -EINVAL; |
452 | } |
453 | } |
454 | |
455 | /* Setup hostbridge */ |
456 | val = readl(addr: p->base + FTPCI_CTRL); |
457 | val |= PCI_COMMAND_IO; |
458 | val |= PCI_COMMAND_MEMORY; |
459 | val |= PCI_COMMAND_MASTER; |
460 | writel(val, addr: p->base + FTPCI_CTRL); |
461 | /* Mask and clear all interrupts */ |
462 | faraday_raw_pci_write_config(p, bus_number: 0, fn: 0, FARADAY_PCI_CTRL2 + 2, size: 2, value: 0xF000); |
463 | if (variant->cascaded_irq) { |
464 | ret = faraday_pci_setup_cascaded_irq(p); |
465 | if (ret) { |
466 | dev_err(dev, "failed to setup cascaded IRQ\n" ); |
467 | return ret; |
468 | } |
469 | } |
470 | |
471 | /* Check bus clock if we can gear up to 66 MHz */ |
472 | if (!IS_ERR(ptr: p->bus_clk)) { |
473 | unsigned long rate; |
474 | u32 val; |
475 | |
476 | faraday_raw_pci_read_config(p, bus_number: 0, fn: 0, |
477 | FARADAY_PCI_STATUS_CMD, size: 4, value: &val); |
478 | rate = clk_get_rate(clk: p->bus_clk); |
479 | |
480 | if ((rate == 33000000) && (val & PCI_STATUS_66MHZ_CAPABLE)) { |
481 | dev_info(dev, "33MHz bus is 66MHz capable\n" ); |
482 | max_bus_speed = PCI_SPEED_66MHz; |
483 | ret = clk_set_rate(clk: p->bus_clk, rate: 66000000); |
484 | if (ret) |
485 | dev_err(dev, "failed to set bus clock\n" ); |
486 | } else { |
487 | dev_info(dev, "33MHz only bus\n" ); |
488 | max_bus_speed = PCI_SPEED_33MHz; |
489 | } |
490 | |
491 | /* Bumping the clock may fail so read back the rate */ |
492 | rate = clk_get_rate(clk: p->bus_clk); |
493 | if (rate == 33000000) |
494 | cur_bus_speed = PCI_SPEED_33MHz; |
495 | if (rate == 66000000) |
496 | cur_bus_speed = PCI_SPEED_66MHz; |
497 | } |
498 | |
499 | ret = faraday_pci_parse_map_dma_ranges(p); |
500 | if (ret) |
501 | return ret; |
502 | |
503 | ret = pci_scan_root_bus_bridge(bridge: host); |
504 | if (ret) { |
505 | dev_err(dev, "failed to scan host: %d\n" , ret); |
506 | return ret; |
507 | } |
508 | p->bus = host->bus; |
509 | p->bus->max_bus_speed = max_bus_speed; |
510 | p->bus->cur_bus_speed = cur_bus_speed; |
511 | |
512 | pci_bus_assign_resources(bus: p->bus); |
513 | pci_bus_add_devices(bus: p->bus); |
514 | |
515 | return 0; |
516 | } |
517 | |
518 | /* |
519 | * We encode bridge variants here, we have at least two so it doesn't |
520 | * hurt to have infrastructure to encompass future variants as well. |
521 | */ |
522 | static const struct faraday_pci_variant faraday_regular = { |
523 | .cascaded_irq = true, |
524 | }; |
525 | |
526 | static const struct faraday_pci_variant faraday_dual = { |
527 | .cascaded_irq = false, |
528 | }; |
529 | |
530 | static const struct of_device_id faraday_pci_of_match[] = { |
531 | { |
532 | .compatible = "faraday,ftpci100" , |
533 | .data = &faraday_regular, |
534 | }, |
535 | { |
536 | .compatible = "faraday,ftpci100-dual" , |
537 | .data = &faraday_dual, |
538 | }, |
539 | {}, |
540 | }; |
541 | |
542 | static struct platform_driver faraday_pci_driver = { |
543 | .driver = { |
544 | .name = "ftpci100" , |
545 | .of_match_table = faraday_pci_of_match, |
546 | .suppress_bind_attrs = true, |
547 | }, |
548 | .probe = faraday_pci_probe, |
549 | }; |
550 | builtin_platform_driver(faraday_pci_driver); |
551 | |