diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-03-01 02:35:04 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 04:55:06 -0400 |
commit | a2fb23af1c31ad6e0c281e56d385f803229d57fa (patch) | |
tree | 9c093cd9cc639cfaac4e2b1057f5d45eb6ab69e3 /arch/sparc64/kernel/pci.c | |
parent | deb66c4521e119442aa266553e8cbfc86eb71232 (diff) |
[SPARC64]: Probe PCI bus using OF device tree.
Almost entirely taken from the 64-bit PowerPC PCI code.
This allowed to eliminate a ton of cruft from the sparc64
PCI layer.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/pci.c')
-rw-r--r-- | arch/sparc64/kernel/pci.c | 375 |
1 files changed, 357 insertions, 18 deletions
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 12109886bb1e..246b8009a2b4 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c | |||
@@ -1,9 +1,11 @@ | |||
1 | /* $Id: pci.c,v 1.39 2002/01/05 01:13:43 davem Exp $ | 1 | /* pci.c: UltraSparc PCI controller support. |
2 | * pci.c: UltraSparc PCI controller support. | ||
3 | * | 2 | * |
4 | * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com) | 3 | * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com) |
5 | * Copyright (C) 1998, 1999 Eddie C. Dost (ecd@skynet.be) | 4 | * Copyright (C) 1998, 1999 Eddie C. Dost (ecd@skynet.be) |
6 | * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) | 5 | * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) |
6 | * | ||
7 | * OF tree based PCI bus probing taken from the PowerPC port | ||
8 | * with minor modifications, see there for credits. | ||
7 | */ | 9 | */ |
8 | 10 | ||
9 | #include <linux/module.h> | 11 | #include <linux/module.h> |
@@ -300,6 +302,329 @@ static void __init pci_controller_probe(void) | |||
300 | pci_controller_scan(pci_controller_init); | 302 | pci_controller_scan(pci_controller_init); |
301 | } | 303 | } |
302 | 304 | ||
305 | static unsigned long pci_parse_of_flags(u32 addr0) | ||
306 | { | ||
307 | unsigned long flags = 0; | ||
308 | |||
309 | if (addr0 & 0x02000000) { | ||
310 | flags = IORESOURCE_MEM | PCI_BASE_ADDRESS_SPACE_MEMORY; | ||
311 | flags |= (addr0 >> 22) & PCI_BASE_ADDRESS_MEM_TYPE_64; | ||
312 | flags |= (addr0 >> 28) & PCI_BASE_ADDRESS_MEM_TYPE_1M; | ||
313 | if (addr0 & 0x40000000) | ||
314 | flags |= IORESOURCE_PREFETCH | ||
315 | | PCI_BASE_ADDRESS_MEM_PREFETCH; | ||
316 | } else if (addr0 & 0x01000000) | ||
317 | flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO; | ||
318 | return flags; | ||
319 | } | ||
320 | |||
321 | /* The of_device layer has translated all of the assigned-address properties | ||
322 | * into physical address resources, we only have to figure out the register | ||
323 | * mapping. | ||
324 | */ | ||
325 | static void pci_parse_of_addrs(struct of_device *op, | ||
326 | struct device_node *node, | ||
327 | struct pci_dev *dev) | ||
328 | { | ||
329 | struct resource *op_res; | ||
330 | const u32 *addrs; | ||
331 | int proplen; | ||
332 | |||
333 | addrs = of_get_property(node, "assigned-addresses", &proplen); | ||
334 | if (!addrs) | ||
335 | return; | ||
336 | printk(" parse addresses (%d bytes) @ %p\n", proplen, addrs); | ||
337 | op_res = &op->resource[0]; | ||
338 | for (; proplen >= 20; proplen -= 20, addrs += 5, op_res++) { | ||
339 | struct resource *res; | ||
340 | unsigned long flags; | ||
341 | int i; | ||
342 | |||
343 | flags = pci_parse_of_flags(addrs[0]); | ||
344 | if (!flags) | ||
345 | continue; | ||
346 | i = addrs[0] & 0xff; | ||
347 | printk(" start: %lx, end: %lx, i: %x\n", | ||
348 | op_res->start, op_res->end, i); | ||
349 | |||
350 | if (PCI_BASE_ADDRESS_0 <= i && i <= PCI_BASE_ADDRESS_5) { | ||
351 | res = &dev->resource[(i - PCI_BASE_ADDRESS_0) >> 2]; | ||
352 | } else if (i == dev->rom_base_reg) { | ||
353 | res = &dev->resource[PCI_ROM_RESOURCE]; | ||
354 | flags |= IORESOURCE_READONLY | IORESOURCE_CACHEABLE; | ||
355 | } else { | ||
356 | printk(KERN_ERR "PCI: bad cfg reg num 0x%x\n", i); | ||
357 | continue; | ||
358 | } | ||
359 | res->start = op_res->start; | ||
360 | res->end = op_res->end; | ||
361 | res->flags = flags; | ||
362 | res->name = pci_name(dev); | ||
363 | } | ||
364 | } | ||
365 | |||
366 | struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, | ||
367 | struct device_node *node, | ||
368 | struct pci_bus *bus, int devfn) | ||
369 | { | ||
370 | struct dev_archdata *sd; | ||
371 | struct pci_dev *dev; | ||
372 | const char *type; | ||
373 | |||
374 | dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); | ||
375 | if (!dev) | ||
376 | return NULL; | ||
377 | |||
378 | sd = &dev->dev.archdata; | ||
379 | sd->iommu = pbm->iommu; | ||
380 | sd->stc = &pbm->stc; | ||
381 | sd->host_controller = pbm; | ||
382 | sd->prom_node = node; | ||
383 | sd->op = of_find_device_by_node(node); | ||
384 | sd->msi_num = 0xffffffff; | ||
385 | |||
386 | type = of_get_property(node, "device_type", NULL); | ||
387 | if (type == NULL) | ||
388 | type = ""; | ||
389 | |||
390 | printk(" create device, devfn: %x, type: %s\n", devfn, type); | ||
391 | |||
392 | dev->bus = bus; | ||
393 | dev->sysdata = node; | ||
394 | dev->dev.parent = bus->bridge; | ||
395 | dev->dev.bus = &pci_bus_type; | ||
396 | dev->devfn = devfn; | ||
397 | dev->multifunction = 0; /* maybe a lie? */ | ||
398 | |||
399 | dev->vendor = of_getintprop_default(node, "vendor-id", 0xffff); | ||
400 | dev->device = of_getintprop_default(node, "device-id", 0xffff); | ||
401 | dev->subsystem_vendor = | ||
402 | of_getintprop_default(node, "subsystem-vendor-id", 0); | ||
403 | dev->subsystem_device = | ||
404 | of_getintprop_default(node, "subsystem-id", 0); | ||
405 | |||
406 | dev->cfg_size = pci_cfg_space_size(dev); | ||
407 | |||
408 | sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus), | ||
409 | dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); | ||
410 | dev->class = of_getintprop_default(node, "class-code", 0); | ||
411 | |||
412 | printk(" class: 0x%x\n", dev->class); | ||
413 | |||
414 | dev->current_state = 4; /* unknown power state */ | ||
415 | dev->error_state = pci_channel_io_normal; | ||
416 | |||
417 | if (!strcmp(type, "pci") || !strcmp(type, "pciex")) { | ||
418 | /* a PCI-PCI bridge */ | ||
419 | dev->hdr_type = PCI_HEADER_TYPE_BRIDGE; | ||
420 | dev->rom_base_reg = PCI_ROM_ADDRESS1; | ||
421 | } else if (!strcmp(type, "cardbus")) { | ||
422 | dev->hdr_type = PCI_HEADER_TYPE_CARDBUS; | ||
423 | } else { | ||
424 | dev->hdr_type = PCI_HEADER_TYPE_NORMAL; | ||
425 | dev->rom_base_reg = PCI_ROM_ADDRESS; | ||
426 | |||
427 | dev->irq = sd->op->irqs[0]; | ||
428 | if (dev->irq == 0xffffffff) | ||
429 | dev->irq = PCI_IRQ_NONE; | ||
430 | } | ||
431 | |||
432 | pci_parse_of_addrs(sd->op, node, dev); | ||
433 | |||
434 | printk(" adding to system ...\n"); | ||
435 | |||
436 | pci_device_add(dev, bus); | ||
437 | |||
438 | return dev; | ||
439 | } | ||
440 | |||
441 | static void __init pci_of_scan_bus(struct pci_pbm_info *pbm, | ||
442 | struct device_node *node, | ||
443 | struct pci_bus *bus); | ||
444 | |||
445 | #define GET_64BIT(prop, i) ((((u64) (prop)[(i)]) << 32) | (prop)[(i)+1]) | ||
446 | |||
447 | void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | ||
448 | struct device_node *node, | ||
449 | struct pci_dev *dev) | ||
450 | { | ||
451 | struct pci_bus *bus; | ||
452 | const u32 *busrange, *ranges; | ||
453 | int len, i; | ||
454 | struct resource *res; | ||
455 | unsigned int flags; | ||
456 | u64 size; | ||
457 | |||
458 | printk("of_scan_pci_bridge(%s)\n", node->full_name); | ||
459 | |||
460 | /* parse bus-range property */ | ||
461 | busrange = of_get_property(node, "bus-range", &len); | ||
462 | if (busrange == NULL || len != 8) { | ||
463 | printk(KERN_DEBUG "Can't get bus-range for PCI-PCI bridge %s\n", | ||
464 | node->full_name); | ||
465 | return; | ||
466 | } | ||
467 | ranges = of_get_property(node, "ranges", &len); | ||
468 | if (ranges == NULL) { | ||
469 | printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n", | ||
470 | node->full_name); | ||
471 | return; | ||
472 | } | ||
473 | |||
474 | bus = pci_add_new_bus(dev->bus, dev, busrange[0]); | ||
475 | if (!bus) { | ||
476 | printk(KERN_ERR "Failed to create pci bus for %s\n", | ||
477 | node->full_name); | ||
478 | return; | ||
479 | } | ||
480 | |||
481 | bus->primary = dev->bus->number; | ||
482 | bus->subordinate = busrange[1]; | ||
483 | bus->bridge_ctl = 0; | ||
484 | |||
485 | /* parse ranges property */ | ||
486 | /* PCI #address-cells == 3 and #size-cells == 2 always */ | ||
487 | res = &dev->resource[PCI_BRIDGE_RESOURCES]; | ||
488 | for (i = 0; i < PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES; ++i) { | ||
489 | res->flags = 0; | ||
490 | bus->resource[i] = res; | ||
491 | ++res; | ||
492 | } | ||
493 | i = 1; | ||
494 | for (; len >= 32; len -= 32, ranges += 8) { | ||
495 | struct resource *root; | ||
496 | |||
497 | flags = pci_parse_of_flags(ranges[0]); | ||
498 | size = GET_64BIT(ranges, 6); | ||
499 | if (flags == 0 || size == 0) | ||
500 | continue; | ||
501 | if (flags & IORESOURCE_IO) { | ||
502 | res = bus->resource[0]; | ||
503 | if (res->flags) { | ||
504 | printk(KERN_ERR "PCI: ignoring extra I/O range" | ||
505 | " for bridge %s\n", node->full_name); | ||
506 | continue; | ||
507 | } | ||
508 | root = &pbm->io_space; | ||
509 | } else { | ||
510 | if (i >= PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES) { | ||
511 | printk(KERN_ERR "PCI: too many memory ranges" | ||
512 | " for bridge %s\n", node->full_name); | ||
513 | continue; | ||
514 | } | ||
515 | res = bus->resource[i]; | ||
516 | ++i; | ||
517 | root = &pbm->mem_space; | ||
518 | } | ||
519 | |||
520 | res->start = GET_64BIT(ranges, 1); | ||
521 | res->end = res->start + size - 1; | ||
522 | res->flags = flags; | ||
523 | |||
524 | /* Another way to implement this would be to add an of_device | ||
525 | * layer routine that can calculate a resource for a given | ||
526 | * range property value in a PCI device. | ||
527 | */ | ||
528 | pbm->parent->resource_adjust(dev, res, root); | ||
529 | } | ||
530 | sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), | ||
531 | bus->number); | ||
532 | printk(" bus name: %s\n", bus->name); | ||
533 | |||
534 | pci_of_scan_bus(pbm, node, bus); | ||
535 | } | ||
536 | |||
537 | static void __init pci_of_scan_bus(struct pci_pbm_info *pbm, | ||
538 | struct device_node *node, | ||
539 | struct pci_bus *bus) | ||
540 | { | ||
541 | struct device_node *child; | ||
542 | const u32 *reg; | ||
543 | int reglen, devfn; | ||
544 | struct pci_dev *dev; | ||
545 | |||
546 | printk("PCI: scan_bus[%s] bus no %d\n", | ||
547 | node->full_name, bus->number); | ||
548 | |||
549 | child = NULL; | ||
550 | while ((child = of_get_next_child(node, child)) != NULL) { | ||
551 | printk(" * %s\n", child->full_name); | ||
552 | reg = of_get_property(child, "reg", ®len); | ||
553 | if (reg == NULL || reglen < 20) | ||
554 | continue; | ||
555 | devfn = (reg[0] >> 8) & 0xff; | ||
556 | |||
557 | /* create a new pci_dev for this device */ | ||
558 | dev = of_create_pci_dev(pbm, child, bus, devfn); | ||
559 | if (!dev) | ||
560 | continue; | ||
561 | printk("PCI: dev header type: %x\n", dev->hdr_type); | ||
562 | |||
563 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || | ||
564 | dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) | ||
565 | of_scan_pci_bridge(pbm, child, dev); | ||
566 | } | ||
567 | } | ||
568 | |||
569 | static ssize_t | ||
570 | show_pciobppath_attr(struct device * dev, struct device_attribute * attr, char * buf) | ||
571 | { | ||
572 | struct pci_dev *pdev; | ||
573 | struct device_node *dp; | ||
574 | |||
575 | pdev = to_pci_dev(dev); | ||
576 | dp = pdev->dev.archdata.prom_node; | ||
577 | |||
578 | return snprintf (buf, PAGE_SIZE, "%s\n", dp->full_name); | ||
579 | } | ||
580 | |||
581 | static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_pciobppath_attr, NULL); | ||
582 | |||
583 | static void __devinit pci_bus_register_of_sysfs(struct pci_bus *bus) | ||
584 | { | ||
585 | struct pci_dev *dev; | ||
586 | int err; | ||
587 | |||
588 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
589 | /* we don't really care if we can create this file or | ||
590 | * not, but we need to assign the result of the call | ||
591 | * or the world will fall under alien invasion and | ||
592 | * everybody will be frozen on a spaceship ready to be | ||
593 | * eaten on alpha centauri by some green and jelly | ||
594 | * humanoid. | ||
595 | */ | ||
596 | err = sysfs_create_file(&dev->dev.kobj, &dev_attr_obppath.attr); | ||
597 | } | ||
598 | } | ||
599 | |||
600 | struct pci_bus * __init pci_scan_one_pbm(struct pci_pbm_info *pbm) | ||
601 | { | ||
602 | struct pci_controller_info *p = pbm->parent; | ||
603 | struct device_node *node = pbm->prom_node; | ||
604 | struct pci_bus *bus; | ||
605 | |||
606 | printk("PCI: Scanning PBM %s\n", node->full_name); | ||
607 | |||
608 | /* XXX parent device? XXX */ | ||
609 | bus = pci_create_bus(NULL, pbm->pci_first_busno, p->pci_ops, pbm); | ||
610 | if (!bus) { | ||
611 | printk(KERN_ERR "Failed to create bus for %s\n", | ||
612 | node->full_name); | ||
613 | return NULL; | ||
614 | } | ||
615 | bus->secondary = pbm->pci_first_busno; | ||
616 | bus->subordinate = pbm->pci_last_busno; | ||
617 | |||
618 | bus->resource[0] = &pbm->io_space; | ||
619 | bus->resource[1] = &pbm->mem_space; | ||
620 | |||
621 | pci_of_scan_bus(pbm, node, bus); | ||
622 | pci_bus_add_devices(bus); | ||
623 | pci_bus_register_of_sysfs(bus); | ||
624 | |||
625 | return bus; | ||
626 | } | ||
627 | |||
303 | static void __init pci_scan_each_controller_bus(void) | 628 | static void __init pci_scan_each_controller_bus(void) |
304 | { | 629 | { |
305 | struct pci_controller_info *p; | 630 | struct pci_controller_info *p; |
@@ -360,8 +685,33 @@ void pcibios_align_resource(void *data, struct resource *res, | |||
360 | { | 685 | { |
361 | } | 686 | } |
362 | 687 | ||
363 | int pcibios_enable_device(struct pci_dev *pdev, int mask) | 688 | int pcibios_enable_device(struct pci_dev *dev, int mask) |
364 | { | 689 | { |
690 | u16 cmd, oldcmd; | ||
691 | int i; | ||
692 | |||
693 | pci_read_config_word(dev, PCI_COMMAND, &cmd); | ||
694 | oldcmd = cmd; | ||
695 | |||
696 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
697 | struct resource *res = &dev->resource[i]; | ||
698 | |||
699 | /* Only set up the requested stuff */ | ||
700 | if (!(mask & (1<<i))) | ||
701 | continue; | ||
702 | |||
703 | if (res->flags & IORESOURCE_IO) | ||
704 | cmd |= PCI_COMMAND_IO; | ||
705 | if (res->flags & IORESOURCE_MEM) | ||
706 | cmd |= PCI_COMMAND_MEMORY; | ||
707 | } | ||
708 | |||
709 | if (cmd != oldcmd) { | ||
710 | printk(KERN_DEBUG "PCI: Enabling device: (%s), cmd %x\n", | ||
711 | pci_name(dev), cmd); | ||
712 | /* Enable the appropriate bits in the PCI command register. */ | ||
713 | pci_write_config_word(dev, PCI_COMMAND, cmd); | ||
714 | } | ||
365 | return 0; | 715 | return 0; |
366 | } | 716 | } |
367 | 717 | ||
@@ -422,17 +772,10 @@ char * __devinit pcibios_setup(char *str) | |||
422 | static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struct *vma, | 772 | static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struct *vma, |
423 | enum pci_mmap_state mmap_state) | 773 | enum pci_mmap_state mmap_state) |
424 | { | 774 | { |
425 | struct pcidev_cookie *pcp = pdev->sysdata; | 775 | struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; |
426 | struct pci_pbm_info *pbm; | ||
427 | struct pci_controller_info *p; | 776 | struct pci_controller_info *p; |
428 | unsigned long space_size, user_offset, user_size; | 777 | unsigned long space_size, user_offset, user_size; |
429 | 778 | ||
430 | if (!pcp) | ||
431 | return -ENXIO; | ||
432 | pbm = pcp->pbm; | ||
433 | if (!pbm) | ||
434 | return -ENXIO; | ||
435 | |||
436 | p = pbm->parent; | 779 | p = pbm->parent; |
437 | if (p->pbms_same_domain) { | 780 | if (p->pbms_same_domain) { |
438 | unsigned long lowest, highest; | 781 | unsigned long lowest, highest; |
@@ -651,8 +994,7 @@ EXPORT_SYMBOL(pci_domain_nr); | |||
651 | #ifdef CONFIG_PCI_MSI | 994 | #ifdef CONFIG_PCI_MSI |
652 | int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) | 995 | int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) |
653 | { | 996 | { |
654 | struct pcidev_cookie *pcp = pdev->sysdata; | 997 | struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; |
655 | struct pci_pbm_info *pbm = pcp->pbm; | ||
656 | struct pci_controller_info *p = pbm->parent; | 998 | struct pci_controller_info *p = pbm->parent; |
657 | int virt_irq, err; | 999 | int virt_irq, err; |
658 | 1000 | ||
@@ -670,8 +1012,7 @@ void arch_teardown_msi_irq(unsigned int virt_irq) | |||
670 | { | 1012 | { |
671 | struct msi_desc *entry = get_irq_msi(virt_irq); | 1013 | struct msi_desc *entry = get_irq_msi(virt_irq); |
672 | struct pci_dev *pdev = entry->dev; | 1014 | struct pci_dev *pdev = entry->dev; |
673 | struct pcidev_cookie *pcp = pdev->sysdata; | 1015 | struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; |
674 | struct pci_pbm_info *pbm = pcp->pbm; | ||
675 | struct pci_controller_info *p = pbm->parent; | 1016 | struct pci_controller_info *p = pbm->parent; |
676 | 1017 | ||
677 | if (!pbm->msi_num || !p->setup_msi_irq) | 1018 | if (!pbm->msi_num || !p->setup_msi_irq) |
@@ -683,9 +1024,7 @@ void arch_teardown_msi_irq(unsigned int virt_irq) | |||
683 | 1024 | ||
684 | struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) | 1025 | struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) |
685 | { | 1026 | { |
686 | struct pcidev_cookie *pc = pdev->sysdata; | 1027 | return pdev->dev.archdata.prom_node; |
687 | |||
688 | return pc->op->node; | ||
689 | } | 1028 | } |
690 | EXPORT_SYMBOL(pci_device_to_OF_node); | 1029 | EXPORT_SYMBOL(pci_device_to_OF_node); |
691 | 1030 | ||