~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/mach-mvebu/pm.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Suspend/resume support. Currently supporting Armada XP only.
  4  *
  5  * Copyright (C) 2014 Marvell
  6  *
  7  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
  8  */
  9 
 10 #include <linux/cpu_pm.h>
 11 #include <linux/delay.h>
 12 #include <linux/gpio.h>
 13 #include <linux/io.h>
 14 #include <linux/kernel.h>
 15 #include <linux/mbus.h>
 16 #include <linux/of_address.h>
 17 #include <linux/suspend.h>
 18 #include <asm/cacheflush.h>
 19 #include <asm/outercache.h>
 20 #include <asm/suspend.h>
 21 
 22 #include "coherency.h"
 23 #include "common.h"
 24 #include "pmsu.h"
 25 
 26 #define SDRAM_CONFIG_OFFS                  0x0
 27 #define  SDRAM_CONFIG_SR_MODE_BIT          BIT(24)
 28 #define SDRAM_OPERATION_OFFS               0x18
 29 #define  SDRAM_OPERATION_SELF_REFRESH      0x7
 30 #define SDRAM_DLB_EVICTION_OFFS            0x30c
 31 #define  SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff
 32 
 33 static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd);
 34 static void __iomem *sdram_ctrl;
 35 
 36 static int mvebu_pm_powerdown(unsigned long data)
 37 {
 38         u32 reg, srcmd;
 39 
 40         flush_cache_all();
 41         outer_flush_all();
 42 
 43         /*
 44          * Issue a Data Synchronization Barrier instruction to ensure
 45          * that all state saving has been completed.
 46          */
 47         dsb();
 48 
 49         /* Flush the DLB and wait ~7 usec */
 50         reg = readl(sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
 51         reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK;
 52         writel(reg, sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
 53 
 54         udelay(7);
 55 
 56         /* Set DRAM in battery backup mode */
 57         reg = readl(sdram_ctrl + SDRAM_CONFIG_OFFS);
 58         reg &= ~SDRAM_CONFIG_SR_MODE_BIT;
 59         writel(reg, sdram_ctrl + SDRAM_CONFIG_OFFS);
 60 
 61         /* Prepare to go to self-refresh */
 62 
 63         srcmd = readl(sdram_ctrl + SDRAM_OPERATION_OFFS);
 64         srcmd &= ~0x1F;
 65         srcmd |= SDRAM_OPERATION_SELF_REFRESH;
 66 
 67         mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd);
 68 
 69         return 0;
 70 }
 71 
 72 #define BOOT_INFO_ADDR      0x3000
 73 #define BOOT_MAGIC_WORD     0xdeadb002
 74 #define BOOT_MAGIC_LIST_END 0xffffffff
 75 
 76 /*
 77  * Those registers are accessed before switching the internal register
 78  * base, which is why we hardcode the 0xd0000000 base address, the one
 79  * used by the SoC out of reset.
 80  */
 81 #define MBUS_WINDOW_12_CTRL       0xd00200b0
 82 #define MBUS_INTERNAL_REG_ADDRESS 0xd0020080
 83 
 84 #define SDRAM_WIN_BASE_REG(x)   (0x20180 + (0x8*x))
 85 #define SDRAM_WIN_CTRL_REG(x)   (0x20184 + (0x8*x))
 86 
 87 static phys_addr_t mvebu_internal_reg_base(void)
 88 {
 89         struct device_node *np;
 90         __be32 in_addr[2];
 91 
 92         np = of_find_node_by_name(NULL, "internal-regs");
 93         BUG_ON(!np);
 94 
 95         /*
 96          * Ask the DT what is the internal register address on this
 97          * platform. In the mvebu-mbus DT binding, 0xf0010000
 98          * corresponds to the internal register window.
 99          */
100         in_addr[0] = cpu_to_be32(0xf0010000);
101         in_addr[1] = 0x0;
102 
103         return of_translate_address(np, in_addr);
104 }
105 
106 static void mvebu_pm_store_armadaxp_bootinfo(u32 *store_addr)
107 {
108         phys_addr_t resume_pc;
109 
110         resume_pc = __pa_symbol(armada_370_xp_cpu_resume);
111 
112         /*
113          * The bootloader expects the first two words to be a magic
114          * value (BOOT_MAGIC_WORD), followed by the address of the
115          * resume code to jump to. Then, it expects a sequence of
116          * (address, value) pairs, which can be used to restore the
117          * value of certain registers. This sequence must end with the
118          * BOOT_MAGIC_LIST_END magic value.
119          */
120 
121         writel(BOOT_MAGIC_WORD, store_addr++);
122         writel(resume_pc, store_addr++);
123 
124         /*
125          * Some platforms remap their internal register base address
126          * to 0xf1000000. However, out of reset, window 12 starts at
127          * 0xf0000000 and ends at 0xf7ffffff, which would overlap with
128          * the internal registers. Therefore, disable window 12.
129          */
130         writel(MBUS_WINDOW_12_CTRL, store_addr++);
131         writel(0x0, store_addr++);
132 
133         /*
134          * Set the internal register base address to the value
135          * expected by Linux, as read from the Device Tree.
136          */
137         writel(MBUS_INTERNAL_REG_ADDRESS, store_addr++);
138         writel(mvebu_internal_reg_base(), store_addr++);
139 
140         /*
141          * Ask the mvebu-mbus driver to store the SDRAM window
142          * configuration, which has to be restored by the bootloader
143          * before re-entering the kernel on resume.
144          */
145         store_addr += mvebu_mbus_save_cpu_target(store_addr);
146 
147         writel(BOOT_MAGIC_LIST_END, store_addr);
148 }
149 
150 static int mvebu_pm_store_bootinfo(void)
151 {
152         u32 *store_addr;
153 
154         store_addr = phys_to_virt(BOOT_INFO_ADDR);
155 
156         if (of_machine_is_compatible("marvell,armadaxp"))
157                 mvebu_pm_store_armadaxp_bootinfo(store_addr);
158         else
159                 return -ENODEV;
160 
161         return 0;
162 }
163 
164 static int mvebu_enter_suspend(void)
165 {
166         int ret;
167 
168         ret = mvebu_pm_store_bootinfo();
169         if (ret)
170                 return ret;
171 
172         cpu_pm_enter();
173 
174         cpu_suspend(0, mvebu_pm_powerdown);
175 
176         outer_resume();
177 
178         mvebu_v7_pmsu_idle_exit();
179 
180         set_cpu_coherent();
181 
182         cpu_pm_exit();
183         return 0;
184 }
185 
186 static int mvebu_pm_enter(suspend_state_t state)
187 {
188         switch (state) {
189         case PM_SUSPEND_STANDBY:
190                 cpu_do_idle();
191                 break;
192         case PM_SUSPEND_MEM:
193                 pr_warn("Entering suspend to RAM. Only special wake-up sources will resume the system\n");
194                 return mvebu_enter_suspend();
195         default:
196                 return -EINVAL;
197         }
198         return 0;
199 }
200 
201 static int mvebu_pm_valid(suspend_state_t state)
202 {
203         if (state == PM_SUSPEND_STANDBY)
204                 return 1;
205 
206         if (state == PM_SUSPEND_MEM && mvebu_board_pm_enter != NULL)
207                 return 1;
208 
209         return 0;
210 }
211 
212 static const struct platform_suspend_ops mvebu_pm_ops = {
213         .enter = mvebu_pm_enter,
214         .valid = mvebu_pm_valid,
215 };
216 
217 static int __init mvebu_pm_init(void)
218 {
219         if (!of_machine_is_compatible("marvell,armadaxp") &&
220             !of_machine_is_compatible("marvell,armada370") &&
221             !of_machine_is_compatible("marvell,armada380") &&
222             !of_machine_is_compatible("marvell,armada390"))
223                 return -ENODEV;
224 
225         suspend_set_ops(&mvebu_pm_ops);
226 
227         return 0;
228 }
229 
230 
231 late_initcall(mvebu_pm_init);
232 
233 int __init mvebu_pm_suspend_init(void (*board_pm_enter)(void __iomem *sdram_reg,
234                                                         u32 srcmd))
235 {
236         struct device_node *np;
237         struct resource res;
238 
239         np = of_find_compatible_node(NULL, NULL,
240                                      "marvell,armada-xp-sdram-controller");
241         if (!np)
242                 return -ENODEV;
243 
244         if (of_address_to_resource(np, 0, &res)) {
245                 of_node_put(np);
246                 return -ENODEV;
247         }
248 
249         if (!request_mem_region(res.start, resource_size(&res),
250                                 np->full_name)) {
251                 of_node_put(np);
252                 return -EBUSY;
253         }
254 
255         sdram_ctrl = ioremap(res.start, resource_size(&res));
256         if (!sdram_ctrl) {
257                 release_mem_region(res.start, resource_size(&res));
258                 of_node_put(np);
259                 return -ENOMEM;
260         }
261 
262         of_node_put(np);
263 
264         mvebu_board_pm_enter = board_pm_enter;
265 
266         return 0;
267 }
268 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php