diff options
Diffstat (limited to 'drivers/bus/mvebu-mbus.c')
-rw-r--r-- | drivers/bus/mvebu-mbus.c | 180 |
1 files changed, 176 insertions, 4 deletions
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 26c3779d871d..eb7682dc123b 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c | |||
@@ -57,6 +57,7 @@ | |||
57 | #include <linux/of_address.h> | 57 | #include <linux/of_address.h> |
58 | #include <linux/debugfs.h> | 58 | #include <linux/debugfs.h> |
59 | #include <linux/log2.h> | 59 | #include <linux/log2.h> |
60 | #include <linux/syscore_ops.h> | ||
60 | 61 | ||
61 | /* | 62 | /* |
62 | * DDR target is the same on all platforms. | 63 | * DDR target is the same on all platforms. |
@@ -94,20 +95,42 @@ | |||
94 | 95 | ||
95 | #define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) | 96 | #define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) |
96 | 97 | ||
98 | /* Relative to mbusbridge_base */ | ||
99 | #define MBUS_BRIDGE_CTRL_OFF 0x0 | ||
100 | #define MBUS_BRIDGE_BASE_OFF 0x4 | ||
101 | |||
102 | /* Maximum number of windows, for all known platforms */ | ||
103 | #define MBUS_WINS_MAX 20 | ||
104 | |||
97 | struct mvebu_mbus_state; | 105 | struct mvebu_mbus_state; |
98 | 106 | ||
99 | struct mvebu_mbus_soc_data { | 107 | struct mvebu_mbus_soc_data { |
100 | unsigned int num_wins; | 108 | unsigned int num_wins; |
101 | unsigned int num_remappable_wins; | 109 | unsigned int num_remappable_wins; |
110 | bool has_mbus_bridge; | ||
102 | unsigned int (*win_cfg_offset)(const int win); | 111 | unsigned int (*win_cfg_offset)(const int win); |
103 | void (*setup_cpu_target)(struct mvebu_mbus_state *s); | 112 | void (*setup_cpu_target)(struct mvebu_mbus_state *s); |
113 | int (*save_cpu_target)(struct mvebu_mbus_state *s, | ||
114 | u32 *store_addr); | ||
104 | int (*show_cpu_target)(struct mvebu_mbus_state *s, | 115 | int (*show_cpu_target)(struct mvebu_mbus_state *s, |
105 | struct seq_file *seq, void *v); | 116 | struct seq_file *seq, void *v); |
106 | }; | 117 | }; |
107 | 118 | ||
119 | /* | ||
120 | * Used to store the state of one MBus window accross suspend/resume. | ||
121 | */ | ||
122 | struct mvebu_mbus_win_data { | ||
123 | u32 ctrl; | ||
124 | u32 base; | ||
125 | u32 remap_lo; | ||
126 | u32 remap_hi; | ||
127 | }; | ||
128 | |||
108 | struct mvebu_mbus_state { | 129 | struct mvebu_mbus_state { |
109 | void __iomem *mbuswins_base; | 130 | void __iomem *mbuswins_base; |
110 | void __iomem *sdramwins_base; | 131 | void __iomem *sdramwins_base; |
132 | void __iomem *mbusbridge_base; | ||
133 | phys_addr_t sdramwins_phys_base; | ||
111 | struct dentry *debugfs_root; | 134 | struct dentry *debugfs_root; |
112 | struct dentry *debugfs_sdram; | 135 | struct dentry *debugfs_sdram; |
113 | struct dentry *debugfs_devs; | 136 | struct dentry *debugfs_devs; |
@@ -115,6 +138,11 @@ struct mvebu_mbus_state { | |||
115 | struct resource pcie_io_aperture; | 138 | struct resource pcie_io_aperture; |
116 | const struct mvebu_mbus_soc_data *soc; | 139 | const struct mvebu_mbus_soc_data *soc; |
117 | int hw_io_coherency; | 140 | int hw_io_coherency; |
141 | |||
142 | /* Used during suspend/resume */ | ||
143 | u32 mbus_bridge_ctrl; | ||
144 | u32 mbus_bridge_base; | ||
145 | struct mvebu_mbus_win_data wins[MBUS_WINS_MAX]; | ||
118 | }; | 146 | }; |
119 | 147 | ||
120 | static struct mvebu_mbus_state mbus_state; | 148 | static struct mvebu_mbus_state mbus_state; |
@@ -516,6 +544,28 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) | |||
516 | mvebu_mbus_dram_info.num_cs = cs; | 544 | mvebu_mbus_dram_info.num_cs = cs; |
517 | } | 545 | } |
518 | 546 | ||
547 | static int | ||
548 | mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus, | ||
549 | u32 *store_addr) | ||
550 | { | ||
551 | int i; | ||
552 | |||
553 | for (i = 0; i < 4; i++) { | ||
554 | u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); | ||
555 | u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); | ||
556 | |||
557 | writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i), | ||
558 | store_addr++); | ||
559 | writel(base, store_addr++); | ||
560 | writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i), | ||
561 | store_addr++); | ||
562 | writel(size, store_addr++); | ||
563 | } | ||
564 | |||
565 | /* We've written 16 words to the store address */ | ||
566 | return 16; | ||
567 | } | ||
568 | |||
519 | static void __init | 569 | static void __init |
520 | mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) | 570 | mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) |
521 | { | 571 | { |
@@ -546,10 +596,35 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) | |||
546 | mvebu_mbus_dram_info.num_cs = cs; | 596 | mvebu_mbus_dram_info.num_cs = cs; |
547 | } | 597 | } |
548 | 598 | ||
599 | static int | ||
600 | mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus, | ||
601 | u32 *store_addr) | ||
602 | { | ||
603 | int i; | ||
604 | |||
605 | for (i = 0; i < 2; i++) { | ||
606 | u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i)); | ||
607 | |||
608 | writel(mbus->sdramwins_phys_base + DOVE_DDR_BASE_CS_OFF(i), | ||
609 | store_addr++); | ||
610 | writel(map, store_addr++); | ||
611 | } | ||
612 | |||
613 | /* We've written 4 words to the store address */ | ||
614 | return 4; | ||
615 | } | ||
616 | |||
617 | int mvebu_mbus_save_cpu_target(u32 *store_addr) | ||
618 | { | ||
619 | return mbus_state.soc->save_cpu_target(&mbus_state, store_addr); | ||
620 | } | ||
621 | |||
549 | static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = { | 622 | static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = { |
550 | .num_wins = 20, | 623 | .num_wins = 20, |
551 | .num_remappable_wins = 8, | 624 | .num_remappable_wins = 8, |
625 | .has_mbus_bridge = true, | ||
552 | .win_cfg_offset = armada_370_xp_mbus_win_offset, | 626 | .win_cfg_offset = armada_370_xp_mbus_win_offset, |
627 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
553 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 628 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
554 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 629 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
555 | }; | 630 | }; |
@@ -558,6 +633,7 @@ static const struct mvebu_mbus_soc_data kirkwood_mbus_data = { | |||
558 | .num_wins = 8, | 633 | .num_wins = 8, |
559 | .num_remappable_wins = 4, | 634 | .num_remappable_wins = 4, |
560 | .win_cfg_offset = orion_mbus_win_offset, | 635 | .win_cfg_offset = orion_mbus_win_offset, |
636 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
561 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 637 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
562 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 638 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
563 | }; | 639 | }; |
@@ -566,6 +642,7 @@ static const struct mvebu_mbus_soc_data dove_mbus_data = { | |||
566 | .num_wins = 8, | 642 | .num_wins = 8, |
567 | .num_remappable_wins = 4, | 643 | .num_remappable_wins = 4, |
568 | .win_cfg_offset = orion_mbus_win_offset, | 644 | .win_cfg_offset = orion_mbus_win_offset, |
645 | .save_cpu_target = mvebu_mbus_dove_save_cpu_target, | ||
569 | .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target, | 646 | .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target, |
570 | .show_cpu_target = mvebu_sdram_debug_show_dove, | 647 | .show_cpu_target = mvebu_sdram_debug_show_dove, |
571 | }; | 648 | }; |
@@ -578,6 +655,7 @@ static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = { | |||
578 | .num_wins = 8, | 655 | .num_wins = 8, |
579 | .num_remappable_wins = 4, | 656 | .num_remappable_wins = 4, |
580 | .win_cfg_offset = orion_mbus_win_offset, | 657 | .win_cfg_offset = orion_mbus_win_offset, |
658 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
581 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 659 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
582 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 660 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
583 | }; | 661 | }; |
@@ -586,6 +664,7 @@ static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = { | |||
586 | .num_wins = 8, | 664 | .num_wins = 8, |
587 | .num_remappable_wins = 2, | 665 | .num_remappable_wins = 2, |
588 | .win_cfg_offset = orion_mbus_win_offset, | 666 | .win_cfg_offset = orion_mbus_win_offset, |
667 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
589 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 668 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
590 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 669 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
591 | }; | 670 | }; |
@@ -594,6 +673,7 @@ static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = { | |||
594 | .num_wins = 14, | 673 | .num_wins = 14, |
595 | .num_remappable_wins = 8, | 674 | .num_remappable_wins = 8, |
596 | .win_cfg_offset = mv78xx0_mbus_win_offset, | 675 | .win_cfg_offset = mv78xx0_mbus_win_offset, |
676 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
597 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 677 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
598 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 678 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
599 | }; | 679 | }; |
@@ -698,11 +778,73 @@ static __init int mvebu_mbus_debugfs_init(void) | |||
698 | } | 778 | } |
699 | fs_initcall(mvebu_mbus_debugfs_init); | 779 | fs_initcall(mvebu_mbus_debugfs_init); |
700 | 780 | ||
781 | static int mvebu_mbus_suspend(void) | ||
782 | { | ||
783 | struct mvebu_mbus_state *s = &mbus_state; | ||
784 | int win; | ||
785 | |||
786 | if (!s->mbusbridge_base) | ||
787 | return -ENODEV; | ||
788 | |||
789 | for (win = 0; win < s->soc->num_wins; win++) { | ||
790 | void __iomem *addr = s->mbuswins_base + | ||
791 | s->soc->win_cfg_offset(win); | ||
792 | |||
793 | s->wins[win].base = readl(addr + WIN_BASE_OFF); | ||
794 | s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF); | ||
795 | |||
796 | if (win >= s->soc->num_remappable_wins) | ||
797 | continue; | ||
798 | |||
799 | s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF); | ||
800 | s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF); | ||
801 | } | ||
802 | |||
803 | s->mbus_bridge_ctrl = readl(s->mbusbridge_base + | ||
804 | MBUS_BRIDGE_CTRL_OFF); | ||
805 | s->mbus_bridge_base = readl(s->mbusbridge_base + | ||
806 | MBUS_BRIDGE_BASE_OFF); | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | static void mvebu_mbus_resume(void) | ||
812 | { | ||
813 | struct mvebu_mbus_state *s = &mbus_state; | ||
814 | int win; | ||
815 | |||
816 | writel(s->mbus_bridge_ctrl, | ||
817 | s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF); | ||
818 | writel(s->mbus_bridge_base, | ||
819 | s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF); | ||
820 | |||
821 | for (win = 0; win < s->soc->num_wins; win++) { | ||
822 | void __iomem *addr = s->mbuswins_base + | ||
823 | s->soc->win_cfg_offset(win); | ||
824 | |||
825 | writel(s->wins[win].base, addr + WIN_BASE_OFF); | ||
826 | writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF); | ||
827 | |||
828 | if (win >= s->soc->num_remappable_wins) | ||
829 | continue; | ||
830 | |||
831 | writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF); | ||
832 | writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF); | ||
833 | } | ||
834 | } | ||
835 | |||
836 | struct syscore_ops mvebu_mbus_syscore_ops = { | ||
837 | .suspend = mvebu_mbus_suspend, | ||
838 | .resume = mvebu_mbus_resume, | ||
839 | }; | ||
840 | |||
701 | static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, | 841 | static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, |
702 | phys_addr_t mbuswins_phys_base, | 842 | phys_addr_t mbuswins_phys_base, |
703 | size_t mbuswins_size, | 843 | size_t mbuswins_size, |
704 | phys_addr_t sdramwins_phys_base, | 844 | phys_addr_t sdramwins_phys_base, |
705 | size_t sdramwins_size) | 845 | size_t sdramwins_size, |
846 | phys_addr_t mbusbridge_phys_base, | ||
847 | size_t mbusbridge_size) | ||
706 | { | 848 | { |
707 | int win; | 849 | int win; |
708 | 850 | ||
@@ -716,11 +858,26 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, | |||
716 | return -ENOMEM; | 858 | return -ENOMEM; |
717 | } | 859 | } |
718 | 860 | ||
861 | mbus->sdramwins_phys_base = sdramwins_phys_base; | ||
862 | |||
863 | if (mbusbridge_phys_base) { | ||
864 | mbus->mbusbridge_base = ioremap(mbusbridge_phys_base, | ||
865 | mbusbridge_size); | ||
866 | if (!mbus->mbusbridge_base) { | ||
867 | iounmap(mbus->sdramwins_base); | ||
868 | iounmap(mbus->mbuswins_base); | ||
869 | return -ENOMEM; | ||
870 | } | ||
871 | } else | ||
872 | mbus->mbusbridge_base = NULL; | ||
873 | |||
719 | for (win = 0; win < mbus->soc->num_wins; win++) | 874 | for (win = 0; win < mbus->soc->num_wins; win++) |
720 | mvebu_mbus_disable_window(mbus, win); | 875 | mvebu_mbus_disable_window(mbus, win); |
721 | 876 | ||
722 | mbus->soc->setup_cpu_target(mbus); | 877 | mbus->soc->setup_cpu_target(mbus); |
723 | 878 | ||
879 | register_syscore_ops(&mvebu_mbus_syscore_ops); | ||
880 | |||
724 | return 0; | 881 | return 0; |
725 | } | 882 | } |
726 | 883 | ||
@@ -746,7 +903,7 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base, | |||
746 | mbuswins_phys_base, | 903 | mbuswins_phys_base, |
747 | mbuswins_size, | 904 | mbuswins_size, |
748 | sdramwins_phys_base, | 905 | sdramwins_phys_base, |
749 | sdramwins_size); | 906 | sdramwins_size, 0, 0); |
750 | } | 907 | } |
751 | 908 | ||
752 | #ifdef CONFIG_OF | 909 | #ifdef CONFIG_OF |
@@ -887,7 +1044,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, | |||
887 | 1044 | ||
888 | int __init mvebu_mbus_dt_init(bool is_coherent) | 1045 | int __init mvebu_mbus_dt_init(bool is_coherent) |
889 | { | 1046 | { |
890 | struct resource mbuswins_res, sdramwins_res; | 1047 | struct resource mbuswins_res, sdramwins_res, mbusbridge_res; |
891 | struct device_node *np, *controller; | 1048 | struct device_node *np, *controller; |
892 | const struct of_device_id *of_id; | 1049 | const struct of_device_id *of_id; |
893 | const __be32 *prop; | 1050 | const __be32 *prop; |
@@ -923,6 +1080,19 @@ int __init mvebu_mbus_dt_init(bool is_coherent) | |||
923 | return -EINVAL; | 1080 | return -EINVAL; |
924 | } | 1081 | } |
925 | 1082 | ||
1083 | /* | ||
1084 | * Set the resource to 0 so that it can be left unmapped by | ||
1085 | * mvebu_mbus_common_init() if the DT doesn't carry the | ||
1086 | * necessary information. This is needed to preserve backward | ||
1087 | * compatibility. | ||
1088 | */ | ||
1089 | memset(&mbusbridge_res, 0, sizeof(mbusbridge_res)); | ||
1090 | |||
1091 | if (mbus_state.soc->has_mbus_bridge) { | ||
1092 | if (of_address_to_resource(controller, 2, &mbusbridge_res)) | ||
1093 | pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n"); | ||
1094 | } | ||
1095 | |||
926 | mbus_state.hw_io_coherency = is_coherent; | 1096 | mbus_state.hw_io_coherency = is_coherent; |
927 | 1097 | ||
928 | /* Get optional pcie-{mem,io}-aperture properties */ | 1098 | /* Get optional pcie-{mem,io}-aperture properties */ |
@@ -933,7 +1103,9 @@ int __init mvebu_mbus_dt_init(bool is_coherent) | |||
933 | mbuswins_res.start, | 1103 | mbuswins_res.start, |
934 | resource_size(&mbuswins_res), | 1104 | resource_size(&mbuswins_res), |
935 | sdramwins_res.start, | 1105 | sdramwins_res.start, |
936 | resource_size(&sdramwins_res)); | 1106 | resource_size(&sdramwins_res), |
1107 | mbusbridge_res.start, | ||
1108 | resource_size(&mbusbridge_res)); | ||
937 | if (ret) | 1109 | if (ret) |
938 | return ret; | 1110 | return ret; |
939 | 1111 | ||