diff options
Diffstat (limited to 'arch/sparc64/kernel/of_device.c')
-rw-r--r-- | arch/sparc64/kernel/of_device.c | 194 |
1 files changed, 188 insertions, 6 deletions
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index 9812cfa6dd36..3670dc8a7d5f 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c | |||
@@ -146,6 +146,26 @@ void of_iounmap(void __iomem *base, unsigned long size) | |||
146 | } | 146 | } |
147 | EXPORT_SYMBOL(of_iounmap); | 147 | EXPORT_SYMBOL(of_iounmap); |
148 | 148 | ||
149 | static int node_match(struct device *dev, void *data) | ||
150 | { | ||
151 | struct of_device *op = to_of_device(dev); | ||
152 | struct device_node *dp = data; | ||
153 | |||
154 | return (op->node == dp); | ||
155 | } | ||
156 | |||
157 | struct of_device *of_find_device_by_node(struct device_node *dp) | ||
158 | { | ||
159 | struct device *dev = bus_find_device(&of_bus_type, NULL, | ||
160 | dp, node_match); | ||
161 | |||
162 | if (dev) | ||
163 | return to_of_device(dev); | ||
164 | |||
165 | return NULL; | ||
166 | } | ||
167 | EXPORT_SYMBOL(of_find_device_by_node); | ||
168 | |||
149 | #ifdef CONFIG_PCI | 169 | #ifdef CONFIG_PCI |
150 | struct bus_type isa_bus_type = { | 170 | struct bus_type isa_bus_type = { |
151 | .name = "isa", | 171 | .name = "isa", |
@@ -261,7 +281,6 @@ static unsigned int of_bus_default_get_flags(u32 *addr) | |||
261 | return IORESOURCE_MEM; | 281 | return IORESOURCE_MEM; |
262 | } | 282 | } |
263 | 283 | ||
264 | |||
265 | /* | 284 | /* |
266 | * PCI bus specific translator | 285 | * PCI bus specific translator |
267 | */ | 286 | */ |
@@ -594,12 +613,171 @@ static void __init build_device_resources(struct of_device *op, | |||
594 | } | 613 | } |
595 | } | 614 | } |
596 | 615 | ||
616 | static struct device_node * __init | ||
617 | apply_interrupt_map(struct device_node *dp, struct device_node *pp, | ||
618 | u32 *imap, int imlen, u32 *imask, | ||
619 | unsigned int *irq_p) | ||
620 | { | ||
621 | struct device_node *cp; | ||
622 | unsigned int irq = *irq_p; | ||
623 | struct of_bus *bus; | ||
624 | phandle handle; | ||
625 | u32 *reg; | ||
626 | int na, num_reg, i; | ||
627 | |||
628 | bus = of_match_bus(pp); | ||
629 | bus->count_cells(dp, &na, NULL); | ||
630 | |||
631 | reg = of_get_property(dp, "reg", &num_reg); | ||
632 | if (!reg || !num_reg) | ||
633 | return NULL; | ||
634 | |||
635 | imlen /= ((na + 3) * 4); | ||
636 | handle = 0; | ||
637 | for (i = 0; i < imlen; i++) { | ||
638 | int j; | ||
639 | |||
640 | for (j = 0; j < na; j++) { | ||
641 | if ((reg[j] & imask[j]) != imap[j]) | ||
642 | goto next; | ||
643 | } | ||
644 | if (imap[na] == irq) { | ||
645 | handle = imap[na + 1]; | ||
646 | irq = imap[na + 2]; | ||
647 | break; | ||
648 | } | ||
649 | |||
650 | next: | ||
651 | imap += (na + 3); | ||
652 | } | ||
653 | if (i == imlen) | ||
654 | return NULL; | ||
655 | |||
656 | *irq_p = irq; | ||
657 | cp = of_find_node_by_phandle(handle); | ||
658 | |||
659 | return cp; | ||
660 | } | ||
661 | |||
662 | static unsigned int __init pci_irq_swizzle(struct device_node *dp, | ||
663 | struct device_node *pp, | ||
664 | unsigned int irq) | ||
665 | { | ||
666 | struct linux_prom_pci_registers *regs; | ||
667 | unsigned int devfn, slot, ret; | ||
668 | |||
669 | if (irq < 1 || irq > 4) | ||
670 | return irq; | ||
671 | |||
672 | regs = of_get_property(dp, "reg", NULL); | ||
673 | if (!regs) | ||
674 | return irq; | ||
675 | |||
676 | devfn = (regs->phys_hi >> 8) & 0xff; | ||
677 | slot = (devfn >> 3) & 0x1f; | ||
678 | |||
679 | ret = ((irq - 1 + (slot & 3)) & 3) + 1; | ||
680 | |||
681 | return ret; | ||
682 | } | ||
683 | |||
684 | static unsigned int __init build_one_device_irq(struct of_device *op, | ||
685 | struct device *parent, | ||
686 | unsigned int irq) | ||
687 | { | ||
688 | struct device_node *dp = op->node; | ||
689 | struct device_node *pp, *ip; | ||
690 | unsigned int orig_irq = irq; | ||
691 | |||
692 | if (irq == 0xffffffff) | ||
693 | return irq; | ||
694 | |||
695 | if (dp->irq_trans) { | ||
696 | irq = dp->irq_trans->irq_build(dp, irq, | ||
697 | dp->irq_trans->data); | ||
698 | #if 1 | ||
699 | printk("%s: direct translate %x --> %x\n", | ||
700 | dp->full_name, orig_irq, irq); | ||
701 | #endif | ||
702 | return irq; | ||
703 | } | ||
704 | |||
705 | /* Something more complicated. Walk up to the root, applying | ||
706 | * interrupt-map or bus specific translations, until we hit | ||
707 | * an IRQ translator. | ||
708 | * | ||
709 | * If we hit a bus type or situation we cannot handle, we | ||
710 | * stop and assume that the original IRQ number was in a | ||
711 | * format which has special meaning to it's immediate parent. | ||
712 | */ | ||
713 | pp = dp->parent; | ||
714 | ip = NULL; | ||
715 | while (pp) { | ||
716 | void *imap, *imsk; | ||
717 | int imlen; | ||
718 | |||
719 | imap = of_get_property(pp, "interrupt-map", &imlen); | ||
720 | imsk = of_get_property(pp, "interrupt-map-mask", NULL); | ||
721 | if (imap && imsk) { | ||
722 | struct device_node *iret; | ||
723 | int this_orig_irq = irq; | ||
724 | |||
725 | iret = apply_interrupt_map(dp, pp, | ||
726 | imap, imlen, imsk, | ||
727 | &irq); | ||
728 | #if 1 | ||
729 | printk("%s: Apply [%s:%x] imap --> [%s:%x]\n", | ||
730 | op->node->full_name, | ||
731 | pp->full_name, this_orig_irq, | ||
732 | (iret ? iret->full_name : "NULL"), irq); | ||
733 | #endif | ||
734 | if (!iret) | ||
735 | break; | ||
736 | |||
737 | if (iret->irq_trans) { | ||
738 | ip = iret; | ||
739 | break; | ||
740 | } | ||
741 | } else { | ||
742 | if (!strcmp(pp->type, "pci") || | ||
743 | !strcmp(pp->type, "pciex")) { | ||
744 | unsigned int this_orig_irq = irq; | ||
745 | |||
746 | irq = pci_irq_swizzle(dp, pp, irq); | ||
747 | #if 1 | ||
748 | printk("%s: PCI swizzle [%s] %x --> %x\n", | ||
749 | op->node->full_name, | ||
750 | pp->full_name, this_orig_irq, irq); | ||
751 | #endif | ||
752 | } | ||
753 | |||
754 | if (pp->irq_trans) { | ||
755 | ip = pp; | ||
756 | break; | ||
757 | } | ||
758 | } | ||
759 | dp = pp; | ||
760 | pp = pp->parent; | ||
761 | } | ||
762 | if (!ip) | ||
763 | return orig_irq; | ||
764 | |||
765 | irq = ip->irq_trans->irq_build(op->node, irq, | ||
766 | ip->irq_trans->data); | ||
767 | #if 1 | ||
768 | printk("%s: Apply IRQ trans [%s] %x --> %x\n", | ||
769 | op->node->full_name, ip->full_name, orig_irq, irq); | ||
770 | #endif | ||
771 | |||
772 | return irq; | ||
773 | } | ||
774 | |||
597 | static struct of_device * __init scan_one_device(struct device_node *dp, | 775 | static struct of_device * __init scan_one_device(struct device_node *dp, |
598 | struct device *parent) | 776 | struct device *parent) |
599 | { | 777 | { |
600 | struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL); | 778 | struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL); |
601 | unsigned int *irq; | 779 | unsigned int *irq; |
602 | int len; | 780 | int len, i; |
603 | 781 | ||
604 | if (!op) | 782 | if (!op) |
605 | return NULL; | 783 | return NULL; |
@@ -613,12 +791,16 @@ static struct of_device * __init scan_one_device(struct device_node *dp, | |||
613 | op->portid = of_getintprop_default(dp, "portid", -1); | 791 | op->portid = of_getintprop_default(dp, "portid", -1); |
614 | 792 | ||
615 | irq = of_get_property(dp, "interrupts", &len); | 793 | irq = of_get_property(dp, "interrupts", &len); |
616 | if (irq) | 794 | if (irq) { |
617 | op->irq = *irq; | 795 | memcpy(op->irqs, irq, len); |
618 | else | 796 | op->num_irqs = len / 4; |
619 | op->irq = 0xffffffff; | 797 | } else { |
798 | op->num_irqs = 0; | ||
799 | } | ||
620 | 800 | ||
621 | build_device_resources(op, parent); | 801 | build_device_resources(op, parent); |
802 | for (i = 0; i < op->num_irqs; i++) | ||
803 | op->irqs[i] = build_one_device_irq(op, parent, op->irqs[i]); | ||
622 | 804 | ||
623 | op->dev.parent = parent; | 805 | op->dev.parent = parent; |
624 | op->dev.bus = &of_bus_type; | 806 | op->dev.bus = &of_bus_type; |