diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2005-11-23 01:56:06 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-01-08 22:49:50 -0500 |
commit | 463ce0e103f419f51b1769111e73fe8bb305d0ec (patch) | |
tree | b4ffced87b886d81b518790fcaf841dd006e8068 /arch/powerpc/kernel/setup_64.c | |
parent | d1405b869850982f05c7ec0d3f137ca27588192f (diff) |
[PATCH] powerpc: serial port discovery (#2)
This moves the discovery of legacy serial ports to a separate file,
makes it common to ppc32 and ppc64, and reworks it to use the new OF
address translators to get to the ports early. This new version can also
detect some PCI serial cards using legacy chips and will probably match
those discovered port with the default console choice.
Only ppc64 gets udbg still yet, unifying udbg isn't finished yet.
It also adds some speed-probing code to udbg so that the default console
can come up at the same speed it was set to by the firmware.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/setup_64.c')
-rw-r--r-- | arch/powerpc/kernel/setup_64.c | 190 |
1 files changed, 9 insertions, 181 deletions
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index e3fb78397dc6..0fc442ad1d26 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c | |||
@@ -459,6 +459,15 @@ void __init setup_system(void) | |||
459 | */ | 459 | */ |
460 | ppc_md.init_early(); | 460 | ppc_md.init_early(); |
461 | 461 | ||
462 | /* | ||
463 | * We can discover serial ports now since the above did setup the | ||
464 | * hash table management for us, thus ioremap works. We do that early | ||
465 | * so that further code can be debugged | ||
466 | */ | ||
467 | #ifdef CONFIG_PPC_MULTIPLATFORM | ||
468 | find_legacy_serial_ports(); | ||
469 | #endif | ||
470 | |||
462 | /* | 471 | /* |
463 | * "Finish" the device-tree, that is do the actual parsing of | 472 | * "Finish" the device-tree, that is do the actual parsing of |
464 | * some of the properties like the interrupt map | 473 | * some of the properties like the interrupt map |
@@ -657,187 +666,6 @@ void ppc64_terminate_msg(unsigned int src, const char *msg) | |||
657 | printk("[terminate]%04x %s\n", src, msg); | 666 | printk("[terminate]%04x %s\n", src, msg); |
658 | } | 667 | } |
659 | 668 | ||
660 | #ifndef CONFIG_PPC_ISERIES | ||
661 | /* | ||
662 | * This function can be used by platforms to "find" legacy serial ports. | ||
663 | * It works for "serial" nodes under an "isa" node, and will try to | ||
664 | * respect the "ibm,aix-loc" property if any. It works with up to 8 | ||
665 | * ports. | ||
666 | */ | ||
667 | |||
668 | #define MAX_LEGACY_SERIAL_PORTS 8 | ||
669 | static struct plat_serial8250_port serial_ports[MAX_LEGACY_SERIAL_PORTS+1]; | ||
670 | static unsigned int old_serial_count; | ||
671 | |||
672 | void __init generic_find_legacy_serial_ports(u64 *physport, | ||
673 | unsigned int *default_speed) | ||
674 | { | ||
675 | struct device_node *np; | ||
676 | u32 *sizeprop; | ||
677 | |||
678 | struct isa_reg_property { | ||
679 | u32 space; | ||
680 | u32 address; | ||
681 | u32 size; | ||
682 | }; | ||
683 | struct pci_reg_property { | ||
684 | struct pci_address addr; | ||
685 | u32 size_hi; | ||
686 | u32 size_lo; | ||
687 | }; | ||
688 | |||
689 | DBG(" -> generic_find_legacy_serial_port()\n"); | ||
690 | |||
691 | *physport = 0; | ||
692 | if (default_speed) | ||
693 | *default_speed = 0; | ||
694 | |||
695 | np = of_find_node_by_path("/"); | ||
696 | if (!np) | ||
697 | return; | ||
698 | |||
699 | /* First fill our array */ | ||
700 | for (np = NULL; (np = of_find_node_by_type(np, "serial"));) { | ||
701 | struct device_node *isa, *pci; | ||
702 | struct isa_reg_property *reg; | ||
703 | unsigned long phys_size, addr_size, io_base; | ||
704 | u32 *rangesp; | ||
705 | u32 *interrupts, *clk, *spd; | ||
706 | char *typep; | ||
707 | int index, rlen, rentsize; | ||
708 | |||
709 | /* Ok, first check if it's under an "isa" parent */ | ||
710 | isa = of_get_parent(np); | ||
711 | if (!isa || strcmp(isa->name, "isa")) { | ||
712 | DBG("%s: no isa parent found\n", np->full_name); | ||
713 | continue; | ||
714 | } | ||
715 | |||
716 | /* Now look for an "ibm,aix-loc" property that gives us ordering | ||
717 | * if any... | ||
718 | */ | ||
719 | typep = (char *)get_property(np, "ibm,aix-loc", NULL); | ||
720 | |||
721 | /* Get the ISA port number */ | ||
722 | reg = (struct isa_reg_property *)get_property(np, "reg", NULL); | ||
723 | if (reg == NULL) | ||
724 | goto next_port; | ||
725 | /* We assume the interrupt number isn't translated ... */ | ||
726 | interrupts = (u32 *)get_property(np, "interrupts", NULL); | ||
727 | /* get clock freq. if present */ | ||
728 | clk = (u32 *)get_property(np, "clock-frequency", NULL); | ||
729 | /* get default speed if present */ | ||
730 | spd = (u32 *)get_property(np, "current-speed", NULL); | ||
731 | /* Default to locate at end of array */ | ||
732 | index = old_serial_count; /* end of the array by default */ | ||
733 | |||
734 | /* If we have a location index, then use it */ | ||
735 | if (typep && *typep == 'S') { | ||
736 | index = simple_strtol(typep+1, NULL, 0) - 1; | ||
737 | /* if index is out of range, use end of array instead */ | ||
738 | if (index >= MAX_LEGACY_SERIAL_PORTS) | ||
739 | index = old_serial_count; | ||
740 | /* if our index is still out of range, that mean that | ||
741 | * array is full, we could scan for a free slot but that | ||
742 | * make little sense to bother, just skip the port | ||
743 | */ | ||
744 | if (index >= MAX_LEGACY_SERIAL_PORTS) | ||
745 | goto next_port; | ||
746 | if (index >= old_serial_count) | ||
747 | old_serial_count = index + 1; | ||
748 | /* Check if there is a port who already claimed our slot */ | ||
749 | if (serial_ports[index].iobase != 0) { | ||
750 | /* if we still have some room, move it, else override */ | ||
751 | if (old_serial_count < MAX_LEGACY_SERIAL_PORTS) { | ||
752 | DBG("Moved legacy port %d -> %d\n", index, | ||
753 | old_serial_count); | ||
754 | serial_ports[old_serial_count++] = | ||
755 | serial_ports[index]; | ||
756 | } else { | ||
757 | DBG("Replacing legacy port %d\n", index); | ||
758 | } | ||
759 | } | ||
760 | } | ||
761 | if (index >= MAX_LEGACY_SERIAL_PORTS) | ||
762 | goto next_port; | ||
763 | if (index >= old_serial_count) | ||
764 | old_serial_count = index + 1; | ||
765 | |||
766 | /* Now fill the entry */ | ||
767 | memset(&serial_ports[index], 0, sizeof(struct plat_serial8250_port)); | ||
768 | serial_ports[index].uartclk = clk ? *clk : BASE_BAUD * 16; | ||
769 | serial_ports[index].iobase = reg->address; | ||
770 | serial_ports[index].irq = interrupts ? interrupts[0] : 0; | ||
771 | serial_ports[index].flags = ASYNC_BOOT_AUTOCONF; | ||
772 | |||
773 | DBG("Added legacy port, index: %d, port: %x, irq: %d, clk: %d\n", | ||
774 | index, | ||
775 | serial_ports[index].iobase, | ||
776 | serial_ports[index].irq, | ||
777 | serial_ports[index].uartclk); | ||
778 | |||
779 | /* Get phys address of IO reg for port 1 */ | ||
780 | if (index != 0) | ||
781 | goto next_port; | ||
782 | |||
783 | pci = of_get_parent(isa); | ||
784 | if (!pci) { | ||
785 | DBG("%s: no pci parent found\n", np->full_name); | ||
786 | goto next_port; | ||
787 | } | ||
788 | |||
789 | rangesp = (u32 *)get_property(pci, "ranges", &rlen); | ||
790 | if (rangesp == NULL) { | ||
791 | of_node_put(pci); | ||
792 | goto next_port; | ||
793 | } | ||
794 | rlen /= 4; | ||
795 | |||
796 | /* we need the #size-cells of the PCI bridge node itself */ | ||
797 | phys_size = 1; | ||
798 | sizeprop = (u32 *)get_property(pci, "#size-cells", NULL); | ||
799 | if (sizeprop != NULL) | ||
800 | phys_size = *sizeprop; | ||
801 | /* we need the parent #addr-cells */ | ||
802 | addr_size = prom_n_addr_cells(pci); | ||
803 | rentsize = 3 + addr_size + phys_size; | ||
804 | io_base = 0; | ||
805 | for (;rlen >= rentsize; rlen -= rentsize,rangesp += rentsize) { | ||
806 | if (((rangesp[0] >> 24) & 0x3) != 1) | ||
807 | continue; /* not IO space */ | ||
808 | io_base = rangesp[3]; | ||
809 | if (addr_size == 2) | ||
810 | io_base = (io_base << 32) | rangesp[4]; | ||
811 | } | ||
812 | if (io_base != 0) { | ||
813 | *physport = io_base + reg->address; | ||
814 | if (default_speed && spd) | ||
815 | *default_speed = *spd; | ||
816 | } | ||
817 | of_node_put(pci); | ||
818 | next_port: | ||
819 | of_node_put(isa); | ||
820 | } | ||
821 | |||
822 | DBG(" <- generic_find_legacy_serial_port()\n"); | ||
823 | } | ||
824 | |||
825 | static struct platform_device serial_device = { | ||
826 | .name = "serial8250", | ||
827 | .id = PLAT8250_DEV_PLATFORM, | ||
828 | .dev = { | ||
829 | .platform_data = serial_ports, | ||
830 | }, | ||
831 | }; | ||
832 | |||
833 | static int __init serial_dev_init(void) | ||
834 | { | ||
835 | return platform_device_register(&serial_device); | ||
836 | } | ||
837 | arch_initcall(serial_dev_init); | ||
838 | |||
839 | #endif /* CONFIG_PPC_ISERIES */ | ||
840 | |||
841 | int check_legacy_ioport(unsigned long base_port) | 669 | int check_legacy_ioport(unsigned long base_port) |
842 | { | 670 | { |
843 | if (ppc_md.check_legacy_ioport == NULL) | 671 | if (ppc_md.check_legacy_ioport == NULL) |