aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/cavium-octeon/octeon-platform.c
diff options
context:
space:
mode:
authorDavid Daney <david.daney@cavium.com>2012-07-05 12:12:38 -0400
committerRalf Baechle <ralf@linux-mips.org>2012-07-23 08:54:52 -0400
commit7ed1815296498e9d1bfa1f13e94b743364b14caf (patch)
treef8988180f1827f9a0f9e251ae79afc80745ec7f6 /arch/mips/cavium-octeon/octeon-platform.c
parentb01da9f130adbf69cfbad2a65f1327f1382bf4ae (diff)
MIPS: Octeon: Initialize and fixup device tree.
If a compiled in device tree template is used, trim out unwanted parts based on legacy platform probing. Signed-off-by: David Daney <david.daney@cavium.com> Cc: linux-mips@linux-mips.org Cc: devicetree-discuss@lists.ozlabs.org Cc: Grant Likely <grant.likely@secretlab.ca> Cc: Rob Herring <rob.herring@calxeda.com> Cc: linux-kernel@vger.kernel.org Cc: David Daney <david.daney@cavium.com> Patchwork: https://patchwork.linux-mips.org/patch/3935/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/cavium-octeon/octeon-platform.c')
-rw-r--r--arch/mips/cavium-octeon/octeon-platform.c523
1 files changed, 522 insertions, 1 deletions
diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c
index cd61d7281d91..2754bc225903 100644
--- a/arch/mips/cavium-octeon/octeon-platform.c
+++ b/arch/mips/cavium-octeon/octeon-platform.c
@@ -3,7 +3,7 @@
3 * License. See the file "COPYING" in the main directory of this archive 3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details. 4 * for more details.
5 * 5 *
6 * Copyright (C) 2004-2010 Cavium Networks 6 * Copyright (C) 2004-2011 Cavium Networks
7 * Copyright (C) 2008 Wind River Systems 7 * Copyright (C) 2008 Wind River Systems
8 */ 8 */
9 9
@@ -13,10 +13,16 @@
13#include <linux/usb.h> 13#include <linux/usb.h>
14#include <linux/dma-mapping.h> 14#include <linux/dma-mapping.h>
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/slab.h>
16#include <linux/platform_device.h> 17#include <linux/platform_device.h>
18#include <linux/of_platform.h>
19#include <linux/of_fdt.h>
20#include <linux/libfdt.h>
17 21
18#include <asm/octeon/octeon.h> 22#include <asm/octeon/octeon.h>
19#include <asm/octeon/cvmx-rnm-defs.h> 23#include <asm/octeon/cvmx-rnm-defs.h>
24#include <asm/octeon/cvmx-helper.h>
25#include <asm/octeon/cvmx-helper-board.h>
20 26
21static struct octeon_cf_data octeon_cf_data; 27static struct octeon_cf_data octeon_cf_data;
22 28
@@ -440,6 +446,521 @@ device_initcall(octeon_ohci_device_init);
440 446
441#endif /* CONFIG_USB */ 447#endif /* CONFIG_USB */
442 448
449static struct of_device_id __initdata octeon_ids[] = {
450 { .compatible = "simple-bus", },
451 { .compatible = "cavium,octeon-6335-uctl", },
452 { .compatible = "cavium,octeon-3860-bootbus", },
453 { .compatible = "cavium,mdio-mux", },
454 { .compatible = "gpio-leds", },
455 {},
456};
457
458static bool __init octeon_has_88e1145(void)
459{
460 return !OCTEON_IS_MODEL(OCTEON_CN52XX) &&
461 !OCTEON_IS_MODEL(OCTEON_CN6XXX) &&
462 !OCTEON_IS_MODEL(OCTEON_CN56XX);
463}
464
465static void __init octeon_fdt_set_phy(int eth, int phy_addr)
466{
467 const __be32 *phy_handle;
468 const __be32 *alt_phy_handle;
469 const __be32 *reg;
470 u32 phandle;
471 int phy;
472 int alt_phy;
473 const char *p;
474 int current_len;
475 char new_name[20];
476
477 phy_handle = fdt_getprop(initial_boot_params, eth, "phy-handle", NULL);
478 if (!phy_handle)
479 return;
480
481 phandle = be32_to_cpup(phy_handle);
482 phy = fdt_node_offset_by_phandle(initial_boot_params, phandle);
483
484 alt_phy_handle = fdt_getprop(initial_boot_params, eth, "cavium,alt-phy-handle", NULL);
485 if (alt_phy_handle) {
486 u32 alt_phandle = be32_to_cpup(alt_phy_handle);
487 alt_phy = fdt_node_offset_by_phandle(initial_boot_params, alt_phandle);
488 } else {
489 alt_phy = -1;
490 }
491
492 if (phy_addr < 0 || phy < 0) {
493 /* Delete the PHY things */
494 fdt_nop_property(initial_boot_params, eth, "phy-handle");
495 /* This one may fail */
496 fdt_nop_property(initial_boot_params, eth, "cavium,alt-phy-handle");
497 if (phy >= 0)
498 fdt_nop_node(initial_boot_params, phy);
499 if (alt_phy >= 0)
500 fdt_nop_node(initial_boot_params, alt_phy);
501 return;
502 }
503
504 if (phy_addr >= 256 && alt_phy > 0) {
505 const struct fdt_property *phy_prop;
506 struct fdt_property *alt_prop;
507 u32 phy_handle_name;
508
509 /* Use the alt phy node instead.*/
510 phy_prop = fdt_get_property(initial_boot_params, eth, "phy-handle", NULL);
511 phy_handle_name = phy_prop->nameoff;
512 fdt_nop_node(initial_boot_params, phy);
513 fdt_nop_property(initial_boot_params, eth, "phy-handle");
514 alt_prop = fdt_get_property_w(initial_boot_params, eth, "cavium,alt-phy-handle", NULL);
515 alt_prop->nameoff = phy_handle_name;
516 phy = alt_phy;
517 }
518
519 phy_addr &= 0xff;
520
521 if (octeon_has_88e1145()) {
522 fdt_nop_property(initial_boot_params, phy, "marvell,reg-init");
523 memset(new_name, 0, sizeof(new_name));
524 strcpy(new_name, "marvell,88e1145");
525 p = fdt_getprop(initial_boot_params, phy, "compatible",
526 &current_len);
527 if (p && current_len >= strlen(new_name))
528 fdt_setprop_inplace(initial_boot_params, phy,
529 "compatible", new_name, current_len);
530 }
531
532 reg = fdt_getprop(initial_boot_params, phy, "reg", NULL);
533 if (phy_addr == be32_to_cpup(reg))
534 return;
535
536 fdt_setprop_inplace_cell(initial_boot_params, phy, "reg", phy_addr);
537
538 snprintf(new_name, sizeof(new_name), "ethernet-phy@%x", phy_addr);
539
540 p = fdt_get_name(initial_boot_params, phy, &current_len);
541 if (p && current_len == strlen(new_name))
542 fdt_set_name(initial_boot_params, phy, new_name);
543 else
544 pr_err("Error: could not rename ethernet phy: <%s>", p);
545}
546
547static void __init octeon_fdt_set_mac_addr(int n, u64 *pmac)
548{
549 u8 new_mac[6];
550 u64 mac = *pmac;
551 int r;
552
553 new_mac[0] = (mac >> 40) & 0xff;
554 new_mac[1] = (mac >> 32) & 0xff;
555 new_mac[2] = (mac >> 24) & 0xff;
556 new_mac[3] = (mac >> 16) & 0xff;
557 new_mac[4] = (mac >> 8) & 0xff;
558 new_mac[5] = mac & 0xff;
559
560 r = fdt_setprop_inplace(initial_boot_params, n, "local-mac-address",
561 new_mac, sizeof(new_mac));
562
563 if (r) {
564 pr_err("Setting \"local-mac-address\" failed %d", r);
565 return;
566 }
567 *pmac = mac + 1;
568}
569
570static void __init octeon_fdt_rm_ethernet(int node)
571{
572 const __be32 *phy_handle;
573
574 phy_handle = fdt_getprop(initial_boot_params, node, "phy-handle", NULL);
575 if (phy_handle) {
576 u32 ph = be32_to_cpup(phy_handle);
577 int p = fdt_node_offset_by_phandle(initial_boot_params, ph);
578 if (p >= 0)
579 fdt_nop_node(initial_boot_params, p);
580 }
581 fdt_nop_node(initial_boot_params, node);
582}
583
584static void __init octeon_fdt_pip_port(int iface, int i, int p, int max, u64 *pmac)
585{
586 char name_buffer[20];
587 int eth;
588 int phy_addr;
589 int ipd_port;
590
591 snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", p);
592 eth = fdt_subnode_offset(initial_boot_params, iface, name_buffer);
593 if (eth < 0)
594 return;
595 if (p > max) {
596 pr_debug("Deleting port %x:%x\n", i, p);
597 octeon_fdt_rm_ethernet(eth);
598 return;
599 }
600 if (OCTEON_IS_MODEL(OCTEON_CN68XX))
601 ipd_port = (0x100 * i) + (0x10 * p) + 0x800;
602 else
603 ipd_port = 16 * i + p;
604
605 phy_addr = cvmx_helper_board_get_mii_address(ipd_port);
606 octeon_fdt_set_phy(eth, phy_addr);
607 octeon_fdt_set_mac_addr(eth, pmac);
608}
609
610static void __init octeon_fdt_pip_iface(int pip, int idx, u64 *pmac)
611{
612 char name_buffer[20];
613 int iface;
614 int p;
615 int count;
616
617 count = cvmx_helper_interface_enumerate(idx);
618
619 snprintf(name_buffer, sizeof(name_buffer), "interface@%d", idx);
620 iface = fdt_subnode_offset(initial_boot_params, pip, name_buffer);
621 if (iface < 0)
622 return;
623
624 for (p = 0; p < 16; p++)
625 octeon_fdt_pip_port(iface, idx, p, count - 1, pmac);
626}
627
628int __init octeon_prune_device_tree(void)
629{
630 int i, max_port, uart_mask;
631 const char *pip_path;
632 const char *alias_prop;
633 char name_buffer[20];
634 int aliases;
635 u64 mac_addr_base;
636
637 if (fdt_check_header(initial_boot_params))
638 panic("Corrupt Device Tree.");
639
640 aliases = fdt_path_offset(initial_boot_params, "/aliases");
641 if (aliases < 0) {
642 pr_err("Error: No /aliases node in device tree.");
643 return -EINVAL;
644 }
645
646
647 mac_addr_base =
648 ((octeon_bootinfo->mac_addr_base[0] & 0xffull)) << 40 |
649 ((octeon_bootinfo->mac_addr_base[1] & 0xffull)) << 32 |
650 ((octeon_bootinfo->mac_addr_base[2] & 0xffull)) << 24 |
651 ((octeon_bootinfo->mac_addr_base[3] & 0xffull)) << 16 |
652 ((octeon_bootinfo->mac_addr_base[4] & 0xffull)) << 8 |
653 (octeon_bootinfo->mac_addr_base[5] & 0xffull);
654
655 if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
656 max_port = 2;
657 else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN68XX))
658 max_port = 1;
659 else
660 max_port = 0;
661
662 if (octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC10E)
663 max_port = 0;
664
665 for (i = 0; i < 2; i++) {
666 int mgmt;
667 snprintf(name_buffer, sizeof(name_buffer),
668 "mix%d", i);
669 alias_prop = fdt_getprop(initial_boot_params, aliases,
670 name_buffer, NULL);
671 if (alias_prop) {
672 mgmt = fdt_path_offset(initial_boot_params, alias_prop);
673 if (mgmt < 0)
674 continue;
675 if (i >= max_port) {
676 pr_debug("Deleting mix%d\n", i);
677 octeon_fdt_rm_ethernet(mgmt);
678 fdt_nop_property(initial_boot_params, aliases,
679 name_buffer);
680 } else {
681 int phy_addr = cvmx_helper_board_get_mii_address(CVMX_HELPER_BOARD_MGMT_IPD_PORT + i);
682 octeon_fdt_set_phy(mgmt, phy_addr);
683 octeon_fdt_set_mac_addr(mgmt, &mac_addr_base);
684 }
685 }
686 }
687
688 pip_path = fdt_getprop(initial_boot_params, aliases, "pip", NULL);
689 if (pip_path) {
690 int pip = fdt_path_offset(initial_boot_params, pip_path);
691 if (pip >= 0)
692 for (i = 0; i <= 4; i++)
693 octeon_fdt_pip_iface(pip, i, &mac_addr_base);
694 }
695
696 /* I2C */
697 if (OCTEON_IS_MODEL(OCTEON_CN52XX) ||
698 OCTEON_IS_MODEL(OCTEON_CN63XX) ||
699 OCTEON_IS_MODEL(OCTEON_CN68XX) ||
700 OCTEON_IS_MODEL(OCTEON_CN56XX))
701 max_port = 2;
702 else
703 max_port = 1;
704
705 for (i = 0; i < 2; i++) {
706 int i2c;
707 snprintf(name_buffer, sizeof(name_buffer),
708 "twsi%d", i);
709 alias_prop = fdt_getprop(initial_boot_params, aliases,
710 name_buffer, NULL);
711
712 if (alias_prop) {
713 i2c = fdt_path_offset(initial_boot_params, alias_prop);
714 if (i2c < 0)
715 continue;
716 if (i >= max_port) {
717 pr_debug("Deleting twsi%d\n", i);
718 fdt_nop_node(initial_boot_params, i2c);
719 fdt_nop_property(initial_boot_params, aliases,
720 name_buffer);
721 }
722 }
723 }
724
725 /* SMI/MDIO */
726 if (OCTEON_IS_MODEL(OCTEON_CN68XX))
727 max_port = 4;
728 else if (OCTEON_IS_MODEL(OCTEON_CN52XX) ||
729 OCTEON_IS_MODEL(OCTEON_CN63XX) ||
730 OCTEON_IS_MODEL(OCTEON_CN56XX))
731 max_port = 2;
732 else
733 max_port = 1;
734
735 for (i = 0; i < 2; i++) {
736 int i2c;
737 snprintf(name_buffer, sizeof(name_buffer),
738 "smi%d", i);
739 alias_prop = fdt_getprop(initial_boot_params, aliases,
740 name_buffer, NULL);
741
742 if (alias_prop) {
743 i2c = fdt_path_offset(initial_boot_params, alias_prop);
744 if (i2c < 0)
745 continue;
746 if (i >= max_port) {
747 pr_debug("Deleting smi%d\n", i);
748 fdt_nop_node(initial_boot_params, i2c);
749 fdt_nop_property(initial_boot_params, aliases,
750 name_buffer);
751 }
752 }
753 }
754
755 /* Serial */
756 uart_mask = 3;
757
758 /* Right now CN52XX is the only chip with a third uart */
759 if (OCTEON_IS_MODEL(OCTEON_CN52XX))
760 uart_mask |= 4; /* uart2 */
761
762 for (i = 0; i < 3; i++) {
763 int uart;
764 snprintf(name_buffer, sizeof(name_buffer),
765 "uart%d", i);
766 alias_prop = fdt_getprop(initial_boot_params, aliases,
767 name_buffer, NULL);
768
769 if (alias_prop) {
770 uart = fdt_path_offset(initial_boot_params, alias_prop);
771 if (uart_mask & (1 << i))
772 continue;
773 pr_debug("Deleting uart%d\n", i);
774 fdt_nop_node(initial_boot_params, uart);
775 fdt_nop_property(initial_boot_params, aliases,
776 name_buffer);
777 }
778 }
779
780 /* Compact Flash */
781 alias_prop = fdt_getprop(initial_boot_params, aliases,
782 "cf0", NULL);
783 if (alias_prop) {
784 union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg;
785 unsigned long base_ptr, region_base, region_size;
786 unsigned long region1_base = 0;
787 unsigned long region1_size = 0;
788 int cs, bootbus;
789 bool is_16bit = false;
790 bool is_true_ide = false;
791 __be32 new_reg[6];
792 __be32 *ranges;
793 int len;
794
795 int cf = fdt_path_offset(initial_boot_params, alias_prop);
796 base_ptr = 0;
797 if (octeon_bootinfo->major_version == 1
798 && octeon_bootinfo->minor_version >= 1) {
799 if (octeon_bootinfo->compact_flash_common_base_addr)
800 base_ptr = octeon_bootinfo->compact_flash_common_base_addr;
801 } else {
802 base_ptr = 0x1d000800;
803 }
804
805 if (!base_ptr)
806 goto no_cf;
807
808 /* Find CS0 region. */
809 for (cs = 0; cs < 8; cs++) {
810 mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs));
811 region_base = mio_boot_reg_cfg.s.base << 16;
812 region_size = (mio_boot_reg_cfg.s.size + 1) << 16;
813 if (mio_boot_reg_cfg.s.en && base_ptr >= region_base
814 && base_ptr < region_base + region_size) {
815 is_16bit = mio_boot_reg_cfg.s.width;
816 break;
817 }
818 }
819 if (cs >= 7) {
820 /* cs and cs + 1 are CS0 and CS1, both must be less than 8. */
821 goto no_cf;
822 }
823
824 if (!(base_ptr & 0xfffful)) {
825 /*
826 * Boot loader signals availability of DMA (true_ide
827 * mode) by setting low order bits of base_ptr to
828 * zero.
829 */
830
831 /* Asume that CS1 immediately follows. */
832 mio_boot_reg_cfg.u64 =
833 cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs + 1));
834 region1_base = mio_boot_reg_cfg.s.base << 16;
835 region1_size = (mio_boot_reg_cfg.s.size + 1) << 16;
836 if (!mio_boot_reg_cfg.s.en)
837 goto no_cf;
838 is_true_ide = true;
839
840 } else {
841 fdt_nop_property(initial_boot_params, cf, "cavium,true-ide");
842 fdt_nop_property(initial_boot_params, cf, "cavium,dma-engine-handle");
843 if (!is_16bit) {
844 __be32 width = cpu_to_be32(8);
845 fdt_setprop_inplace(initial_boot_params, cf,
846 "cavium,bus-width", &width, sizeof(width));
847 }
848 }
849 new_reg[0] = cpu_to_be32(cs);
850 new_reg[1] = cpu_to_be32(0);
851 new_reg[2] = cpu_to_be32(0x10000);
852 new_reg[3] = cpu_to_be32(cs + 1);
853 new_reg[4] = cpu_to_be32(0);
854 new_reg[5] = cpu_to_be32(0x10000);
855 fdt_setprop_inplace(initial_boot_params, cf,
856 "reg", new_reg, sizeof(new_reg));
857
858 bootbus = fdt_parent_offset(initial_boot_params, cf);
859 if (bootbus < 0)
860 goto no_cf;
861 ranges = fdt_getprop_w(initial_boot_params, bootbus, "ranges", &len);
862 if (!ranges || len < (5 * 8 * sizeof(__be32)))
863 goto no_cf;
864
865 ranges[(cs * 5) + 2] = cpu_to_be32(region_base >> 32);
866 ranges[(cs * 5) + 3] = cpu_to_be32(region_base & 0xffffffff);
867 ranges[(cs * 5) + 4] = cpu_to_be32(region_size);
868 if (is_true_ide) {
869 cs++;
870 ranges[(cs * 5) + 2] = cpu_to_be32(region1_base >> 32);
871 ranges[(cs * 5) + 3] = cpu_to_be32(region1_base & 0xffffffff);
872 ranges[(cs * 5) + 4] = cpu_to_be32(region1_size);
873 }
874 goto end_cf;
875no_cf:
876 fdt_nop_node(initial_boot_params, cf);
877
878end_cf:
879 ;
880 }
881
882 /* 8 char LED */
883 alias_prop = fdt_getprop(initial_boot_params, aliases,
884 "led0", NULL);
885 if (alias_prop) {
886 union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg;
887 unsigned long base_ptr, region_base, region_size;
888 int cs, bootbus;
889 __be32 new_reg[6];
890 __be32 *ranges;
891 int len;
892 int led = fdt_path_offset(initial_boot_params, alias_prop);
893
894 base_ptr = octeon_bootinfo->led_display_base_addr;
895 if (base_ptr == 0)
896 goto no_led;
897 /* Find CS0 region. */
898 for (cs = 0; cs < 8; cs++) {
899 mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs));
900 region_base = mio_boot_reg_cfg.s.base << 16;
901 region_size = (mio_boot_reg_cfg.s.size + 1) << 16;
902 if (mio_boot_reg_cfg.s.en && base_ptr >= region_base
903 && base_ptr < region_base + region_size)
904 break;
905 }
906
907 if (cs > 7)
908 goto no_led;
909
910 new_reg[0] = cpu_to_be32(cs);
911 new_reg[1] = cpu_to_be32(0x20);
912 new_reg[2] = cpu_to_be32(0x20);
913 new_reg[3] = cpu_to_be32(cs);
914 new_reg[4] = cpu_to_be32(0);
915 new_reg[5] = cpu_to_be32(0x20);
916 fdt_setprop_inplace(initial_boot_params, led,
917 "reg", new_reg, sizeof(new_reg));
918
919 bootbus = fdt_parent_offset(initial_boot_params, led);
920 if (bootbus < 0)
921 goto no_led;
922 ranges = fdt_getprop_w(initial_boot_params, bootbus, "ranges", &len);
923 if (!ranges || len < (5 * 8 * sizeof(__be32)))
924 goto no_led;
925
926 ranges[(cs * 5) + 2] = cpu_to_be32(region_base >> 32);
927 ranges[(cs * 5) + 3] = cpu_to_be32(region_base & 0xffffffff);
928 ranges[(cs * 5) + 4] = cpu_to_be32(region_size);
929 goto end_led;
930
931no_led:
932 fdt_nop_node(initial_boot_params, led);
933end_led:
934 ;
935 }
936
937 /* OHCI/UHCI USB */
938 alias_prop = fdt_getprop(initial_boot_params, aliases,
939 "uctl", NULL);
940 if (alias_prop) {
941 int uctl = fdt_path_offset(initial_boot_params, alias_prop);
942
943 if (uctl >= 0 && (!OCTEON_IS_MODEL(OCTEON_CN6XXX) ||
944 octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC2E)) {
945 pr_debug("Deleting uctl\n");
946 fdt_nop_node(initial_boot_params, uctl);
947 fdt_nop_property(initial_boot_params, aliases, "uctl");
948 } else if (octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC10E ||
949 octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC4E) {
950 /* Missing "refclk-type" defaults to crystal. */
951 fdt_nop_property(initial_boot_params, uctl, "refclk-type");
952 }
953 }
954
955 return 0;
956}
957
958static int __init octeon_publish_devices(void)
959{
960 return of_platform_bus_probe(NULL, octeon_ids, NULL);
961}
962device_initcall(octeon_publish_devices);
963
443MODULE_AUTHOR("David Daney <ddaney@caviumnetworks.com>"); 964MODULE_AUTHOR("David Daney <ddaney@caviumnetworks.com>");
444MODULE_LICENSE("GPL"); 965MODULE_LICENSE("GPL");
445MODULE_DESCRIPTION("Platform driver for Octeon SOC"); 966MODULE_DESCRIPTION("Platform driver for Octeon SOC");