aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Petazzoni <thomas.petazzoni@free-electrons.com>2014-11-21 11:00:03 -0500
committerJason Cooper <jason@lakedaemon.net>2014-11-30 11:40:10 -0500
commita0e89c02da974838053a3604025e43600dc6ac45 (patch)
tree082f2ce8417738b0241d14c873ee2395b07e2084
parentf9a49ab53a269fda39ae57063bd336b4bd62fa76 (diff)
bus: mvebu-mbus: suspend/resume support
This commit extends the mvebu-mbus driver to provide suspend/resume support. Since mvebu-mbus is not a platform_driver, the syscore_ops mechanism is used to get ->suspend() and ->resume() hooks called into the driver. In those hooks, we save and restore the MBus windows state, to make sure after resume all Mbus windows are properly restored. Note that while the state of some windows could be gathered by looking again at the Device Tree (for statically described windows), it is not the case of dynamically described windows such as the PCIe memory and I/O windows. Therefore, we take the simple approach of saving and restoring the registers for all MBus windows. In addition, the commit extends the Device Tree binding of the MBus controller, to control the MBus bridge registers (which define which parts of the physical address space is routed to MBus windows vs. normal RAM memory). Those registers must be saved and restored during suspend/resume. The Device Tree binding extension is made is a backward compatible fashion, but of course, suspend/resume will not work without the Device Tree update. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Link: https://lkml.kernel.org/r/1416585613-2113-7-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
-rw-r--r--Documentation/devicetree/bindings/bus/mvebu-mbus.txt17
-rw-r--r--drivers/bus/mvebu-mbus.c124
2 files changed, 130 insertions, 11 deletions
diff --git a/Documentation/devicetree/bindings/bus/mvebu-mbus.txt b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
index 5fa44f52a0b8..5e16c3ccb061 100644
--- a/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
+++ b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
@@ -48,9 +48,12 @@ Required properties:
48- compatible: Should be set to "marvell,mbus-controller". 48- compatible: Should be set to "marvell,mbus-controller".
49 49
50- reg: Device's register space. 50- reg: Device's register space.
51 Two entries are expected (see the examples below): 51 Two or three entries are expected (see the examples below):
52 the first one controls the devices decoding window and 52 the first one controls the devices decoding window,
53 the second one controls the SDRAM decoding window. 53 the second one controls the SDRAM decoding window and
54 the third controls the MBus bridge (only with the
55 marvell,armada370-mbus and marvell,armadaxp-mbus
56 compatible strings)
54 57
55Example: 58Example:
56 59
@@ -67,7 +70,7 @@ Example:
67 70
68 mbusc: mbus-controller@20000 { 71 mbusc: mbus-controller@20000 {
69 compatible = "marvell,mbus-controller"; 72 compatible = "marvell,mbus-controller";
70 reg = <0x20000 0x100>, <0x20180 0x20>; 73 reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
71 }; 74 };
72 75
73 /* more children ...*/ 76 /* more children ...*/
@@ -126,7 +129,7 @@ are skipped.
126 129
127 mbusc: mbus-controller@20000 { 130 mbusc: mbus-controller@20000 {
128 compatible = "marvell,mbus-controller"; 131 compatible = "marvell,mbus-controller";
129 reg = <0x20000 0x100>, <0x20180 0x20>; 132 reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
130 }; 133 };
131 134
132 /* more children ...*/ 135 /* more children ...*/
@@ -170,7 +173,7 @@ Using this macro, the above example would be:
170 173
171 mbusc: mbus-controller@20000 { 174 mbusc: mbus-controller@20000 {
172 compatible = "marvell,mbus-controller"; 175 compatible = "marvell,mbus-controller";
173 reg = <0x20000 0x100>, <0x20180 0x20>; 176 reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
174 }; 177 };
175 178
176 /* other children */ 179 /* other children */
@@ -266,7 +269,7 @@ See the example below, where a more complete device tree is shown:
266 ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>; 269 ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>;
267 270
268 mbusc: mbus-controller@20000 { 271 mbusc: mbus-controller@20000 {
269 reg = <0x20000 0x100>, <0x20180 0x20>; 272 reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
270 }; 273 };
271 274
272 interrupt-controller@20000 { 275 interrupt-controller@20000 {
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 26c3779d871d..e8c159399c82 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,39 @@
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
97struct mvebu_mbus_state; 105struct mvebu_mbus_state;
98 106
99struct mvebu_mbus_soc_data { 107struct 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);
104 int (*show_cpu_target)(struct mvebu_mbus_state *s, 113 int (*show_cpu_target)(struct mvebu_mbus_state *s,
105 struct seq_file *seq, void *v); 114 struct seq_file *seq, void *v);
106}; 115};
107 116
117/*
118 * Used to store the state of one MBus window accross suspend/resume.
119 */
120struct mvebu_mbus_win_data {
121 u32 ctrl;
122 u32 base;
123 u32 remap_lo;
124 u32 remap_hi;
125};
126
108struct mvebu_mbus_state { 127struct mvebu_mbus_state {
109 void __iomem *mbuswins_base; 128 void __iomem *mbuswins_base;
110 void __iomem *sdramwins_base; 129 void __iomem *sdramwins_base;
130 void __iomem *mbusbridge_base;
111 struct dentry *debugfs_root; 131 struct dentry *debugfs_root;
112 struct dentry *debugfs_sdram; 132 struct dentry *debugfs_sdram;
113 struct dentry *debugfs_devs; 133 struct dentry *debugfs_devs;
@@ -115,6 +135,11 @@ struct mvebu_mbus_state {
115 struct resource pcie_io_aperture; 135 struct resource pcie_io_aperture;
116 const struct mvebu_mbus_soc_data *soc; 136 const struct mvebu_mbus_soc_data *soc;
117 int hw_io_coherency; 137 int hw_io_coherency;
138
139 /* Used during suspend/resume */
140 u32 mbus_bridge_ctrl;
141 u32 mbus_bridge_base;
142 struct mvebu_mbus_win_data wins[MBUS_WINS_MAX];
118}; 143};
119 144
120static struct mvebu_mbus_state mbus_state; 145static struct mvebu_mbus_state mbus_state;
@@ -549,6 +574,7 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
549static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = { 574static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = {
550 .num_wins = 20, 575 .num_wins = 20,
551 .num_remappable_wins = 8, 576 .num_remappable_wins = 8,
577 .has_mbus_bridge = true,
552 .win_cfg_offset = armada_370_xp_mbus_win_offset, 578 .win_cfg_offset = armada_370_xp_mbus_win_offset,
553 .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, 579 .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
554 .show_cpu_target = mvebu_sdram_debug_show_orion, 580 .show_cpu_target = mvebu_sdram_debug_show_orion,
@@ -698,11 +724,73 @@ static __init int mvebu_mbus_debugfs_init(void)
698} 724}
699fs_initcall(mvebu_mbus_debugfs_init); 725fs_initcall(mvebu_mbus_debugfs_init);
700 726
727static int mvebu_mbus_suspend(void)
728{
729 struct mvebu_mbus_state *s = &mbus_state;
730 int win;
731
732 if (!s->mbusbridge_base)
733 return -ENODEV;
734
735 for (win = 0; win < s->soc->num_wins; win++) {
736 void __iomem *addr = s->mbuswins_base +
737 s->soc->win_cfg_offset(win);
738
739 s->wins[win].base = readl(addr + WIN_BASE_OFF);
740 s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF);
741
742 if (win >= s->soc->num_remappable_wins)
743 continue;
744
745 s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF);
746 s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF);
747 }
748
749 s->mbus_bridge_ctrl = readl(s->mbusbridge_base +
750 MBUS_BRIDGE_CTRL_OFF);
751 s->mbus_bridge_base = readl(s->mbusbridge_base +
752 MBUS_BRIDGE_BASE_OFF);
753
754 return 0;
755}
756
757static void mvebu_mbus_resume(void)
758{
759 struct mvebu_mbus_state *s = &mbus_state;
760 int win;
761
762 writel(s->mbus_bridge_ctrl,
763 s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF);
764 writel(s->mbus_bridge_base,
765 s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF);
766
767 for (win = 0; win < s->soc->num_wins; win++) {
768 void __iomem *addr = s->mbuswins_base +
769 s->soc->win_cfg_offset(win);
770
771 writel(s->wins[win].base, addr + WIN_BASE_OFF);
772 writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF);
773
774 if (win >= s->soc->num_remappable_wins)
775 continue;
776
777 writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF);
778 writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF);
779 }
780}
781
782struct syscore_ops mvebu_mbus_syscore_ops = {
783 .suspend = mvebu_mbus_suspend,
784 .resume = mvebu_mbus_resume,
785};
786
701static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, 787static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
702 phys_addr_t mbuswins_phys_base, 788 phys_addr_t mbuswins_phys_base,
703 size_t mbuswins_size, 789 size_t mbuswins_size,
704 phys_addr_t sdramwins_phys_base, 790 phys_addr_t sdramwins_phys_base,
705 size_t sdramwins_size) 791 size_t sdramwins_size,
792 phys_addr_t mbusbridge_phys_base,
793 size_t mbusbridge_size)
706{ 794{
707 int win; 795 int win;
708 796
@@ -716,11 +804,24 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
716 return -ENOMEM; 804 return -ENOMEM;
717 } 805 }
718 806
807 if (mbusbridge_phys_base) {
808 mbus->mbusbridge_base = ioremap(mbusbridge_phys_base,
809 mbusbridge_size);
810 if (!mbus->mbusbridge_base) {
811 iounmap(mbus->sdramwins_base);
812 iounmap(mbus->mbuswins_base);
813 return -ENOMEM;
814 }
815 } else
816 mbus->mbusbridge_base = NULL;
817
719 for (win = 0; win < mbus->soc->num_wins; win++) 818 for (win = 0; win < mbus->soc->num_wins; win++)
720 mvebu_mbus_disable_window(mbus, win); 819 mvebu_mbus_disable_window(mbus, win);
721 820
722 mbus->soc->setup_cpu_target(mbus); 821 mbus->soc->setup_cpu_target(mbus);
723 822
823 register_syscore_ops(&mvebu_mbus_syscore_ops);
824
724 return 0; 825 return 0;
725} 826}
726 827
@@ -746,7 +847,7 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
746 mbuswins_phys_base, 847 mbuswins_phys_base,
747 mbuswins_size, 848 mbuswins_size,
748 sdramwins_phys_base, 849 sdramwins_phys_base,
749 sdramwins_size); 850 sdramwins_size, 0, 0);
750} 851}
751 852
752#ifdef CONFIG_OF 853#ifdef CONFIG_OF
@@ -887,7 +988,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
887 988
888int __init mvebu_mbus_dt_init(bool is_coherent) 989int __init mvebu_mbus_dt_init(bool is_coherent)
889{ 990{
890 struct resource mbuswins_res, sdramwins_res; 991 struct resource mbuswins_res, sdramwins_res, mbusbridge_res;
891 struct device_node *np, *controller; 992 struct device_node *np, *controller;
892 const struct of_device_id *of_id; 993 const struct of_device_id *of_id;
893 const __be32 *prop; 994 const __be32 *prop;
@@ -923,6 +1024,19 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
923 return -EINVAL; 1024 return -EINVAL;
924 } 1025 }
925 1026
1027 /*
1028 * Set the resource to 0 so that it can be left unmapped by
1029 * mvebu_mbus_common_init() if the DT doesn't carry the
1030 * necessary information. This is needed to preserve backward
1031 * compatibility.
1032 */
1033 memset(&mbusbridge_res, 0, sizeof(mbusbridge_res));
1034
1035 if (mbus_state.soc->has_mbus_bridge) {
1036 if (of_address_to_resource(controller, 2, &mbusbridge_res))
1037 pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n");
1038 }
1039
926 mbus_state.hw_io_coherency = is_coherent; 1040 mbus_state.hw_io_coherency = is_coherent;
927 1041
928 /* Get optional pcie-{mem,io}-aperture properties */ 1042 /* Get optional pcie-{mem,io}-aperture properties */
@@ -933,7 +1047,9 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
933 mbuswins_res.start, 1047 mbuswins_res.start,
934 resource_size(&mbuswins_res), 1048 resource_size(&mbuswins_res),
935 sdramwins_res.start, 1049 sdramwins_res.start,
936 resource_size(&sdramwins_res)); 1050 resource_size(&sdramwins_res),
1051 mbusbridge_res.start,
1052 resource_size(&mbusbridge_res));
937 if (ret) 1053 if (ret)
938 return ret; 1054 return ret;
939 1055