18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AM33XX Arch Power Management Routines
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/
68c2ecf20Sopenharmony_ci *	Dave Gerlach
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/cpuidle.h>
108c2ecf20Sopenharmony_ci#include <linux/platform_data/pm33xx.h>
118c2ecf20Sopenharmony_ci#include <linux/suspend.h>
128c2ecf20Sopenharmony_ci#include <asm/cpuidle.h>
138c2ecf20Sopenharmony_ci#include <asm/smp_scu.h>
148c2ecf20Sopenharmony_ci#include <asm/suspend.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/clk.h>
178c2ecf20Sopenharmony_ci#include <linux/cpu.h>
188c2ecf20Sopenharmony_ci#include <linux/platform_data/gpio-omap.h>
198c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
208c2ecf20Sopenharmony_ci#include <linux/wkup_m3_ipc.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/rtc.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "cm33xx.h"
258c2ecf20Sopenharmony_ci#include "common.h"
268c2ecf20Sopenharmony_ci#include "control.h"
278c2ecf20Sopenharmony_ci#include "clockdomain.h"
288c2ecf20Sopenharmony_ci#include "iomap.h"
298c2ecf20Sopenharmony_ci#include "pm.h"
308c2ecf20Sopenharmony_ci#include "powerdomain.h"
318c2ecf20Sopenharmony_ci#include "prm33xx.h"
328c2ecf20Sopenharmony_ci#include "soc.h"
338c2ecf20Sopenharmony_ci#include "sram.h"
348c2ecf20Sopenharmony_ci#include "omap-secure.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm;
378c2ecf20Sopenharmony_cistatic struct clockdomain *gfx_l4ls_clkdm;
388c2ecf20Sopenharmony_cistatic void __iomem *scu_base;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int (*idle_fn)(u32 wfi_flags);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct amx3_idle_state {
438c2ecf20Sopenharmony_ci	int wfi_flags;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic struct amx3_idle_state *idle_states;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int am43xx_map_scu(void)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	scu_base = ioremap(scu_a9_get_base(), SZ_256);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (!scu_base)
538c2ecf20Sopenharmony_ci		return -ENOMEM;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int am33xx_check_off_mode_enable(void)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	if (enable_off_mode)
618c2ecf20Sopenharmony_ci		pr_warn("WARNING: This platform does not support off-mode, entering DeepSleep suspend.\n");
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/* off mode not supported on am335x so return 0 always */
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int am43xx_check_off_mode_enable(void)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	/*
708c2ecf20Sopenharmony_ci	 * Check for am437x-gp-evm which has the right Hardware design to
718c2ecf20Sopenharmony_ci	 * support this mode reliably.
728c2ecf20Sopenharmony_ci	 */
738c2ecf20Sopenharmony_ci	if (of_machine_is_compatible("ti,am437x-gp-evm") && enable_off_mode)
748c2ecf20Sopenharmony_ci		return enable_off_mode;
758c2ecf20Sopenharmony_ci	else if (enable_off_mode)
768c2ecf20Sopenharmony_ci		pr_warn("WARNING: This platform does not support off-mode, entering DeepSleep suspend.\n");
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int amx3_common_init(int (*idle)(u32 wfi_flags))
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	gfx_pwrdm = pwrdm_lookup("gfx_pwrdm");
848c2ecf20Sopenharmony_ci	per_pwrdm = pwrdm_lookup("per_pwrdm");
858c2ecf20Sopenharmony_ci	mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if ((!gfx_pwrdm) || (!per_pwrdm) || (!mpu_pwrdm))
888c2ecf20Sopenharmony_ci		return -ENODEV;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	(void)clkdm_for_each(omap_pm_clkdms_setup, NULL);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* CEFUSE domain can be turned off post bootup */
938c2ecf20Sopenharmony_ci	cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm");
948c2ecf20Sopenharmony_ci	if (!cefuse_pwrdm)
958c2ecf20Sopenharmony_ci		pr_err("PM: Failed to get cefuse_pwrdm\n");
968c2ecf20Sopenharmony_ci	else if (omap_type() != OMAP2_DEVICE_TYPE_GP)
978c2ecf20Sopenharmony_ci		pr_info("PM: Leaving EFUSE power domain active\n");
988c2ecf20Sopenharmony_ci	else
998c2ecf20Sopenharmony_ci		omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	idle_fn = idle;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int am33xx_suspend_init(int (*idle)(u32 wfi_flags))
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	int ret;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm");
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (!gfx_l4ls_clkdm) {
1138c2ecf20Sopenharmony_ci		pr_err("PM: Cannot lookup gfx_l4ls_clkdm clockdomains\n");
1148c2ecf20Sopenharmony_ci		return -ENODEV;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	ret = amx3_common_init(idle);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return ret;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int am43xx_suspend_init(int (*idle)(u32 wfi_flags))
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	int ret = 0;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ret = am43xx_map_scu();
1278c2ecf20Sopenharmony_ci	if (ret) {
1288c2ecf20Sopenharmony_ci		pr_err("PM: Could not ioremap SCU\n");
1298c2ecf20Sopenharmony_ci		return ret;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ret = amx3_common_init(idle);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return ret;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int amx3_suspend_deinit(void)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	idle_fn = NULL;
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void amx3_pre_suspend_common(void)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic void amx3_post_suspend_common(void)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	int status;
1518c2ecf20Sopenharmony_ci	/*
1528c2ecf20Sopenharmony_ci	 * Because gfx_pwrdm is the only one under MPU control,
1538c2ecf20Sopenharmony_ci	 * comment on transition status
1548c2ecf20Sopenharmony_ci	 */
1558c2ecf20Sopenharmony_ci	status = pwrdm_read_pwrst(gfx_pwrdm);
1568c2ecf20Sopenharmony_ci	if (status != PWRDM_POWER_OFF)
1578c2ecf20Sopenharmony_ci		pr_err("PM: GFX domain did not transition: %x\n", status);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int am33xx_suspend(unsigned int state, int (*fn)(unsigned long),
1618c2ecf20Sopenharmony_ci			  unsigned long args)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	int ret = 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	amx3_pre_suspend_common();
1668c2ecf20Sopenharmony_ci	ret = cpu_suspend(args, fn);
1678c2ecf20Sopenharmony_ci	amx3_post_suspend_common();
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * BUG: GFX_L4LS clock domain needs to be woken up to
1718c2ecf20Sopenharmony_ci	 * ensure thet L4LS clock domain does not get stuck in
1728c2ecf20Sopenharmony_ci	 * transition. If that happens L3 module does not get
1738c2ecf20Sopenharmony_ci	 * disabled, thereby leading to PER power domain
1748c2ecf20Sopenharmony_ci	 * transition failing
1758c2ecf20Sopenharmony_ci	 */
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	clkdm_wakeup(gfx_l4ls_clkdm);
1788c2ecf20Sopenharmony_ci	clkdm_sleep(gfx_l4ls_clkdm);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return ret;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int am43xx_suspend(unsigned int state, int (*fn)(unsigned long),
1848c2ecf20Sopenharmony_ci			  unsigned long args)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	int ret = 0;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Suspend secure side on HS devices */
1898c2ecf20Sopenharmony_ci	if (omap_type() != OMAP2_DEVICE_TYPE_GP) {
1908c2ecf20Sopenharmony_ci		if (optee_available)
1918c2ecf20Sopenharmony_ci			omap_smccc_smc(AM43xx_PPA_SVC_PM_SUSPEND, 0);
1928c2ecf20Sopenharmony_ci		else
1938c2ecf20Sopenharmony_ci			omap_secure_dispatcher(AM43xx_PPA_SVC_PM_SUSPEND,
1948c2ecf20Sopenharmony_ci					       FLAG_START_CRITICAL,
1958c2ecf20Sopenharmony_ci					       0, 0, 0, 0, 0);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	amx3_pre_suspend_common();
1998c2ecf20Sopenharmony_ci	scu_power_mode(scu_base, SCU_PM_POWEROFF);
2008c2ecf20Sopenharmony_ci	ret = cpu_suspend(args, fn);
2018c2ecf20Sopenharmony_ci	scu_power_mode(scu_base, SCU_PM_NORMAL);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (!am43xx_check_off_mode_enable())
2048c2ecf20Sopenharmony_ci		amx3_post_suspend_common();
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/*
2078c2ecf20Sopenharmony_ci	 * Resume secure side on HS devices.
2088c2ecf20Sopenharmony_ci	 *
2098c2ecf20Sopenharmony_ci	 * Note that even on systems with OP-TEE available this resume call is
2108c2ecf20Sopenharmony_ci	 * issued to the ROM. This is because upon waking from suspend the ROM
2118c2ecf20Sopenharmony_ci	 * is restored as the secure monitor. On systems with OP-TEE ROM will
2128c2ecf20Sopenharmony_ci	 * restore OP-TEE during this call.
2138c2ecf20Sopenharmony_ci	 */
2148c2ecf20Sopenharmony_ci	if (omap_type() != OMAP2_DEVICE_TYPE_GP)
2158c2ecf20Sopenharmony_ci		omap_secure_dispatcher(AM43xx_PPA_SVC_PM_RESUME,
2168c2ecf20Sopenharmony_ci				       FLAG_START_CRITICAL,
2178c2ecf20Sopenharmony_ci				       0, 0, 0, 0, 0);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return ret;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int am33xx_cpu_suspend(int (*fn)(unsigned long), unsigned long args)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	int ret = 0;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (omap_irq_pending() || need_resched())
2278c2ecf20Sopenharmony_ci		return ret;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	ret = cpu_suspend(args, fn);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return ret;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int am43xx_cpu_suspend(int (*fn)(unsigned long), unsigned long args)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	int ret = 0;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (!scu_base)
2398c2ecf20Sopenharmony_ci		return 0;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	scu_power_mode(scu_base, SCU_PM_DORMANT);
2428c2ecf20Sopenharmony_ci	ret = cpu_suspend(args, fn);
2438c2ecf20Sopenharmony_ci	scu_power_mode(scu_base, SCU_PM_NORMAL);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return ret;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void amx3_begin_suspend(void)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	cpu_idle_poll_ctrl(true);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic void amx3_finish_suspend(void)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	cpu_idle_poll_ctrl(false);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic struct am33xx_pm_sram_addr *amx3_get_sram_addrs(void)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	if (soc_is_am33xx())
2628c2ecf20Sopenharmony_ci		return &am33xx_pm_sram;
2638c2ecf20Sopenharmony_ci	else if (soc_is_am437x())
2648c2ecf20Sopenharmony_ci		return &am43xx_pm_sram;
2658c2ecf20Sopenharmony_ci	else
2668c2ecf20Sopenharmony_ci		return NULL;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic void am43xx_save_context(void)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void am33xx_save_context(void)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	omap_intc_save_context();
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic void am33xx_restore_context(void)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	omap_intc_restore_context();
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic void am43xx_restore_context(void)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	/*
2868c2ecf20Sopenharmony_ci	 * HACK: restore dpll_per_clkdcoldo register contents, to avoid
2878c2ecf20Sopenharmony_ci	 * breaking suspend-resume
2888c2ecf20Sopenharmony_ci	 */
2898c2ecf20Sopenharmony_ci	writel_relaxed(0x0, AM33XX_L4_WK_IO_ADDRESS(0x44df2e14));
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic struct am33xx_pm_platform_data am33xx_ops = {
2938c2ecf20Sopenharmony_ci	.init = am33xx_suspend_init,
2948c2ecf20Sopenharmony_ci	.deinit = amx3_suspend_deinit,
2958c2ecf20Sopenharmony_ci	.soc_suspend = am33xx_suspend,
2968c2ecf20Sopenharmony_ci	.cpu_suspend = am33xx_cpu_suspend,
2978c2ecf20Sopenharmony_ci	.begin_suspend = amx3_begin_suspend,
2988c2ecf20Sopenharmony_ci	.finish_suspend = amx3_finish_suspend,
2998c2ecf20Sopenharmony_ci	.get_sram_addrs = amx3_get_sram_addrs,
3008c2ecf20Sopenharmony_ci	.save_context = am33xx_save_context,
3018c2ecf20Sopenharmony_ci	.restore_context = am33xx_restore_context,
3028c2ecf20Sopenharmony_ci	.check_off_mode_enable = am33xx_check_off_mode_enable,
3038c2ecf20Sopenharmony_ci};
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic struct am33xx_pm_platform_data am43xx_ops = {
3068c2ecf20Sopenharmony_ci	.init = am43xx_suspend_init,
3078c2ecf20Sopenharmony_ci	.deinit = amx3_suspend_deinit,
3088c2ecf20Sopenharmony_ci	.soc_suspend = am43xx_suspend,
3098c2ecf20Sopenharmony_ci	.cpu_suspend = am43xx_cpu_suspend,
3108c2ecf20Sopenharmony_ci	.begin_suspend = amx3_begin_suspend,
3118c2ecf20Sopenharmony_ci	.finish_suspend = amx3_finish_suspend,
3128c2ecf20Sopenharmony_ci	.get_sram_addrs = amx3_get_sram_addrs,
3138c2ecf20Sopenharmony_ci	.save_context = am43xx_save_context,
3148c2ecf20Sopenharmony_ci	.restore_context = am43xx_restore_context,
3158c2ecf20Sopenharmony_ci	.check_off_mode_enable = am43xx_check_off_mode_enable,
3168c2ecf20Sopenharmony_ci};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic struct am33xx_pm_platform_data *am33xx_pm_get_pdata(void)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	if (soc_is_am33xx())
3218c2ecf20Sopenharmony_ci		return &am33xx_ops;
3228c2ecf20Sopenharmony_ci	else if (soc_is_am437x())
3238c2ecf20Sopenharmony_ci		return &am43xx_ops;
3248c2ecf20Sopenharmony_ci	else
3258c2ecf20Sopenharmony_ci		return NULL;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND
3298c2ecf20Sopenharmony_ci/*
3308c2ecf20Sopenharmony_ci * Block system suspend initially. Later on pm33xx sets up it's own
3318c2ecf20Sopenharmony_ci * platform_suspend_ops after probe. That depends also on loaded
3328c2ecf20Sopenharmony_ci * wkup_m3_ipc and booted am335x-pm-firmware.elf.
3338c2ecf20Sopenharmony_ci */
3348c2ecf20Sopenharmony_cistatic int amx3_suspend_block(suspend_state_t state)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	pr_warn("PM not initialized for pm33xx, wkup_m3_ipc, or am335x-pm-firmware.elf\n");
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return -EINVAL;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int amx3_pm_valid(suspend_state_t state)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	switch (state) {
3448c2ecf20Sopenharmony_ci	case PM_SUSPEND_STANDBY:
3458c2ecf20Sopenharmony_ci		return 1;
3468c2ecf20Sopenharmony_ci	default:
3478c2ecf20Sopenharmony_ci		return 0;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops amx3_blocked_pm_ops = {
3528c2ecf20Sopenharmony_ci	.begin = amx3_suspend_block,
3538c2ecf20Sopenharmony_ci	.valid = amx3_pm_valid,
3548c2ecf20Sopenharmony_ci};
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic void __init amx3_block_suspend(void)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	suspend_set_ops(&amx3_blocked_pm_ops);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci#else
3618c2ecf20Sopenharmony_cistatic inline void amx3_block_suspend(void)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci#endif	/* CONFIG_SUSPEND */
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ciint __init amx3_common_pm_init(void)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct am33xx_pm_platform_data *pdata;
3698c2ecf20Sopenharmony_ci	struct platform_device_info devinfo;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	pdata = am33xx_pm_get_pdata();
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	memset(&devinfo, 0, sizeof(devinfo));
3748c2ecf20Sopenharmony_ci	devinfo.name = "pm33xx";
3758c2ecf20Sopenharmony_ci	devinfo.data = pdata;
3768c2ecf20Sopenharmony_ci	devinfo.size_data = sizeof(*pdata);
3778c2ecf20Sopenharmony_ci	devinfo.id = -1;
3788c2ecf20Sopenharmony_ci	platform_device_register_full(&devinfo);
3798c2ecf20Sopenharmony_ci	amx3_block_suspend();
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int __init amx3_idle_init(struct device_node *cpu_node, int cpu)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct device_node *state_node;
3878c2ecf20Sopenharmony_ci	struct amx3_idle_state states[CPUIDLE_STATE_MAX];
3888c2ecf20Sopenharmony_ci	int i;
3898c2ecf20Sopenharmony_ci	int state_count = 1;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	for (i = 0; ; i++) {
3928c2ecf20Sopenharmony_ci		state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
3938c2ecf20Sopenharmony_ci		if (!state_node)
3948c2ecf20Sopenharmony_ci			break;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		if (!of_device_is_available(state_node))
3978c2ecf20Sopenharmony_ci			continue;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		if (i == CPUIDLE_STATE_MAX) {
4008c2ecf20Sopenharmony_ci			pr_warn("%s: cpuidle states reached max possible\n",
4018c2ecf20Sopenharmony_ci				__func__);
4028c2ecf20Sopenharmony_ci			break;
4038c2ecf20Sopenharmony_ci		}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		states[state_count].wfi_flags = 0;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		if (of_property_read_bool(state_node, "ti,idle-wkup-m3"))
4088c2ecf20Sopenharmony_ci			states[state_count].wfi_flags |= WFI_FLAG_WAKE_M3 |
4098c2ecf20Sopenharmony_ci							 WFI_FLAG_FLUSH_CACHE;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		state_count++;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	idle_states = kcalloc(state_count, sizeof(*idle_states), GFP_KERNEL);
4158c2ecf20Sopenharmony_ci	if (!idle_states)
4168c2ecf20Sopenharmony_ci		return -ENOMEM;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	for (i = 1; i < state_count; i++)
4198c2ecf20Sopenharmony_ci		idle_states[i].wfi_flags = states[i].wfi_flags;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic int amx3_idle_enter(unsigned long index)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct amx3_idle_state *idle_state = &idle_states[index];
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (!idle_state)
4298c2ecf20Sopenharmony_ci		return -EINVAL;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (idle_fn)
4328c2ecf20Sopenharmony_ci		idle_fn(idle_state->wfi_flags);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return 0;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic struct cpuidle_ops amx3_cpuidle_ops __initdata = {
4388c2ecf20Sopenharmony_ci	.init = amx3_idle_init,
4398c2ecf20Sopenharmony_ci	.suspend = amx3_idle_enter,
4408c2ecf20Sopenharmony_ci};
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ciCPUIDLE_METHOD_OF_DECLARE(pm33xx_idle, "ti,am3352", &amx3_cpuidle_ops);
4438c2ecf20Sopenharmony_ciCPUIDLE_METHOD_OF_DECLARE(pm43xx_idle, "ti,am4372", &amx3_cpuidle_ops);
444