aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2018-03-07 10:21:59 -0500
committerArnd Bergmann <arnd@arndb.de>2018-03-07 10:21:59 -0500
commit18b4788badeae8ed3a9bd3c240d83dffe5db8f37 (patch)
tree664dacce5739cfe87b791994c37ada5bb99b2fa2
parent6f566c4f304de8a90d520f7ca1925b4ea7dc3581 (diff)
parentafe761f8d3e99155b76833421e76553a3ac69577 (diff)
Merge tag 'omap-for-v4.17/am-pm-signed' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/soc
Pull "Add am335x and am437x PM code for v4.17" from Tony Lindgren: This series of changes from Dave Gerlach adds the PM related code to allow low-power suspend states. The code consists of the SoC specific assembly code and a related PM driver. * tag 'omap-for-v4.17/am-pm-signed' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap: soc: ti: Add pm33xx driver for basic suspend support ARM: OMAP2+: pm33xx-core: Add platform code needed for PM ARM: OMAP2+: Introduce low-level suspend code for AM43XX ARM: OMAP2+: Introduce low-level suspend code for AM33XX
-rw-r--r--Documentation/devicetree/bindings/arm/omap/mpu.txt16
-rw-r--r--arch/arm/mach-omap2/Kconfig1
-rw-r--r--arch/arm/mach-omap2/Makefile16
-rw-r--r--arch/arm/mach-omap2/common.h7
-rw-r--r--arch/arm/mach-omap2/io.c2
-rw-r--r--arch/arm/mach-omap2/pm-asm-offsets.c31
-rw-r--r--arch/arm/mach-omap2/pm.h3
-rw-r--r--arch/arm/mach-omap2/pm33xx-core.c189
-rw-r--r--arch/arm/mach-omap2/sleep33xx.S214
-rw-r--r--arch/arm/mach-omap2/sleep43xx.S387
-rw-r--r--drivers/soc/ti/Kconfig9
-rw-r--r--drivers/soc/ti/Makefile1
-rw-r--r--drivers/soc/ti/pm33xx.c349
-rw-r--r--include/linux/platform_data/pm33xx.h42
14 files changed, 1267 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/omap/mpu.txt b/Documentation/devicetree/bindings/arm/omap/mpu.txt
index 763695db2bd9..f301e636fd52 100644
--- a/Documentation/devicetree/bindings/arm/omap/mpu.txt
+++ b/Documentation/devicetree/bindings/arm/omap/mpu.txt
@@ -13,6 +13,13 @@ Required properties:
13Optional properties: 13Optional properties:
14- sram: Phandle to the ocmcram node 14- sram: Phandle to the ocmcram node
15 15
16am335x and am437x only:
17- pm-sram: Phandles to ocmcram nodes to be used for power management.
18 First should be type 'protect-exec' for the driver to use to copy
19 and run PM functions, second should be regular pool to be used for
20 data region for code. See Documentation/devicetree/bindings/sram/sram.txt
21 for more details.
22
16Examples: 23Examples:
17 24
18- For an OMAP5 SMP system: 25- For an OMAP5 SMP system:
@@ -36,3 +43,12 @@ mpu {
36 compatible = "ti,omap3-mpu"; 43 compatible = "ti,omap3-mpu";
37 ti,hwmods = "mpu"; 44 ti,hwmods = "mpu";
38}; 45};
46
47- For an AM335x system:
48
49mpu {
50 compatible = "ti,omap3-mpu";
51 ti,hwmods = "mpu";
52 pm-sram = <&pm_sram_code
53 &pm_sram_data>;
54};
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 00b1f17f8d44..9f27b486a536 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -72,6 +72,7 @@ config SOC_AM43XX
72 select ARM_ERRATA_754322 72 select ARM_ERRATA_754322
73 select ARM_ERRATA_775420 73 select ARM_ERRATA_775420
74 select OMAP_INTERCONNECT 74 select OMAP_INTERCONNECT
75 select ARM_CPU_SUSPEND if PM
75 76
76config SOC_DRA7XX 77config SOC_DRA7XX
77 bool "TI DRA7XX" 78 bool "TI DRA7XX"
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index c15bbcad5f67..4603c30fef73 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -88,6 +88,8 @@ omap-4-5-pm-common += pm44xx.o
88obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common) 88obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common)
89obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common) 89obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common)
90obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-pm-common) 90obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-pm-common)
91obj-$(CONFIG_SOC_AM33XX) += pm33xx-core.o sleep33xx.o
92obj-$(CONFIG_SOC_AM43XX) += pm33xx-core.o sleep43xx.o
91obj-$(CONFIG_PM_DEBUG) += pm-debug.o 93obj-$(CONFIG_PM_DEBUG) += pm-debug.o
92 94
93obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o 95obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o
@@ -95,6 +97,8 @@ obj-$(CONFIG_POWER_AVS_OMAP_CLASS3) += smartreflex-class3.o
95 97
96AFLAGS_sleep24xx.o :=-Wa,-march=armv6 98AFLAGS_sleep24xx.o :=-Wa,-march=armv6
97AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec) 99AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec)
100AFLAGS_sleep33xx.o :=-Wa,-march=armv7-a$(plus_sec)
101AFLAGS_sleep43xx.o :=-Wa,-march=armv7-a$(plus_sec)
98 102
99endif 103endif
100 104
@@ -232,3 +236,15 @@ obj-y += $(omap-hsmmc-m) $(omap-hsmmc-y)
232obj-y += omap_phy_internal.o 236obj-y += omap_phy_internal.o
233 237
234obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o 238obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o
239
240arch/arm/mach-omap2/pm-asm-offsets.s: arch/arm/mach-omap2/pm-asm-offsets.c
241 $(call if_changed_dep,cc_s_c)
242
243include/generated/ti-pm-asm-offsets.h: arch/arm/mach-omap2/pm-asm-offsets.s FORCE
244 $(call filechk,offsets,__TI_PM_ASM_OFFSETS_H__)
245
246# For rule to generate ti-emif-asm-offsets.h dependency
247include drivers/memory/Makefile.asm-offsets
248
249arch/arm/mach-omap2/sleep33xx.o: include/generated/ti-pm-asm-offsets.h include/generated/ti-emif-asm-offsets.h
250arch/arm/mach-omap2/sleep43xx.o: include/generated/ti-pm-asm-offsets.h include/generated/ti-emif-asm-offsets.h
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index bc202835371b..fbe0b78bf489 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -77,6 +77,13 @@ static inline int omap4_pm_init_early(void)
77} 77}
78#endif 78#endif
79 79
80#if defined(CONFIG_PM) && (defined(CONFIG_SOC_AM33XX) || \
81 defined(CONFIG_SOC_AM43XX))
82void amx3_common_pm_init(void);
83#else
84static inline void amx3_common_pm_init(void) { }
85#endif
86
80extern void omap2_init_common_infrastructure(void); 87extern void omap2_init_common_infrastructure(void);
81 88
82extern void omap_init_time(void); 89extern void omap_init_time(void);
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index cb5d7314cf99..cf546dfe3b32 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -622,6 +622,7 @@ void __init am33xx_init_early(void)
622void __init am33xx_init_late(void) 622void __init am33xx_init_late(void)
623{ 623{
624 omap_common_late_init(); 624 omap_common_late_init();
625 amx3_common_pm_init();
625} 626}
626#endif 627#endif
627 628
@@ -646,6 +647,7 @@ void __init am43xx_init_late(void)
646{ 647{
647 omap_common_late_init(); 648 omap_common_late_init();
648 omap2_clk_enable_autoidle_all(); 649 omap2_clk_enable_autoidle_all();
650 amx3_common_pm_init();
649} 651}
650#endif 652#endif
651 653
diff --git a/arch/arm/mach-omap2/pm-asm-offsets.c b/arch/arm/mach-omap2/pm-asm-offsets.c
new file mode 100644
index 000000000000..6d4392da7c11
--- /dev/null
+++ b/arch/arm/mach-omap2/pm-asm-offsets.c
@@ -0,0 +1,31 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * TI AM33XX and AM43XX PM Assembly Offsets
4 *
5 * Copyright (C) 2017-2018 Texas Instruments Inc.
6 */
7
8#include <linux/kbuild.h>
9#include <linux/platform_data/pm33xx.h>
10
11int main(void)
12{
13 DEFINE(AMX3_PM_WFI_FLAGS_OFFSET,
14 offsetof(struct am33xx_pm_sram_data, wfi_flags));
15 DEFINE(AMX3_PM_L2_AUX_CTRL_VAL_OFFSET,
16 offsetof(struct am33xx_pm_sram_data, l2_aux_ctrl_val));
17 DEFINE(AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET,
18 offsetof(struct am33xx_pm_sram_data, l2_prefetch_ctrl_val));
19 DEFINE(AMX3_PM_SRAM_DATA_SIZE, sizeof(struct am33xx_pm_sram_data));
20
21 BLANK();
22
23 DEFINE(AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET,
24 offsetof(struct am33xx_pm_ro_sram_data, amx3_pm_sram_data_virt));
25 DEFINE(AMX3_PM_RO_SRAM_DATA_PHYS_OFFSET,
26 offsetof(struct am33xx_pm_ro_sram_data, amx3_pm_sram_data_phys));
27 DEFINE(AMX3_PM_RO_SRAM_DATA_SIZE,
28 sizeof(struct am33xx_pm_ro_sram_data));
29
30 return 0;
31}
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index 8e30772cfe32..c73776b82348 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -81,6 +81,9 @@ extern unsigned int omap3_do_wfi_sz;
81/* ... and its pointer from SRAM after copy */ 81/* ... and its pointer from SRAM after copy */
82extern void (*omap3_do_wfi_sram)(void); 82extern void (*omap3_do_wfi_sram)(void);
83 83
84extern struct am33xx_pm_sram_addr am33xx_pm_sram;
85extern struct am33xx_pm_sram_addr am43xx_pm_sram;
86
84extern void omap3_save_scratchpad_contents(void); 87extern void omap3_save_scratchpad_contents(void);
85 88
86#define PM_RTA_ERRATUM_i608 (1 << 0) 89#define PM_RTA_ERRATUM_i608 (1 << 0)
diff --git a/arch/arm/mach-omap2/pm33xx-core.c b/arch/arm/mach-omap2/pm33xx-core.c
new file mode 100644
index 000000000000..93c0b5ba9f09
--- /dev/null
+++ b/arch/arm/mach-omap2/pm33xx-core.c
@@ -0,0 +1,189 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * AM33XX Arch Power Management Routines
4 *
5 * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/
6 * Dave Gerlach
7 */
8
9#include <asm/smp_scu.h>
10#include <asm/suspend.h>
11#include <linux/errno.h>
12#include <linux/platform_data/pm33xx.h>
13
14#include "cm33xx.h"
15#include "common.h"
16#include "control.h"
17#include "clockdomain.h"
18#include "iomap.h"
19#include "omap_hwmod.h"
20#include "pm.h"
21#include "powerdomain.h"
22#include "prm33xx.h"
23#include "soc.h"
24#include "sram.h"
25
26static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm;
27static struct clockdomain *gfx_l4ls_clkdm;
28static void __iomem *scu_base;
29
30static int __init am43xx_map_scu(void)
31{
32 scu_base = ioremap(scu_a9_get_base(), SZ_256);
33
34 if (!scu_base)
35 return -ENOMEM;
36
37 return 0;
38}
39
40static int amx3_common_init(void)
41{
42 gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
43 per_pwrdm = pwrdm_lookup("per_pwrdm");
44 mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
45
46 if ((!gfx_pwrdm) || (!per_pwrdm) || (!mpu_pwrdm))
47 return -ENODEV;
48
49 (void)clkdm_for_each(omap_pm_clkdms_setup, NULL);
50
51 /* CEFUSE domain can be turned off post bootup */
52 cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm");
53 if (cefuse_pwrdm)
54 omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF);
55 else
56 pr_err("PM: Failed to get cefuse_pwrdm\n");
57
58 return 0;
59}
60
61static int am33xx_suspend_init(void)
62{
63 int ret;
64
65 gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm");
66
67 if (!gfx_l4ls_clkdm) {
68 pr_err("PM: Cannot lookup gfx_l4ls_clkdm clockdomains\n");
69 return -ENODEV;
70 }
71
72 ret = amx3_common_init();
73
74 return ret;
75}
76
77static int am43xx_suspend_init(void)
78{
79 int ret = 0;
80
81 ret = am43xx_map_scu();
82 if (ret) {
83 pr_err("PM: Could not ioremap SCU\n");
84 return ret;
85 }
86
87 ret = amx3_common_init();
88
89 return ret;
90}
91
92static void amx3_pre_suspend_common(void)
93{
94 omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF);
95}
96
97static void amx3_post_suspend_common(void)
98{
99 int status;
100 /*
101 * Because gfx_pwrdm is the only one under MPU control,
102 * comment on transition status
103 */
104 status = pwrdm_read_pwrst(gfx_pwrdm);
105 if (status != PWRDM_POWER_OFF)
106 pr_err("PM: GFX domain did not transition: %x\n", status);
107}
108
109static int am33xx_suspend(unsigned int state, int (*fn)(unsigned long))
110{
111 int ret = 0;
112
113 amx3_pre_suspend_common();
114 ret = cpu_suspend(0, fn);
115 amx3_post_suspend_common();
116
117 /*
118 * BUG: GFX_L4LS clock domain needs to be woken up to
119 * ensure thet L4LS clock domain does not get stuck in
120 * transition. If that happens L3 module does not get
121 * disabled, thereby leading to PER power domain
122 * transition failing
123 */
124
125 clkdm_wakeup(gfx_l4ls_clkdm);
126 clkdm_sleep(gfx_l4ls_clkdm);
127
128 return ret;
129}
130
131static int am43xx_suspend(unsigned int state, int (*fn)(unsigned long))
132{
133 int ret = 0;
134
135 amx3_pre_suspend_common();
136 scu_power_mode(scu_base, SCU_PM_POWEROFF);
137 ret = cpu_suspend(0, fn);
138 scu_power_mode(scu_base, SCU_PM_NORMAL);
139 amx3_post_suspend_common();
140
141 return ret;
142}
143
144static struct am33xx_pm_sram_addr *amx3_get_sram_addrs(void)
145{
146 if (soc_is_am33xx())
147 return &am33xx_pm_sram;
148 else if (soc_is_am437x())
149 return &am43xx_pm_sram;
150 else
151 return NULL;
152}
153
154static struct am33xx_pm_platform_data am33xx_ops = {
155 .init = am33xx_suspend_init,
156 .soc_suspend = am33xx_suspend,
157 .get_sram_addrs = amx3_get_sram_addrs,
158};
159
160static struct am33xx_pm_platform_data am43xx_ops = {
161 .init = am43xx_suspend_init,
162 .soc_suspend = am43xx_suspend,
163 .get_sram_addrs = amx3_get_sram_addrs,
164};
165
166static struct am33xx_pm_platform_data *am33xx_pm_get_pdata(void)
167{
168 if (soc_is_am33xx())
169 return &am33xx_ops;
170 else if (soc_is_am437x())
171 return &am43xx_ops;
172 else
173 return NULL;
174}
175
176void __init amx3_common_pm_init(void)
177{
178 struct am33xx_pm_platform_data *pdata;
179 struct platform_device_info devinfo;
180
181 pdata = am33xx_pm_get_pdata();
182
183 memset(&devinfo, 0, sizeof(devinfo));
184 devinfo.name = "pm33xx";
185 devinfo.data = pdata;
186 devinfo.size_data = sizeof(*pdata);
187 devinfo.id = -1;
188 platform_device_register_full(&devinfo);
189}
diff --git a/arch/arm/mach-omap2/sleep33xx.S b/arch/arm/mach-omap2/sleep33xx.S
new file mode 100644
index 000000000000..218d79930b04
--- /dev/null
+++ b/arch/arm/mach-omap2/sleep33xx.S
@@ -0,0 +1,214 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Low level suspend code for AM33XX SoCs
4 *
5 * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/
6 * Dave Gerlach, Vaibhav Bedia
7 */
8
9#include <generated/ti-emif-asm-offsets.h>
10#include <generated/ti-pm-asm-offsets.h>
11#include <linux/linkage.h>
12#include <linux/ti-emif-sram.h>
13#include <asm/assembler.h>
14#include <asm/memory.h>
15
16#include "iomap.h"
17#include "cm33xx.h"
18
19#define AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED 0x00030000
20#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE 0x0003
21#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE 0x0002
22
23 .arm
24 .align 3
25
26ENTRY(am33xx_do_wfi)
27 stmfd sp!, {r4 - r11, lr} @ save registers on stack
28
29 /*
30 * Flush all data from the L1 and L2 data cache before disabling
31 * SCTLR.C bit.
32 */
33 ldr r1, kernel_flush
34 blx r1
35
36 /*
37 * Clear the SCTLR.C bit to prevent further data cache
38 * allocation. Clearing SCTLR.C would make all the data accesses
39 * strongly ordered and would not hit the cache.
40 */
41 mrc p15, 0, r0, c1, c0, 0
42 bic r0, r0, #(1 << 2) @ Disable the C bit
43 mcr p15, 0, r0, c1, c0, 0
44 isb
45
46 /*
47 * Invalidate L1 and L2 data cache.
48 */
49 ldr r1, kernel_flush
50 blx r1
51
52 adr r9, am33xx_emif_sram_table
53
54 ldr r3, [r9, #EMIF_PM_ENTER_SR_OFFSET]
55 blx r3
56
57 ldr r3, [r9, #EMIF_PM_SAVE_CONTEXT_OFFSET]
58 blx r3
59
60 /* Disable EMIF */
61 ldr r1, virt_emif_clkctrl
62 ldr r2, [r1]
63 bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
64 str r2, [r1]
65
66 ldr r1, virt_emif_clkctrl
67wait_emif_disable:
68 ldr r2, [r1]
69 mov r3, #AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED
70 cmp r2, r3
71 bne wait_emif_disable
72
73 /*
74 * For the MPU WFI to be registered as an interrupt
75 * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set
76 * to DISABLED
77 */
78 ldr r1, virt_mpu_clkctrl
79 ldr r2, [r1]
80 bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
81 str r2, [r1]
82
83 /*
84 * Execute an ISB instruction to ensure that all of the
85 * CP15 register changes have been committed.
86 */
87 isb
88
89 /*
90 * Execute a barrier instruction to ensure that all cache,
91 * TLB and branch predictor maintenance operations issued
92 * have completed.
93 */
94 dsb
95 dmb
96
97 /*
98 * Execute a WFI instruction and wait until the
99 * STANDBYWFI output is asserted to indicate that the
100 * CPU is in idle and low power state. CPU can specualatively
101 * prefetch the instructions so add NOPs after WFI. Thirteen
102 * NOPs as per Cortex-A8 pipeline.
103 */
104 wfi
105
106 nop
107 nop
108 nop
109 nop
110 nop
111 nop
112 nop
113 nop
114 nop
115 nop
116 nop
117 nop
118 nop
119
120 /* We come here in case of an abort due to a late interrupt */
121
122 /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */
123 ldr r1, virt_mpu_clkctrl
124 mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
125 str r2, [r1]
126
127 /* Re-enable EMIF */
128 ldr r1, virt_emif_clkctrl
129 mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
130 str r2, [r1]
131wait_emif_enable:
132 ldr r3, [r1]
133 cmp r2, r3
134 bne wait_emif_enable
135
136
137 ldr r1, [r9, #EMIF_PM_ABORT_SR_OFFSET]
138 blx r1
139
140 /*
141 * Set SCTLR.C bit to allow data cache allocation
142 */
143 mrc p15, 0, r0, c1, c0, 0
144 orr r0, r0, #(1 << 2) @ Enable the C bit
145 mcr p15, 0, r0, c1, c0, 0
146 isb
147
148 /* Let the suspend code know about the abort */
149 mov r0, #1
150 ldmfd sp!, {r4 - r11, pc} @ restore regs and return
151ENDPROC(am33xx_do_wfi)
152
153 .align
154ENTRY(am33xx_resume_offset)
155 .word . - am33xx_do_wfi
156
157ENTRY(am33xx_resume_from_deep_sleep)
158 /* Re-enable EMIF */
159 ldr r0, phys_emif_clkctrl
160 mov r1, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
161 str r1, [r0]
162wait_emif_enable1:
163 ldr r2, [r0]
164 cmp r1, r2
165 bne wait_emif_enable1
166
167 adr r9, am33xx_emif_sram_table
168
169 ldr r1, [r9, #EMIF_PM_RESTORE_CONTEXT_OFFSET]
170 blx r1
171
172 ldr r1, [r9, #EMIF_PM_EXIT_SR_OFFSET]
173 blx r1
174
175resume_to_ddr:
176 /* We are back. Branch to the common CPU resume routine */
177 mov r0, #0
178 ldr pc, resume_addr
179ENDPROC(am33xx_resume_from_deep_sleep)
180
181/*
182 * Local variables
183 */
184 .align
185resume_addr:
186 .word cpu_resume - PAGE_OFFSET + 0x80000000
187kernel_flush:
188 .word v7_flush_dcache_all
189virt_mpu_clkctrl:
190 .word AM33XX_CM_MPU_MPU_CLKCTRL
191virt_emif_clkctrl:
192 .word AM33XX_CM_PER_EMIF_CLKCTRL
193phys_emif_clkctrl:
194 .word (AM33XX_CM_BASE + AM33XX_CM_PER_MOD + \
195 AM33XX_CM_PER_EMIF_CLKCTRL_OFFSET)
196
197.align 3
198/* DDR related defines */
199am33xx_emif_sram_table:
200 .space EMIF_PM_FUNCTIONS_SIZE
201
202ENTRY(am33xx_pm_sram)
203 .word am33xx_do_wfi
204 .word am33xx_do_wfi_sz
205 .word am33xx_resume_offset
206 .word am33xx_emif_sram_table
207 .word am33xx_pm_ro_sram_data
208
209.align 3
210ENTRY(am33xx_pm_ro_sram_data)
211 .space AMX3_PM_RO_SRAM_DATA_SIZE
212
213ENTRY(am33xx_do_wfi_sz)
214 .word . - am33xx_do_wfi
diff --git a/arch/arm/mach-omap2/sleep43xx.S b/arch/arm/mach-omap2/sleep43xx.S
new file mode 100644
index 000000000000..59770a69396b
--- /dev/null
+++ b/arch/arm/mach-omap2/sleep43xx.S
@@ -0,0 +1,387 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Low level suspend code for AM43XX SoCs
4 *
5 * Copyright (C) 2013-2018 Texas Instruments Incorporated - http://www.ti.com/
6 * Dave Gerlach, Vaibhav Bedia
7 */
8
9#include <generated/ti-emif-asm-offsets.h>
10#include <generated/ti-pm-asm-offsets.h>
11#include <linux/linkage.h>
12#include <linux/ti-emif-sram.h>
13
14#include <asm/assembler.h>
15#include <asm/hardware/cache-l2x0.h>
16#include <asm/memory.h>
17
18#include "cm33xx.h"
19#include "common.h"
20#include "iomap.h"
21#include "omap-secure.h"
22#include "omap44xx.h"
23#include "prm33xx.h"
24#include "prcm43xx.h"
25
26#define AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED 0x00030000
27#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE 0x0003
28#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE 0x0002
29
30#define AM43XX_EMIF_POWEROFF_ENABLE 0x1
31#define AM43XX_EMIF_POWEROFF_DISABLE 0x0
32
33#define AM43XX_CM_CLKSTCTRL_CLKTRCTRL_SW_SLEEP 0x1
34#define AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO 0x3
35
36#define AM43XX_CM_BASE 0x44DF0000
37
38#define AM43XX_CM_REGADDR(inst, reg) \
39 AM33XX_L4_WK_IO_ADDRESS(AM43XX_CM_BASE + (inst) + (reg))
40
41#define AM43XX_CM_MPU_CLKSTCTRL AM43XX_CM_REGADDR(AM43XX_CM_MPU_INST, \
42 AM43XX_CM_MPU_MPU_CDOFFS)
43#define AM43XX_CM_MPU_MPU_CLKCTRL AM43XX_CM_REGADDR(AM43XX_CM_MPU_INST, \
44 AM43XX_CM_MPU_MPU_CLKCTRL_OFFSET)
45#define AM43XX_CM_PER_EMIF_CLKCTRL AM43XX_CM_REGADDR(AM43XX_CM_PER_INST, \
46 AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET)
47#define AM43XX_PRM_EMIF_CTRL_OFFSET 0x0030
48
49 .arm
50 .align 3
51
52ENTRY(am43xx_do_wfi)
53 stmfd sp!, {r4 - r11, lr} @ save registers on stack
54
55 /* Retrieve l2 cache virt address BEFORE we shut off EMIF */
56 ldr r1, get_l2cache_base
57 blx r1
58 mov r8, r0
59
60 /*
61 * Flush all data from the L1 and L2 data cache before disabling
62 * SCTLR.C bit.
63 */
64 ldr r1, kernel_flush
65 blx r1
66
67 /*
68 * Clear the SCTLR.C bit to prevent further data cache
69 * allocation. Clearing SCTLR.C would make all the data accesses
70 * strongly ordered and would not hit the cache.
71 */
72 mrc p15, 0, r0, c1, c0, 0
73 bic r0, r0, #(1 << 2) @ Disable the C bit
74 mcr p15, 0, r0, c1, c0, 0
75 isb
76 dsb
77
78 /*
79 * Invalidate L1 and L2 data cache.
80 */
81 ldr r1, kernel_flush
82 blx r1
83
84#ifdef CONFIG_CACHE_L2X0
85 /*
86 * Clean and invalidate the L2 cache.
87 */
88#ifdef CONFIG_PL310_ERRATA_727915
89 mov r0, #0x03
90 mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX
91 dsb
92 smc #0
93 dsb
94#endif
95 mov r0, r8
96 adr r4, am43xx_pm_ro_sram_data
97 ldr r3, [r4, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET]
98
99 mov r2, r0
100 ldr r0, [r2, #L2X0_AUX_CTRL]
101 str r0, [r3, #AMX3_PM_L2_AUX_CTRL_VAL_OFFSET]
102 ldr r0, [r2, #L310_PREFETCH_CTRL]
103 str r0, [r3, #AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET]
104
105 ldr r0, l2_val
106 str r0, [r2, #L2X0_CLEAN_INV_WAY]
107wait:
108 ldr r0, [r2, #L2X0_CLEAN_INV_WAY]
109 ldr r1, l2_val
110 ands r0, r0, r1
111 bne wait
112#ifdef CONFIG_PL310_ERRATA_727915
113 mov r0, #0x00
114 mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX
115 dsb
116 smc #0
117 dsb
118#endif
119l2x_sync:
120 mov r0, r8
121 mov r2, r0
122 mov r0, #0x0
123 str r0, [r2, #L2X0_CACHE_SYNC]
124sync:
125 ldr r0, [r2, #L2X0_CACHE_SYNC]
126 ands r0, r0, #0x1
127 bne sync
128#endif
129
130 adr r9, am43xx_emif_sram_table
131
132 ldr r3, [r9, #EMIF_PM_ENTER_SR_OFFSET]
133 blx r3
134
135 ldr r3, [r9, #EMIF_PM_SAVE_CONTEXT_OFFSET]
136 blx r3
137
138 /* Disable EMIF */
139 ldr r1, am43xx_virt_emif_clkctrl
140 ldr r2, [r1]
141 bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
142 str r2, [r1]
143
144wait_emif_disable:
145 ldr r2, [r1]
146 mov r3, #AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED
147 cmp r2, r3
148 bne wait_emif_disable
149
150 /*
151 * For the MPU WFI to be registered as an interrupt
152 * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set
153 * to DISABLED
154 */
155 ldr r1, am43xx_virt_mpu_clkctrl
156 ldr r2, [r1]
157 bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
158 str r2, [r1]
159
160 /*
161 * Put MPU CLKDM to SW_SLEEP
162 */
163 ldr r1, am43xx_virt_mpu_clkstctrl
164 mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_SW_SLEEP
165 str r2, [r1]
166
167 /*
168 * Execute a barrier instruction to ensure that all cache,
169 * TLB and branch predictor maintenance operations issued
170 * have completed.
171 */
172 dsb
173 dmb
174
175 /*
176 * Execute a WFI instruction and wait until the
177 * STANDBYWFI output is asserted to indicate that the
178 * CPU is in idle and low power state. CPU can specualatively
179 * prefetch the instructions so add NOPs after WFI. Sixteen
180 * NOPs as per Cortex-A9 pipeline.
181 */
182 wfi
183
184 nop
185 nop
186 nop
187 nop
188 nop
189 nop
190 nop
191 nop
192 nop
193 nop
194 nop
195 nop
196 nop
197 nop
198 nop
199 nop
200
201 /* We come here in case of an abort due to a late interrupt */
202 ldr r1, am43xx_virt_mpu_clkstctrl
203 mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO
204 str r2, [r1]
205
206 /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */
207 ldr r1, am43xx_virt_mpu_clkctrl
208 mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
209 str r2, [r1]
210
211 /* Re-enable EMIF */
212 ldr r1, am43xx_virt_emif_clkctrl
213 mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
214 str r2, [r1]
215wait_emif_enable:
216 ldr r3, [r1]
217 cmp r2, r3
218 bne wait_emif_enable
219
220 /*
221 * Set SCTLR.C bit to allow data cache allocation
222 */
223 mrc p15, 0, r0, c1, c0, 0
224 orr r0, r0, #(1 << 2) @ Enable the C bit
225 mcr p15, 0, r0, c1, c0, 0
226 isb
227
228 ldr r1, [r9, #EMIF_PM_ABORT_SR_OFFSET]
229 blx r1
230
231 /* Let the suspend code know about the abort */
232 mov r0, #1
233 ldmfd sp!, {r4 - r11, pc} @ restore regs and return
234ENDPROC(am43xx_do_wfi)
235
236 .align
237ENTRY(am43xx_resume_offset)
238 .word . - am43xx_do_wfi
239
240ENTRY(am43xx_resume_from_deep_sleep)
241 /* Set MPU CLKSTCTRL to HW AUTO so that CPUidle works properly */
242 ldr r1, am43xx_virt_mpu_clkstctrl
243 mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO
244 str r2, [r1]
245
246 /* For AM43xx, use EMIF power down until context is restored */
247 ldr r2, am43xx_phys_emif_poweroff
248 mov r1, #AM43XX_EMIF_POWEROFF_ENABLE
249 str r1, [r2, #0x0]
250
251 /* Re-enable EMIF */
252 ldr r1, am43xx_phys_emif_clkctrl
253 mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
254 str r2, [r1]
255wait_emif_enable1:
256 ldr r3, [r1]
257 cmp r2, r3
258 bne wait_emif_enable1
259
260 adr r9, am43xx_emif_sram_table
261
262 ldr r1, [r9, #EMIF_PM_RESTORE_CONTEXT_OFFSET]
263 blx r1
264
265 ldr r1, [r9, #EMIF_PM_EXIT_SR_OFFSET]
266 blx r1
267
268 ldr r2, am43xx_phys_emif_poweroff
269 mov r1, #AM43XX_EMIF_POWEROFF_DISABLE
270 str r1, [r2, #0x0]
271
272#ifdef CONFIG_CACHE_L2X0
273 ldr r2, l2_cache_base
274 ldr r0, [r2, #L2X0_CTRL]
275 and r0, #0x0f
276 cmp r0, #1
277 beq skip_l2en @ Skip if already enabled
278
279 adr r4, am43xx_pm_ro_sram_data
280 ldr r3, [r4, #AMX3_PM_RO_SRAM_DATA_PHYS_OFFSET]
281 ldr r0, [r3, #AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET]
282
283 ldr r12, l2_smc1
284 dsb
285 smc #0
286 dsb
287set_aux_ctrl:
288 ldr r0, [r3, #AMX3_PM_L2_AUX_CTRL_VAL_OFFSET]
289 ldr r12, l2_smc2
290 dsb
291 smc #0
292 dsb
293
294 /* L2 invalidate on resume */
295 ldr r0, l2_val
296 ldr r2, l2_cache_base
297 str r0, [r2, #L2X0_INV_WAY]
298wait2:
299 ldr r0, [r2, #L2X0_INV_WAY]
300 ldr r1, l2_val
301 ands r0, r0, r1
302 bne wait2
303#ifdef CONFIG_PL310_ERRATA_727915
304 mov r0, #0x00
305 mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX
306 dsb
307 smc #0
308 dsb
309#endif
310l2x_sync2:
311 ldr r2, l2_cache_base
312 mov r0, #0x0
313 str r0, [r2, #L2X0_CACHE_SYNC]
314sync2:
315 ldr r0, [r2, #L2X0_CACHE_SYNC]
316 ands r0, r0, #0x1
317 bne sync2
318
319 mov r0, #0x1
320 ldr r12, l2_smc3
321 dsb
322 smc #0
323 dsb
324#endif
325skip_l2en:
326 /* We are back. Branch to the common CPU resume routine */
327 mov r0, #0
328 ldr pc, resume_addr
329ENDPROC(am43xx_resume_from_deep_sleep)
330
331/*
332 * Local variables
333 */
334 .align
335resume_addr:
336 .word cpu_resume - PAGE_OFFSET + 0x80000000
337get_l2cache_base:
338 .word omap4_get_l2cache_base
339kernel_flush:
340 .word v7_flush_dcache_all
341ddr_start:
342 .word PAGE_OFFSET
343
344am43xx_phys_emif_poweroff:
345 .word (AM43XX_CM_BASE + AM43XX_PRM_DEVICE_INST + \
346 AM43XX_PRM_EMIF_CTRL_OFFSET)
347am43xx_virt_mpu_clkstctrl:
348 .word (AM43XX_CM_MPU_CLKSTCTRL)
349am43xx_virt_mpu_clkctrl:
350 .word (AM43XX_CM_MPU_MPU_CLKCTRL)
351am43xx_virt_emif_clkctrl:
352 .word (AM43XX_CM_PER_EMIF_CLKCTRL)
353am43xx_phys_emif_clkctrl:
354 .word (AM43XX_CM_BASE + AM43XX_CM_PER_INST + \
355 AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET)
356
357/* L2 cache related defines for AM437x */
358l2_cache_base:
359 .word OMAP44XX_L2CACHE_BASE
360l2_smc1:
361 .word OMAP4_MON_L2X0_PREFETCH_INDEX
362l2_smc2:
363 .word OMAP4_MON_L2X0_AUXCTRL_INDEX
364l2_smc3:
365 .word OMAP4_MON_L2X0_CTRL_INDEX
366l2_val:
367 .word 0xffff
368
369.align 3
370/* DDR related defines */
371ENTRY(am43xx_emif_sram_table)
372 .space EMIF_PM_FUNCTIONS_SIZE
373
374ENTRY(am43xx_pm_sram)
375 .word am43xx_do_wfi
376 .word am43xx_do_wfi_sz
377 .word am43xx_resume_offset
378 .word am43xx_emif_sram_table
379 .word am43xx_pm_ro_sram_data
380
381.align 3
382
383ENTRY(am43xx_pm_ro_sram_data)
384 .space AMX3_PM_RO_SRAM_DATA_SIZE
385
386ENTRY(am43xx_do_wfi_sz)
387 .word . - am43xx_do_wfi
diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index 39e152abe6b9..92770d84a288 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -28,6 +28,15 @@ config KEYSTONE_NAVIGATOR_DMA
28 28
29 If unsure, say N. 29 If unsure, say N.
30 30
31config AMX3_PM
32 tristate "AMx3 Power Management"
33 depends on SOC_AM33XX || SOC_AM43XX
34 depends on WKUP_M3_IPC && TI_EMIF_SRAM && SRAM
35 help
36 Enable power management on AM335x and AM437x. Required for suspend to mem
37 and standby states on both AM335x and AM437x platforms and for deeper cpuidle
38 c-states on AM335x.
39
31config WKUP_M3_IPC 40config WKUP_M3_IPC
32 tristate "TI AMx3 Wkup-M3 IPC Driver" 41 tristate "TI AMx3 Wkup-M3 IPC Driver"
33 depends on WKUP_M3_RPROC 42 depends on WKUP_M3_RPROC
diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
index 8e205287f120..a22edc0b258a 100644
--- a/drivers/soc/ti/Makefile
+++ b/drivers/soc/ti/Makefile
@@ -5,5 +5,6 @@
5obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o 5obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o
6knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o 6knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o
7obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o 7obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o
8obj-$(CONFIG_AMX3_PM) += pm33xx.o
8obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o 9obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o
9obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o 10obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o
diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c
new file mode 100644
index 000000000000..652739c7f718
--- /dev/null
+++ b/drivers/soc/ti/pm33xx.c
@@ -0,0 +1,349 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * AM33XX Power Management Routines
4 *
5 * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/
6 * Vaibhav Bedia, Dave Gerlach
7 */
8
9#include <linux/cpu.h>
10#include <linux/err.h>
11#include <linux/genalloc.h>
12#include <linux/kernel.h>
13#include <linux/init.h>
14#include <linux/io.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/platform_data/pm33xx.h>
18#include <linux/platform_device.h>
19#include <linux/sizes.h>
20#include <linux/sram.h>
21#include <linux/suspend.h>
22#include <linux/ti-emif-sram.h>
23#include <linux/wkup_m3_ipc.h>
24
25#include <asm/proc-fns.h>
26#include <asm/suspend.h>
27#include <asm/system_misc.h>
28
29#define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \
30 (unsigned long)pm_sram->do_wfi)
31
32static int (*am33xx_do_wfi_sram)(unsigned long unused);
33static phys_addr_t am33xx_do_wfi_sram_phys;
34
35static struct gen_pool *sram_pool, *sram_pool_data;
36static unsigned long ocmcram_location, ocmcram_location_data;
37
38static struct am33xx_pm_platform_data *pm_ops;
39static struct am33xx_pm_sram_addr *pm_sram;
40
41static struct device *pm33xx_dev;
42static struct wkup_m3_ipc *m3_ipc;
43
44static u32 sram_suspend_address(unsigned long addr)
45{
46 return ((unsigned long)am33xx_do_wfi_sram +
47 AMX3_PM_SRAM_SYMBOL_OFFSET(addr));
48}
49
50#ifdef CONFIG_SUSPEND
51static int am33xx_pm_suspend(suspend_state_t suspend_state)
52{
53 int i, ret = 0;
54
55 ret = pm_ops->soc_suspend((unsigned long)suspend_state,
56 am33xx_do_wfi_sram);
57
58 if (ret) {
59 dev_err(pm33xx_dev, "PM: Kernel suspend failure\n");
60 } else {
61 i = m3_ipc->ops->request_pm_status(m3_ipc);
62
63 switch (i) {
64 case 0:
65 dev_info(pm33xx_dev,
66 "PM: Successfully put all powerdomains to target state\n");
67 break;
68 case 1:
69 dev_err(pm33xx_dev,
70 "PM: Could not transition all powerdomains to target state\n");
71 ret = -1;
72 break;
73 default:
74 dev_err(pm33xx_dev,
75 "PM: CM3 returned unknown result = %d\n", i);
76 ret = -1;
77 }
78 }
79
80 return ret;
81}
82
83static int am33xx_pm_enter(suspend_state_t suspend_state)
84{
85 int ret = 0;
86
87 switch (suspend_state) {
88 case PM_SUSPEND_MEM:
89 case PM_SUSPEND_STANDBY:
90 ret = am33xx_pm_suspend(suspend_state);
91 break;
92 default:
93 ret = -EINVAL;
94 }
95
96 return ret;
97}
98
99static int am33xx_pm_begin(suspend_state_t state)
100{
101 int ret = -EINVAL;
102
103 switch (state) {
104 case PM_SUSPEND_MEM:
105 ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_DEEPSLEEP);
106 break;
107 case PM_SUSPEND_STANDBY:
108 ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_STANDBY);
109 break;
110 }
111
112 return ret;
113}
114
115static void am33xx_pm_end(void)
116{
117 m3_ipc->ops->finish_low_power(m3_ipc);
118}
119
120static int am33xx_pm_valid(suspend_state_t state)
121{
122 switch (state) {
123 case PM_SUSPEND_STANDBY:
124 case PM_SUSPEND_MEM:
125 return 1;
126 default:
127 return 0;
128 }
129}
130
131static const struct platform_suspend_ops am33xx_pm_ops = {
132 .begin = am33xx_pm_begin,
133 .end = am33xx_pm_end,
134 .enter = am33xx_pm_enter,
135 .valid = am33xx_pm_valid,
136};
137#endif /* CONFIG_SUSPEND */
138
139static void am33xx_pm_set_ipc_ops(void)
140{
141 u32 resume_address;
142 int temp;
143
144 temp = ti_emif_get_mem_type();
145 if (temp < 0) {
146 dev_err(pm33xx_dev, "PM: Cannot determine memory type, no PM available\n");
147 return;
148 }
149 m3_ipc->ops->set_mem_type(m3_ipc, temp);
150
151 /* Physical resume address to be used by ROM code */
152 resume_address = am33xx_do_wfi_sram_phys +
153 *pm_sram->resume_offset + 0x4;
154
155 m3_ipc->ops->set_resume_address(m3_ipc, (void *)resume_address);
156}
157
158static void am33xx_pm_free_sram(void)
159{
160 gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz);
161 gen_pool_free(sram_pool_data, ocmcram_location_data,
162 sizeof(struct am33xx_pm_ro_sram_data));
163}
164
165/*
166 * Push the minimal suspend-resume code to SRAM
167 */
168static int am33xx_pm_alloc_sram(void)
169{
170 struct device_node *np;
171 int ret = 0;
172
173 np = of_find_compatible_node(NULL, NULL, "ti,omap3-mpu");
174 if (!np) {
175 np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu");
176 if (!np) {
177 dev_err(pm33xx_dev, "PM: %s: Unable to find device node for mpu\n",
178 __func__);
179 return -ENODEV;
180 }
181 }
182
183 sram_pool = of_gen_pool_get(np, "pm-sram", 0);
184 if (!sram_pool) {
185 dev_err(pm33xx_dev, "PM: %s: Unable to get sram pool for ocmcram\n",
186 __func__);
187 ret = -ENODEV;
188 goto mpu_put_node;
189 }
190
191 sram_pool_data = of_gen_pool_get(np, "pm-sram", 1);
192 if (!sram_pool_data) {
193 dev_err(pm33xx_dev, "PM: %s: Unable to get sram data pool for ocmcram\n",
194 __func__);
195 ret = -ENODEV;
196 goto mpu_put_node;
197 }
198
199 ocmcram_location = gen_pool_alloc(sram_pool, *pm_sram->do_wfi_sz);
200 if (!ocmcram_location) {
201 dev_err(pm33xx_dev, "PM: %s: Unable to allocate memory from ocmcram\n",
202 __func__);
203 ret = -ENOMEM;
204 goto mpu_put_node;
205 }
206
207 ocmcram_location_data = gen_pool_alloc(sram_pool_data,
208 sizeof(struct emif_regs_amx3));
209 if (!ocmcram_location_data) {
210 dev_err(pm33xx_dev, "PM: Unable to allocate memory from ocmcram\n");
211 gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz);
212 ret = -ENOMEM;
213 }
214
215mpu_put_node:
216 of_node_put(np);
217 return ret;
218}
219
220static int am33xx_push_sram_idle(void)
221{
222 struct am33xx_pm_ro_sram_data ro_sram_data;
223 int ret;
224 u32 table_addr, ro_data_addr;
225 void *copy_addr;
226
227 ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data;
228 ro_sram_data.amx3_pm_sram_data_phys =
229 gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data);
230
231 /* Save physical address to calculate resume offset during pm init */
232 am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool,
233 ocmcram_location);
234
235 am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location,
236 pm_sram->do_wfi,
237 *pm_sram->do_wfi_sz);
238 if (!am33xx_do_wfi_sram) {
239 dev_err(pm33xx_dev,
240 "PM: %s: am33xx_do_wfi copy to sram failed\n",
241 __func__);
242 return -ENODEV;
243 }
244
245 table_addr =
246 sram_suspend_address((unsigned long)pm_sram->emif_sram_table);
247 ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr);
248 if (ret) {
249 dev_dbg(pm33xx_dev,
250 "PM: %s: EMIF function copy failed\n", __func__);
251 return -EPROBE_DEFER;
252 }
253
254 ro_data_addr =
255 sram_suspend_address((unsigned long)pm_sram->ro_sram_data);
256 copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr,
257 &ro_sram_data,
258 sizeof(ro_sram_data));
259 if (!copy_addr) {
260 dev_err(pm33xx_dev,
261 "PM: %s: ro_sram_data copy to sram failed\n",
262 __func__);
263 return -ENODEV;
264 }
265
266 return 0;
267}
268
269static int am33xx_pm_probe(struct platform_device *pdev)
270{
271 struct device *dev = &pdev->dev;
272 int ret;
273
274 if (!of_machine_is_compatible("ti,am33xx") &&
275 !of_machine_is_compatible("ti,am43"))
276 return -ENODEV;
277
278 pm_ops = dev->platform_data;
279 if (!pm_ops) {
280 dev_err(dev, "PM: Cannot get core PM ops!\n");
281 return -ENODEV;
282 }
283
284 pm_sram = pm_ops->get_sram_addrs();
285 if (!pm_sram) {
286 dev_err(dev, "PM: Cannot get PM asm function addresses!!\n");
287 return -ENODEV;
288 }
289
290 pm33xx_dev = dev;
291
292 ret = am33xx_pm_alloc_sram();
293 if (ret)
294 return ret;
295
296 ret = am33xx_push_sram_idle();
297 if (ret)
298 goto err_free_sram;
299
300 m3_ipc = wkup_m3_ipc_get();
301 if (!m3_ipc) {
302 dev_dbg(dev, "PM: Cannot get wkup_m3_ipc handle\n");
303 ret = -EPROBE_DEFER;
304 goto err_free_sram;
305 }
306
307 am33xx_pm_set_ipc_ops();
308
309#ifdef CONFIG_SUSPEND
310 suspend_set_ops(&am33xx_pm_ops);
311#endif /* CONFIG_SUSPEND */
312
313 ret = pm_ops->init();
314 if (ret) {
315 dev_err(dev, "Unable to call core pm init!\n");
316 ret = -ENODEV;
317 goto err_put_wkup_m3_ipc;
318 }
319
320 return 0;
321
322err_put_wkup_m3_ipc:
323 wkup_m3_ipc_put(m3_ipc);
324err_free_sram:
325 am33xx_pm_free_sram();
326 pm33xx_dev = NULL;
327 return ret;
328}
329
330static int am33xx_pm_remove(struct platform_device *pdev)
331{
332 suspend_set_ops(NULL);
333 wkup_m3_ipc_put(m3_ipc);
334 am33xx_pm_free_sram();
335 return 0;
336}
337
338static struct platform_driver am33xx_pm_driver = {
339 .driver = {
340 .name = "pm33xx",
341 },
342 .probe = am33xx_pm_probe,
343 .remove = am33xx_pm_remove,
344};
345module_platform_driver(am33xx_pm_driver);
346
347MODULE_ALIAS("platform:pm33xx");
348MODULE_LICENSE("GPL v2");
349MODULE_DESCRIPTION("am33xx power management driver");
diff --git a/include/linux/platform_data/pm33xx.h b/include/linux/platform_data/pm33xx.h
new file mode 100644
index 000000000000..f9bed2a0af9d
--- /dev/null
+++ b/include/linux/platform_data/pm33xx.h
@@ -0,0 +1,42 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * TI pm33xx platform data
4 *
5 * Copyright (C) 2016-2018 Texas Instruments, Inc.
6 * Dave Gerlach <d-gerlach@ti.com>
7 */
8
9#ifndef _LINUX_PLATFORM_DATA_PM33XX_H
10#define _LINUX_PLATFORM_DATA_PM33XX_H
11
12#include <linux/kbuild.h>
13#include <linux/types.h>
14
15#ifndef __ASSEMBLER__
16struct am33xx_pm_sram_addr {
17 void (*do_wfi)(void);
18 unsigned long *do_wfi_sz;
19 unsigned long *resume_offset;
20 unsigned long *emif_sram_table;
21 unsigned long *ro_sram_data;
22};
23
24struct am33xx_pm_platform_data {
25 int (*init)(void);
26 int (*soc_suspend)(unsigned int state, int (*fn)(unsigned long));
27 struct am33xx_pm_sram_addr *(*get_sram_addrs)(void);
28};
29
30struct am33xx_pm_sram_data {
31 u32 wfi_flags;
32 u32 l2_aux_ctrl_val;
33 u32 l2_prefetch_ctrl_val;
34} __packed __aligned(8);
35
36struct am33xx_pm_ro_sram_data {
37 u32 amx3_pm_sram_data_virt;
38 u32 amx3_pm_sram_data_phys;
39} __packed __aligned(8);
40
41#endif /* __ASSEMBLER__ */
42#endif /* _LINUX_PLATFORM_DATA_PM33XX_H */