diff options
author | Arnd Bergmann <arnd@arndb.de> | 2018-03-07 10:21:59 -0500 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2018-03-07 10:21:59 -0500 |
commit | 18b4788badeae8ed3a9bd3c240d83dffe5db8f37 (patch) | |
tree | 664dacce5739cfe87b791994c37ada5bb99b2fa2 | |
parent | 6f566c4f304de8a90d520f7ca1925b4ea7dc3581 (diff) | |
parent | afe761f8d3e99155b76833421e76553a3ac69577 (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.txt | 16 | ||||
-rw-r--r-- | arch/arm/mach-omap2/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 16 | ||||
-rw-r--r-- | arch/arm/mach-omap2/common.h | 7 | ||||
-rw-r--r-- | arch/arm/mach-omap2/io.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm-asm-offsets.c | 31 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm33xx-core.c | 189 | ||||
-rw-r--r-- | arch/arm/mach-omap2/sleep33xx.S | 214 | ||||
-rw-r--r-- | arch/arm/mach-omap2/sleep43xx.S | 387 | ||||
-rw-r--r-- | drivers/soc/ti/Kconfig | 9 | ||||
-rw-r--r-- | drivers/soc/ti/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/ti/pm33xx.c | 349 | ||||
-rw-r--r-- | include/linux/platform_data/pm33xx.h | 42 |
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: | |||
13 | Optional properties: | 13 | Optional properties: |
14 | - sram: Phandle to the ocmcram node | 14 | - sram: Phandle to the ocmcram node |
15 | 15 | ||
16 | am335x 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 | |||
16 | Examples: | 23 | Examples: |
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 | |||
49 | mpu { | ||
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 | ||
76 | config SOC_DRA7XX | 77 | config 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 | |||
88 | obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common) | 88 | obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common) |
89 | obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common) | 89 | obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common) |
90 | obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-pm-common) | 90 | obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-pm-common) |
91 | obj-$(CONFIG_SOC_AM33XX) += pm33xx-core.o sleep33xx.o | ||
92 | obj-$(CONFIG_SOC_AM43XX) += pm33xx-core.o sleep43xx.o | ||
91 | obj-$(CONFIG_PM_DEBUG) += pm-debug.o | 93 | obj-$(CONFIG_PM_DEBUG) += pm-debug.o |
92 | 94 | ||
93 | obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o | 95 | obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o |
@@ -95,6 +97,8 @@ obj-$(CONFIG_POWER_AVS_OMAP_CLASS3) += smartreflex-class3.o | |||
95 | 97 | ||
96 | AFLAGS_sleep24xx.o :=-Wa,-march=armv6 | 98 | AFLAGS_sleep24xx.o :=-Wa,-march=armv6 |
97 | AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec) | 99 | AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec) |
100 | AFLAGS_sleep33xx.o :=-Wa,-march=armv7-a$(plus_sec) | ||
101 | AFLAGS_sleep43xx.o :=-Wa,-march=armv7-a$(plus_sec) | ||
98 | 102 | ||
99 | endif | 103 | endif |
100 | 104 | ||
@@ -232,3 +236,15 @@ obj-y += $(omap-hsmmc-m) $(omap-hsmmc-y) | |||
232 | obj-y += omap_phy_internal.o | 236 | obj-y += omap_phy_internal.o |
233 | 237 | ||
234 | obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o | 238 | obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o |
239 | |||
240 | arch/arm/mach-omap2/pm-asm-offsets.s: arch/arm/mach-omap2/pm-asm-offsets.c | ||
241 | $(call if_changed_dep,cc_s_c) | ||
242 | |||
243 | include/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 | ||
247 | include drivers/memory/Makefile.asm-offsets | ||
248 | |||
249 | arch/arm/mach-omap2/sleep33xx.o: include/generated/ti-pm-asm-offsets.h include/generated/ti-emif-asm-offsets.h | ||
250 | arch/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)) | ||
82 | void amx3_common_pm_init(void); | ||
83 | #else | ||
84 | static inline void amx3_common_pm_init(void) { } | ||
85 | #endif | ||
86 | |||
80 | extern void omap2_init_common_infrastructure(void); | 87 | extern void omap2_init_common_infrastructure(void); |
81 | 88 | ||
82 | extern void omap_init_time(void); | 89 | extern 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) | |||
622 | void __init am33xx_init_late(void) | 622 | void __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 | |||
11 | int 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 */ |
82 | extern void (*omap3_do_wfi_sram)(void); | 82 | extern void (*omap3_do_wfi_sram)(void); |
83 | 83 | ||
84 | extern struct am33xx_pm_sram_addr am33xx_pm_sram; | ||
85 | extern struct am33xx_pm_sram_addr am43xx_pm_sram; | ||
86 | |||
84 | extern void omap3_save_scratchpad_contents(void); | 87 | extern 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 | |||
26 | static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm; | ||
27 | static struct clockdomain *gfx_l4ls_clkdm; | ||
28 | static void __iomem *scu_base; | ||
29 | |||
30 | static 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 | |||
40 | static 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 | |||
61 | static 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 | |||
77 | static 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 | |||
92 | static void amx3_pre_suspend_common(void) | ||
93 | { | ||
94 | omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF); | ||
95 | } | ||
96 | |||
97 | static 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 | |||
109 | static 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 | |||
131 | static 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 | |||
144 | static 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 | |||
154 | static 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 | |||
160 | static 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 | |||
166 | static 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 | |||
176 | void __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 | |||
26 | ENTRY(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 | ||
67 | wait_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] | ||
131 | wait_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 | ||
151 | ENDPROC(am33xx_do_wfi) | ||
152 | |||
153 | .align | ||
154 | ENTRY(am33xx_resume_offset) | ||
155 | .word . - am33xx_do_wfi | ||
156 | |||
157 | ENTRY(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] | ||
162 | wait_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 | |||
175 | resume_to_ddr: | ||
176 | /* We are back. Branch to the common CPU resume routine */ | ||
177 | mov r0, #0 | ||
178 | ldr pc, resume_addr | ||
179 | ENDPROC(am33xx_resume_from_deep_sleep) | ||
180 | |||
181 | /* | ||
182 | * Local variables | ||
183 | */ | ||
184 | .align | ||
185 | resume_addr: | ||
186 | .word cpu_resume - PAGE_OFFSET + 0x80000000 | ||
187 | kernel_flush: | ||
188 | .word v7_flush_dcache_all | ||
189 | virt_mpu_clkctrl: | ||
190 | .word AM33XX_CM_MPU_MPU_CLKCTRL | ||
191 | virt_emif_clkctrl: | ||
192 | .word AM33XX_CM_PER_EMIF_CLKCTRL | ||
193 | phys_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 */ | ||
199 | am33xx_emif_sram_table: | ||
200 | .space EMIF_PM_FUNCTIONS_SIZE | ||
201 | |||
202 | ENTRY(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 | ||
210 | ENTRY(am33xx_pm_ro_sram_data) | ||
211 | .space AMX3_PM_RO_SRAM_DATA_SIZE | ||
212 | |||
213 | ENTRY(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 | |||
52 | ENTRY(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] | ||
107 | wait: | ||
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 | ||
119 | l2x_sync: | ||
120 | mov r0, r8 | ||
121 | mov r2, r0 | ||
122 | mov r0, #0x0 | ||
123 | str r0, [r2, #L2X0_CACHE_SYNC] | ||
124 | sync: | ||
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 | |||
144 | wait_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] | ||
215 | wait_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 | ||
234 | ENDPROC(am43xx_do_wfi) | ||
235 | |||
236 | .align | ||
237 | ENTRY(am43xx_resume_offset) | ||
238 | .word . - am43xx_do_wfi | ||
239 | |||
240 | ENTRY(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] | ||
255 | wait_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 | ||
287 | set_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] | ||
298 | wait2: | ||
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 | ||
310 | l2x_sync2: | ||
311 | ldr r2, l2_cache_base | ||
312 | mov r0, #0x0 | ||
313 | str r0, [r2, #L2X0_CACHE_SYNC] | ||
314 | sync2: | ||
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 | ||
325 | skip_l2en: | ||
326 | /* We are back. Branch to the common CPU resume routine */ | ||
327 | mov r0, #0 | ||
328 | ldr pc, resume_addr | ||
329 | ENDPROC(am43xx_resume_from_deep_sleep) | ||
330 | |||
331 | /* | ||
332 | * Local variables | ||
333 | */ | ||
334 | .align | ||
335 | resume_addr: | ||
336 | .word cpu_resume - PAGE_OFFSET + 0x80000000 | ||
337 | get_l2cache_base: | ||
338 | .word omap4_get_l2cache_base | ||
339 | kernel_flush: | ||
340 | .word v7_flush_dcache_all | ||
341 | ddr_start: | ||
342 | .word PAGE_OFFSET | ||
343 | |||
344 | am43xx_phys_emif_poweroff: | ||
345 | .word (AM43XX_CM_BASE + AM43XX_PRM_DEVICE_INST + \ | ||
346 | AM43XX_PRM_EMIF_CTRL_OFFSET) | ||
347 | am43xx_virt_mpu_clkstctrl: | ||
348 | .word (AM43XX_CM_MPU_CLKSTCTRL) | ||
349 | am43xx_virt_mpu_clkctrl: | ||
350 | .word (AM43XX_CM_MPU_MPU_CLKCTRL) | ||
351 | am43xx_virt_emif_clkctrl: | ||
352 | .word (AM43XX_CM_PER_EMIF_CLKCTRL) | ||
353 | am43xx_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 */ | ||
358 | l2_cache_base: | ||
359 | .word OMAP44XX_L2CACHE_BASE | ||
360 | l2_smc1: | ||
361 | .word OMAP4_MON_L2X0_PREFETCH_INDEX | ||
362 | l2_smc2: | ||
363 | .word OMAP4_MON_L2X0_AUXCTRL_INDEX | ||
364 | l2_smc3: | ||
365 | .word OMAP4_MON_L2X0_CTRL_INDEX | ||
366 | l2_val: | ||
367 | .word 0xffff | ||
368 | |||
369 | .align 3 | ||
370 | /* DDR related defines */ | ||
371 | ENTRY(am43xx_emif_sram_table) | ||
372 | .space EMIF_PM_FUNCTIONS_SIZE | ||
373 | |||
374 | ENTRY(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 | |||
383 | ENTRY(am43xx_pm_ro_sram_data) | ||
384 | .space AMX3_PM_RO_SRAM_DATA_SIZE | ||
385 | |||
386 | ENTRY(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 | ||
31 | config 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 | |||
31 | config WKUP_M3_IPC | 40 | config 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 @@ | |||
5 | obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o | 5 | obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o |
6 | knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o | 6 | knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o |
7 | obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o | 7 | obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o |
8 | obj-$(CONFIG_AMX3_PM) += pm33xx.o | ||
8 | obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o | 9 | obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o |
9 | obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o | 10 | obj-$(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 | |||
32 | static int (*am33xx_do_wfi_sram)(unsigned long unused); | ||
33 | static phys_addr_t am33xx_do_wfi_sram_phys; | ||
34 | |||
35 | static struct gen_pool *sram_pool, *sram_pool_data; | ||
36 | static unsigned long ocmcram_location, ocmcram_location_data; | ||
37 | |||
38 | static struct am33xx_pm_platform_data *pm_ops; | ||
39 | static struct am33xx_pm_sram_addr *pm_sram; | ||
40 | |||
41 | static struct device *pm33xx_dev; | ||
42 | static struct wkup_m3_ipc *m3_ipc; | ||
43 | |||
44 | static 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 | ||
51 | static 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 | |||
83 | static 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 | |||
99 | static 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 | |||
115 | static void am33xx_pm_end(void) | ||
116 | { | ||
117 | m3_ipc->ops->finish_low_power(m3_ipc); | ||
118 | } | ||
119 | |||
120 | static 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 | |||
131 | static 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 | |||
139 | static 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 | |||
158 | static 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 | */ | ||
168 | static 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 | |||
215 | mpu_put_node: | ||
216 | of_node_put(np); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | static 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 | |||
269 | static 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 | |||
322 | err_put_wkup_m3_ipc: | ||
323 | wkup_m3_ipc_put(m3_ipc); | ||
324 | err_free_sram: | ||
325 | am33xx_pm_free_sram(); | ||
326 | pm33xx_dev = NULL; | ||
327 | return ret; | ||
328 | } | ||
329 | |||
330 | static 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 | |||
338 | static struct platform_driver am33xx_pm_driver = { | ||
339 | .driver = { | ||
340 | .name = "pm33xx", | ||
341 | }, | ||
342 | .probe = am33xx_pm_probe, | ||
343 | .remove = am33xx_pm_remove, | ||
344 | }; | ||
345 | module_platform_driver(am33xx_pm_driver); | ||
346 | |||
347 | MODULE_ALIAS("platform:pm33xx"); | ||
348 | MODULE_LICENSE("GPL v2"); | ||
349 | MODULE_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__ | ||
16 | struct 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 | |||
24 | struct 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 | |||
30 | struct 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 | |||
36 | struct 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 */ | ||