162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pm.c - Common OMAP2+ power management-related code 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Texas Instruments, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/pm_opp.h> 1462306a36Sopenharmony_ci#include <linux/export.h> 1562306a36Sopenharmony_ci#include <linux/suspend.h> 1662306a36Sopenharmony_ci#include <linux/clk.h> 1762306a36Sopenharmony_ci#include <linux/cpu.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/system_misc.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "omap_device.h" 2262306a36Sopenharmony_ci#include "common.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "soc.h" 2562306a36Sopenharmony_ci#include "prcm-common.h" 2662306a36Sopenharmony_ci#include "voltage.h" 2762306a36Sopenharmony_ci#include "powerdomain.h" 2862306a36Sopenharmony_ci#include "clockdomain.h" 2962306a36Sopenharmony_ci#include "pm.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciu32 enable_off_mode; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * omap_pm_suspend: points to a function that does the SoC-specific 3662306a36Sopenharmony_ci * suspend work 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic int (*omap_pm_suspend)(void); 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#ifdef CONFIG_PM 4262306a36Sopenharmony_ci/** 4362306a36Sopenharmony_ci * struct omap2_oscillator - Describe the board main oscillator latencies 4462306a36Sopenharmony_ci * @startup_time: oscillator startup latency 4562306a36Sopenharmony_ci * @shutdown_time: oscillator shutdown latency 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistruct omap2_oscillator { 4862306a36Sopenharmony_ci u32 startup_time; 4962306a36Sopenharmony_ci u32 shutdown_time; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct omap2_oscillator oscillator = { 5362306a36Sopenharmony_ci .startup_time = ULONG_MAX, 5462306a36Sopenharmony_ci .shutdown_time = ULONG_MAX, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_civoid omap_pm_get_oscillator(u32 *tstart, u32 *tshut) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci if (!tstart || !tshut) 6062306a36Sopenharmony_ci return; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci *tstart = oscillator.startup_time; 6362306a36Sopenharmony_ci *tshut = oscillator.shutdown_time; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ciint omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci clkdm_allow_idle(clkdm); 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 7462306a36Sopenharmony_cistatic int omap_pm_enter(suspend_state_t suspend_state) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int ret = 0; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!omap_pm_suspend) 7962306a36Sopenharmony_ci return -ENOENT; /* XXX doublecheck */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci switch (suspend_state) { 8262306a36Sopenharmony_ci case PM_SUSPEND_MEM: 8362306a36Sopenharmony_ci ret = omap_pm_suspend(); 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci default: 8662306a36Sopenharmony_ci ret = -EINVAL; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int omap_pm_begin(suspend_state_t state) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci cpu_idle_poll_ctrl(true); 9562306a36Sopenharmony_ci if (soc_is_omap34xx()) 9662306a36Sopenharmony_ci omap_prcm_irq_prepare(); 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void omap_pm_end(void) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci cpu_idle_poll_ctrl(false); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void omap_pm_wake(void) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci if (soc_is_omap34xx()) 10862306a36Sopenharmony_ci omap_prcm_irq_complete(); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const struct platform_suspend_ops omap_pm_ops = { 11262306a36Sopenharmony_ci .begin = omap_pm_begin, 11362306a36Sopenharmony_ci .end = omap_pm_end, 11462306a36Sopenharmony_ci .enter = omap_pm_enter, 11562306a36Sopenharmony_ci .wake = omap_pm_wake, 11662306a36Sopenharmony_ci .valid = suspend_valid_only_mem, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * omap_common_suspend_init - Set common suspend routines for OMAP SoCs 12162306a36Sopenharmony_ci * @pm_suspend: function pointer to SoC specific suspend function 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_civoid omap_common_suspend_init(void *pm_suspend) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci omap_pm_suspend = pm_suspend; 12662306a36Sopenharmony_ci suspend_set_ops(&omap_pm_ops); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciint __maybe_unused omap_pm_nop_init(void) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciint (*omap_pm_soc_init)(void); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int __init omap2_common_pm_late_init(void) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int error; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (!omap_pm_soc_init) 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Init the voltage layer */ 14562306a36Sopenharmony_ci omap3_twl_init(); 14662306a36Sopenharmony_ci omap4_twl_init(); 14762306a36Sopenharmony_ci omap4_cpcap_init(); 14862306a36Sopenharmony_ci omap_voltage_late_init(); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Smartreflex device init */ 15162306a36Sopenharmony_ci omap_devinit_smartreflex(); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci error = omap_pm_soc_init(); 15462306a36Sopenharmony_ci if (error) 15562306a36Sopenharmony_ci pr_warn("%s: pm soc init failed: %i\n", __func__, error); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci omap2_clk_enable_autoidle_all(); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ciomap_late_initcall(omap2_common_pm_late_init); 162