aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/setup_64.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2005-11-23 01:56:06 -0500
committerPaul Mackerras <paulus@samba.org>2006-01-08 22:49:50 -0500
commit463ce0e103f419f51b1769111e73fe8bb305d0ec (patch)
treeb4ffced87b886d81b518790fcaf841dd006e8068 /arch/powerpc/kernel/setup_64.c
parentd1405b869850982f05c7ec0d3f137ca27588192f (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.c190
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
669static struct plat_serial8250_port serial_ports[MAX_LEGACY_SERIAL_PORTS+1];
670static unsigned int old_serial_count;
671
672void __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
825static struct platform_device serial_device = {
826 .name = "serial8250",
827 .id = PLAT8250_DEV_PLATFORM,
828 .dev = {
829 .platform_data = serial_ports,
830 },
831};
832
833static int __init serial_dev_init(void)
834{
835 return platform_device_register(&serial_device);
836}
837arch_initcall(serial_dev_init);
838
839#endif /* CONFIG_PPC_ISERIES */
840
841int check_legacy_ioport(unsigned long base_port) 669int 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)