diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2012-11-29 06:50:30 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-11-30 09:40:45 -0500 |
commit | cd24834130ac655d15accee6757e0eaeab4ad4ef (patch) | |
tree | 33be1efb537d6a08557a488f096c22516da0b3c2 /arch/s390/pci | |
parent | d07dc5d8ab6f15353c866e2768c389abdc1faba6 (diff) |
s390/pci: base support
Add PCI support for s390, (only 64 bit mode is supported by hardware):
- PCI facility tests
- PCI instructions: pcilg, pcistg, pcistb, stpcifc, mpcifc, rpcit
- map readb/w/l/q and writeb/w/l/q to pcilg and pcistg instructions
- pci_iomap implementation
- memcpy_fromio/toio
- pci_root_ops using special pcilg/pcistg
- device, bus and domain allocation
Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/pci')
-rw-r--r-- | arch/s390/pci/Makefile | 5 | ||||
-rw-r--r-- | arch/s390/pci/pci.c | 557 |
2 files changed, 562 insertions, 0 deletions
diff --git a/arch/s390/pci/Makefile b/arch/s390/pci/Makefile new file mode 100644 index 000000000000..78a1344ff7b1 --- /dev/null +++ b/arch/s390/pci/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for the s390 PCI subsystem. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_PCI) += pci.o | ||
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c new file mode 100644 index 000000000000..0b80ac7e158f --- /dev/null +++ b/arch/s390/pci/pci.c | |||
@@ -0,0 +1,557 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corp. 2012 | ||
3 | * | ||
4 | * Author(s): | ||
5 | * Jan Glauber <jang@linux.vnet.ibm.com> | ||
6 | * | ||
7 | * The System z PCI code is a rewrite from a prototype by | ||
8 | * the following people (Kudoz!): | ||
9 | * Alexander Schmidt <alexschm@de.ibm.com> | ||
10 | * Christoph Raisch <raisch@de.ibm.com> | ||
11 | * Hannes Hering <hering2@de.ibm.com> | ||
12 | * Hoang-Nam Nguyen <hnguyen@de.ibm.com> | ||
13 | * Jan-Bernd Themann <themann@de.ibm.com> | ||
14 | * Stefan Roscher <stefan.roscher@de.ibm.com> | ||
15 | * Thomas Klein <tklein@de.ibm.com> | ||
16 | */ | ||
17 | |||
18 | #define COMPONENT "zPCI" | ||
19 | #define pr_fmt(fmt) COMPONENT ": " fmt | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/export.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/seq_file.h> | ||
27 | #include <linux/pci.h> | ||
28 | #include <linux/msi.h> | ||
29 | |||
30 | #include <asm/facility.h> | ||
31 | #include <asm/pci_insn.h> | ||
32 | |||
33 | #define DEBUG /* enable pr_debug */ | ||
34 | |||
35 | #define ZPCI_NR_DMA_SPACES 1 | ||
36 | #define ZPCI_NR_DEVICES CONFIG_PCI_NR_FUNCTIONS | ||
37 | |||
38 | /* list of all detected zpci devices */ | ||
39 | LIST_HEAD(zpci_list); | ||
40 | DEFINE_MUTEX(zpci_list_lock); | ||
41 | |||
42 | static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES); | ||
43 | static DEFINE_SPINLOCK(zpci_domain_lock); | ||
44 | |||
45 | /* I/O Map */ | ||
46 | static DEFINE_SPINLOCK(zpci_iomap_lock); | ||
47 | static DECLARE_BITMAP(zpci_iomap, ZPCI_IOMAP_MAX_ENTRIES); | ||
48 | struct zpci_iomap_entry *zpci_iomap_start; | ||
49 | EXPORT_SYMBOL_GPL(zpci_iomap_start); | ||
50 | |||
51 | struct zpci_dev *get_zdev(struct pci_dev *pdev) | ||
52 | { | ||
53 | return (struct zpci_dev *) pdev->sysdata; | ||
54 | } | ||
55 | |||
56 | struct zpci_dev *get_zdev_by_fid(u32 fid) | ||
57 | { | ||
58 | struct zpci_dev *tmp, *zdev = NULL; | ||
59 | |||
60 | mutex_lock(&zpci_list_lock); | ||
61 | list_for_each_entry(tmp, &zpci_list, entry) { | ||
62 | if (tmp->fid == fid) { | ||
63 | zdev = tmp; | ||
64 | break; | ||
65 | } | ||
66 | } | ||
67 | mutex_unlock(&zpci_list_lock); | ||
68 | return zdev; | ||
69 | } | ||
70 | |||
71 | bool zpci_fid_present(u32 fid) | ||
72 | { | ||
73 | return (get_zdev_by_fid(fid) != NULL) ? true : false; | ||
74 | } | ||
75 | |||
76 | static struct zpci_dev *get_zdev_by_bus(struct pci_bus *bus) | ||
77 | { | ||
78 | return (bus && bus->sysdata) ? (struct zpci_dev *) bus->sysdata : NULL; | ||
79 | } | ||
80 | |||
81 | int pci_domain_nr(struct pci_bus *bus) | ||
82 | { | ||
83 | return ((struct zpci_dev *) bus->sysdata)->domain; | ||
84 | } | ||
85 | EXPORT_SYMBOL_GPL(pci_domain_nr); | ||
86 | |||
87 | int pci_proc_domain(struct pci_bus *bus) | ||
88 | { | ||
89 | return pci_domain_nr(bus); | ||
90 | } | ||
91 | EXPORT_SYMBOL_GPL(pci_proc_domain); | ||
92 | |||
93 | /* Store PCI function information block */ | ||
94 | static int zpci_store_fib(struct zpci_dev *zdev, u8 *fc) | ||
95 | { | ||
96 | struct zpci_fib *fib; | ||
97 | u8 status, cc; | ||
98 | |||
99 | fib = (void *) get_zeroed_page(GFP_KERNEL); | ||
100 | if (!fib) | ||
101 | return -ENOMEM; | ||
102 | |||
103 | do { | ||
104 | cc = __stpcifc(zdev->fh, 0, fib, &status); | ||
105 | if (cc == 2) { | ||
106 | msleep(ZPCI_INSN_BUSY_DELAY); | ||
107 | memset(fib, 0, PAGE_SIZE); | ||
108 | } | ||
109 | } while (cc == 2); | ||
110 | |||
111 | if (cc) | ||
112 | pr_err_once("%s: cc: %u status: %u\n", | ||
113 | __func__, cc, status); | ||
114 | |||
115 | /* Return PCI function controls */ | ||
116 | *fc = fib->fc; | ||
117 | |||
118 | free_page((unsigned long) fib); | ||
119 | return (cc) ? -EIO : 0; | ||
120 | } | ||
121 | |||
122 | #define ZPCI_PCIAS_CFGSPC 15 | ||
123 | |||
124 | static int zpci_cfg_load(struct zpci_dev *zdev, int offset, u32 *val, u8 len) | ||
125 | { | ||
126 | u64 req = ZPCI_CREATE_REQ(zdev->fh, ZPCI_PCIAS_CFGSPC, len); | ||
127 | u64 data; | ||
128 | int rc; | ||
129 | |||
130 | rc = pcilg_instr(&data, req, offset); | ||
131 | data = data << ((8 - len) * 8); | ||
132 | data = le64_to_cpu(data); | ||
133 | if (!rc) | ||
134 | *val = (u32) data; | ||
135 | else | ||
136 | *val = 0xffffffff; | ||
137 | return rc; | ||
138 | } | ||
139 | |||
140 | static int zpci_cfg_store(struct zpci_dev *zdev, int offset, u32 val, u8 len) | ||
141 | { | ||
142 | u64 req = ZPCI_CREATE_REQ(zdev->fh, ZPCI_PCIAS_CFGSPC, len); | ||
143 | u64 data = val; | ||
144 | int rc; | ||
145 | |||
146 | data = cpu_to_le64(data); | ||
147 | data = data >> ((8 - len) * 8); | ||
148 | rc = pcistg_instr(data, req, offset); | ||
149 | return rc; | ||
150 | } | ||
151 | |||
152 | void __devinit pcibios_fixup_bus(struct pci_bus *bus) | ||
153 | { | ||
154 | } | ||
155 | |||
156 | resource_size_t pcibios_align_resource(void *data, const struct resource *res, | ||
157 | resource_size_t size, | ||
158 | resource_size_t align) | ||
159 | { | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /* Create a virtual mapping cookie for a PCI BAR */ | ||
164 | void __iomem *pci_iomap(struct pci_dev *pdev, int bar, unsigned long max) | ||
165 | { | ||
166 | struct zpci_dev *zdev = get_zdev(pdev); | ||
167 | u64 addr; | ||
168 | int idx; | ||
169 | |||
170 | if ((bar & 7) != bar) | ||
171 | return NULL; | ||
172 | |||
173 | idx = zdev->bars[bar].map_idx; | ||
174 | spin_lock(&zpci_iomap_lock); | ||
175 | zpci_iomap_start[idx].fh = zdev->fh; | ||
176 | zpci_iomap_start[idx].bar = bar; | ||
177 | spin_unlock(&zpci_iomap_lock); | ||
178 | |||
179 | addr = ZPCI_IOMAP_ADDR_BASE | ((u64) idx << 48); | ||
180 | return (void __iomem *) addr; | ||
181 | } | ||
182 | EXPORT_SYMBOL_GPL(pci_iomap); | ||
183 | |||
184 | void pci_iounmap(struct pci_dev *pdev, void __iomem *addr) | ||
185 | { | ||
186 | unsigned int idx; | ||
187 | |||
188 | idx = (((__force u64) addr) & ~ZPCI_IOMAP_ADDR_BASE) >> 48; | ||
189 | spin_lock(&zpci_iomap_lock); | ||
190 | zpci_iomap_start[idx].fh = 0; | ||
191 | zpci_iomap_start[idx].bar = 0; | ||
192 | spin_unlock(&zpci_iomap_lock); | ||
193 | } | ||
194 | EXPORT_SYMBOL_GPL(pci_iounmap); | ||
195 | |||
196 | static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, | ||
197 | int size, u32 *val) | ||
198 | { | ||
199 | struct zpci_dev *zdev = get_zdev_by_bus(bus); | ||
200 | |||
201 | if (!zdev || devfn != ZPCI_DEVFN) | ||
202 | return 0; | ||
203 | return zpci_cfg_load(zdev, where, val, size); | ||
204 | } | ||
205 | |||
206 | static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, | ||
207 | int size, u32 val) | ||
208 | { | ||
209 | struct zpci_dev *zdev = get_zdev_by_bus(bus); | ||
210 | |||
211 | if (!zdev || devfn != ZPCI_DEVFN) | ||
212 | return 0; | ||
213 | return zpci_cfg_store(zdev, where, val, size); | ||
214 | } | ||
215 | |||
216 | static struct pci_ops pci_root_ops = { | ||
217 | .read = pci_read, | ||
218 | .write = pci_write, | ||
219 | }; | ||
220 | |||
221 | static void zpci_map_resources(struct zpci_dev *zdev) | ||
222 | { | ||
223 | struct pci_dev *pdev = zdev->pdev; | ||
224 | resource_size_t len; | ||
225 | int i; | ||
226 | |||
227 | for (i = 0; i < PCI_BAR_COUNT; i++) { | ||
228 | len = pci_resource_len(pdev, i); | ||
229 | if (!len) | ||
230 | continue; | ||
231 | pdev->resource[i].start = (resource_size_t) pci_iomap(pdev, i, 0); | ||
232 | pdev->resource[i].end = pdev->resource[i].start + len - 1; | ||
233 | pr_debug("BAR%i: -> start: %Lx end: %Lx\n", | ||
234 | i, pdev->resource[i].start, pdev->resource[i].end); | ||
235 | } | ||
236 | }; | ||
237 | |||
238 | static void zpci_unmap_resources(struct pci_dev *pdev) | ||
239 | { | ||
240 | resource_size_t len; | ||
241 | int i; | ||
242 | |||
243 | for (i = 0; i < PCI_BAR_COUNT; i++) { | ||
244 | len = pci_resource_len(pdev, i); | ||
245 | if (!len) | ||
246 | continue; | ||
247 | pci_iounmap(pdev, (void *) pdev->resource[i].start); | ||
248 | } | ||
249 | }; | ||
250 | |||
251 | struct zpci_dev *zpci_alloc_device(void) | ||
252 | { | ||
253 | struct zpci_dev *zdev; | ||
254 | |||
255 | /* Alloc memory for our private pci device data */ | ||
256 | zdev = kzalloc(sizeof(*zdev), GFP_KERNEL); | ||
257 | if (!zdev) | ||
258 | return ERR_PTR(-ENOMEM); | ||
259 | return zdev; | ||
260 | } | ||
261 | |||
262 | void zpci_free_device(struct zpci_dev *zdev) | ||
263 | { | ||
264 | kfree(zdev); | ||
265 | } | ||
266 | |||
267 | /* Called on removal of pci_dev, leaves zpci and bus device */ | ||
268 | static void zpci_remove_device(struct pci_dev *pdev) | ||
269 | { | ||
270 | struct zpci_dev *zdev = get_zdev(pdev); | ||
271 | |||
272 | dev_info(&pdev->dev, "Removing device %u\n", zdev->domain); | ||
273 | zdev->state = ZPCI_FN_STATE_CONFIGURED; | ||
274 | zpci_unmap_resources(pdev); | ||
275 | list_del(&zdev->entry); /* can be called from init */ | ||
276 | zdev->pdev = NULL; | ||
277 | } | ||
278 | |||
279 | static void zpci_scan_devices(void) | ||
280 | { | ||
281 | struct zpci_dev *zdev; | ||
282 | |||
283 | mutex_lock(&zpci_list_lock); | ||
284 | list_for_each_entry(zdev, &zpci_list, entry) | ||
285 | if (zdev->state == ZPCI_FN_STATE_CONFIGURED) | ||
286 | zpci_scan_device(zdev); | ||
287 | mutex_unlock(&zpci_list_lock); | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * Too late for any s390 specific setup, since interrupts must be set up | ||
292 | * already which requires DMA setup too and the pci scan will access the | ||
293 | * config space, which only works if the function handle is enabled. | ||
294 | */ | ||
295 | int pcibios_enable_device(struct pci_dev *pdev, int mask) | ||
296 | { | ||
297 | struct resource *res; | ||
298 | u16 cmd; | ||
299 | int i; | ||
300 | |||
301 | pci_read_config_word(pdev, PCI_COMMAND, &cmd); | ||
302 | |||
303 | for (i = 0; i < PCI_BAR_COUNT; i++) { | ||
304 | res = &pdev->resource[i]; | ||
305 | |||
306 | if (res->flags & IORESOURCE_IO) | ||
307 | return -EINVAL; | ||
308 | |||
309 | if (res->flags & IORESOURCE_MEM) | ||
310 | cmd |= PCI_COMMAND_MEMORY; | ||
311 | } | ||
312 | pci_write_config_word(pdev, PCI_COMMAND, cmd); | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | void pcibios_disable_device(struct pci_dev *pdev) | ||
317 | { | ||
318 | zpci_remove_device(pdev); | ||
319 | pdev->sysdata = NULL; | ||
320 | } | ||
321 | |||
322 | static struct resource *zpci_alloc_bus_resource(unsigned long start, unsigned long size, | ||
323 | unsigned long flags, int domain) | ||
324 | { | ||
325 | struct resource *r; | ||
326 | char *name; | ||
327 | int rc; | ||
328 | |||
329 | r = kzalloc(sizeof(*r), GFP_KERNEL); | ||
330 | if (!r) | ||
331 | return ERR_PTR(-ENOMEM); | ||
332 | r->start = start; | ||
333 | r->end = r->start + size - 1; | ||
334 | r->flags = flags; | ||
335 | r->parent = &iomem_resource; | ||
336 | name = kmalloc(18, GFP_KERNEL); | ||
337 | if (!name) { | ||
338 | kfree(r); | ||
339 | return ERR_PTR(-ENOMEM); | ||
340 | } | ||
341 | sprintf(name, "PCI Bus: %04x:%02x", domain, ZPCI_BUS_NR); | ||
342 | r->name = name; | ||
343 | |||
344 | rc = request_resource(&iomem_resource, r); | ||
345 | if (rc) | ||
346 | pr_debug("request resource %pR failed\n", r); | ||
347 | return r; | ||
348 | } | ||
349 | |||
350 | static int zpci_alloc_iomap(struct zpci_dev *zdev) | ||
351 | { | ||
352 | int entry; | ||
353 | |||
354 | spin_lock(&zpci_iomap_lock); | ||
355 | entry = find_first_zero_bit(zpci_iomap, ZPCI_IOMAP_MAX_ENTRIES); | ||
356 | if (entry == ZPCI_IOMAP_MAX_ENTRIES) { | ||
357 | spin_unlock(&zpci_iomap_lock); | ||
358 | return -ENOSPC; | ||
359 | } | ||
360 | set_bit(entry, zpci_iomap); | ||
361 | spin_unlock(&zpci_iomap_lock); | ||
362 | return entry; | ||
363 | } | ||
364 | |||
365 | static void zpci_free_iomap(struct zpci_dev *zdev, int entry) | ||
366 | { | ||
367 | spin_lock(&zpci_iomap_lock); | ||
368 | memset(&zpci_iomap_start[entry], 0, sizeof(struct zpci_iomap_entry)); | ||
369 | clear_bit(entry, zpci_iomap); | ||
370 | spin_unlock(&zpci_iomap_lock); | ||
371 | } | ||
372 | |||
373 | static int zpci_create_device_bus(struct zpci_dev *zdev) | ||
374 | { | ||
375 | struct resource *res; | ||
376 | LIST_HEAD(resources); | ||
377 | int i; | ||
378 | |||
379 | /* allocate mapping entry for each used bar */ | ||
380 | for (i = 0; i < PCI_BAR_COUNT; i++) { | ||
381 | unsigned long addr, size, flags; | ||
382 | int entry; | ||
383 | |||
384 | if (!zdev->bars[i].size) | ||
385 | continue; | ||
386 | entry = zpci_alloc_iomap(zdev); | ||
387 | if (entry < 0) | ||
388 | return entry; | ||
389 | zdev->bars[i].map_idx = entry; | ||
390 | |||
391 | /* only MMIO is supported */ | ||
392 | flags = IORESOURCE_MEM; | ||
393 | if (zdev->bars[i].val & 8) | ||
394 | flags |= IORESOURCE_PREFETCH; | ||
395 | if (zdev->bars[i].val & 4) | ||
396 | flags |= IORESOURCE_MEM_64; | ||
397 | |||
398 | addr = ZPCI_IOMAP_ADDR_BASE + ((u64) entry << 48); | ||
399 | |||
400 | size = 1UL << zdev->bars[i].size; | ||
401 | |||
402 | res = zpci_alloc_bus_resource(addr, size, flags, zdev->domain); | ||
403 | if (IS_ERR(res)) { | ||
404 | zpci_free_iomap(zdev, entry); | ||
405 | return PTR_ERR(res); | ||
406 | } | ||
407 | pci_add_resource(&resources, res); | ||
408 | } | ||
409 | |||
410 | zdev->bus = pci_create_root_bus(NULL, ZPCI_BUS_NR, &pci_root_ops, | ||
411 | zdev, &resources); | ||
412 | if (!zdev->bus) | ||
413 | return -EIO; | ||
414 | |||
415 | zdev->bus->max_bus_speed = zdev->max_bus_speed; | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int zpci_alloc_domain(struct zpci_dev *zdev) | ||
420 | { | ||
421 | spin_lock(&zpci_domain_lock); | ||
422 | zdev->domain = find_first_zero_bit(zpci_domain, ZPCI_NR_DEVICES); | ||
423 | if (zdev->domain == ZPCI_NR_DEVICES) { | ||
424 | spin_unlock(&zpci_domain_lock); | ||
425 | return -ENOSPC; | ||
426 | } | ||
427 | set_bit(zdev->domain, zpci_domain); | ||
428 | spin_unlock(&zpci_domain_lock); | ||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static void zpci_free_domain(struct zpci_dev *zdev) | ||
433 | { | ||
434 | spin_lock(&zpci_domain_lock); | ||
435 | clear_bit(zdev->domain, zpci_domain); | ||
436 | spin_unlock(&zpci_domain_lock); | ||
437 | } | ||
438 | |||
439 | int zpci_create_device(struct zpci_dev *zdev) | ||
440 | { | ||
441 | int rc; | ||
442 | |||
443 | rc = zpci_alloc_domain(zdev); | ||
444 | if (rc) | ||
445 | goto out; | ||
446 | |||
447 | rc = zpci_create_device_bus(zdev); | ||
448 | if (rc) | ||
449 | goto out_bus; | ||
450 | |||
451 | mutex_lock(&zpci_list_lock); | ||
452 | list_add_tail(&zdev->entry, &zpci_list); | ||
453 | mutex_unlock(&zpci_list_lock); | ||
454 | |||
455 | if (zdev->state == ZPCI_FN_STATE_STANDBY) | ||
456 | return 0; | ||
457 | |||
458 | return 0; | ||
459 | |||
460 | out_bus: | ||
461 | zpci_free_domain(zdev); | ||
462 | out: | ||
463 | return rc; | ||
464 | } | ||
465 | |||
466 | void zpci_stop_device(struct zpci_dev *zdev) | ||
467 | { | ||
468 | /* | ||
469 | * Note: SCLP disables fh via set-pci-fn so don't | ||
470 | * do that here. | ||
471 | */ | ||
472 | } | ||
473 | EXPORT_SYMBOL_GPL(zpci_stop_device); | ||
474 | |||
475 | int zpci_scan_device(struct zpci_dev *zdev) | ||
476 | { | ||
477 | zdev->pdev = pci_scan_single_device(zdev->bus, ZPCI_DEVFN); | ||
478 | if (!zdev->pdev) { | ||
479 | pr_err("pci_scan_single_device failed for fid: 0x%x\n", | ||
480 | zdev->fid); | ||
481 | goto out; | ||
482 | } | ||
483 | |||
484 | zpci_map_resources(zdev); | ||
485 | pci_bus_add_devices(zdev->bus); | ||
486 | |||
487 | /* now that pdev was added to the bus mark it as used */ | ||
488 | zdev->state = ZPCI_FN_STATE_ONLINE; | ||
489 | return 0; | ||
490 | |||
491 | out: | ||
492 | return -EIO; | ||
493 | } | ||
494 | EXPORT_SYMBOL_GPL(zpci_scan_device); | ||
495 | |||
496 | static inline int barsize(u8 size) | ||
497 | { | ||
498 | return (size) ? (1 << size) >> 10 : 0; | ||
499 | } | ||
500 | |||
501 | static int zpci_mem_init(void) | ||
502 | { | ||
503 | /* TODO: use realloc */ | ||
504 | zpci_iomap_start = kzalloc(ZPCI_IOMAP_MAX_ENTRIES * sizeof(*zpci_iomap_start), | ||
505 | GFP_KERNEL); | ||
506 | if (!zpci_iomap_start) | ||
507 | goto error_zdev; | ||
508 | return 0; | ||
509 | |||
510 | error_zdev: | ||
511 | return -ENOMEM; | ||
512 | } | ||
513 | |||
514 | static void zpci_mem_exit(void) | ||
515 | { | ||
516 | kfree(zpci_iomap_start); | ||
517 | } | ||
518 | |||
519 | unsigned int pci_probe = 1; | ||
520 | EXPORT_SYMBOL_GPL(pci_probe); | ||
521 | |||
522 | char * __init pcibios_setup(char *str) | ||
523 | { | ||
524 | if (!strcmp(str, "off")) { | ||
525 | pci_probe = 0; | ||
526 | return NULL; | ||
527 | } | ||
528 | return str; | ||
529 | } | ||
530 | |||
531 | static int __init pci_base_init(void) | ||
532 | { | ||
533 | int rc; | ||
534 | |||
535 | if (!pci_probe) | ||
536 | return 0; | ||
537 | |||
538 | if (!test_facility(2) || !test_facility(69) | ||
539 | || !test_facility(71) || !test_facility(72)) | ||
540 | return 0; | ||
541 | |||
542 | pr_info("Probing PCI hardware: PCI:%d SID:%d AEN:%d\n", | ||
543 | test_facility(69), test_facility(70), | ||
544 | test_facility(71)); | ||
545 | |||
546 | rc = zpci_mem_init(); | ||
547 | if (rc) | ||
548 | goto out_mem; | ||
549 | |||
550 | zpci_scan_devices(); | ||
551 | return 0; | ||
552 | |||
553 | zpci_mem_exit(); | ||
554 | out_mem: | ||
555 | return rc; | ||
556 | } | ||
557 | subsys_initcall(pci_base_init); | ||