| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * pm.c - Common OMAP2+ power management-related code | |
| * | |
| * Copyright (C) 2010 Texas Instruments, Inc. | |
| * Copyright (C) 2010 Nokia Corporation | |
| */ | |
| #include <linux/kernel.h> | |
| #include <linux/init.h> | |
| #include <linux/io.h> | |
| #include <linux/err.h> | |
| #include <linux/pm_opp.h> | |
| #include <linux/export.h> | |
| #include <linux/suspend.h> | |
| #include <linux/clk.h> | |
| #include <linux/cpu.h> | |
| #include <asm/system_misc.h> | |
| #include "omap_device.h" | |
| #include "common.h" | |
| #include "soc.h" | |
| #include "prcm-common.h" | |
| #include "voltage.h" | |
| #include "powerdomain.h" | |
| #include "clockdomain.h" | |
| #include "pm.h" | |
| u32 enable_off_mode; | |
| #ifdef CONFIG_SUSPEND | |
| /* | |
| * omap_pm_suspend: points to a function that does the SoC-specific | |
| * suspend work | |
| */ | |
| static int (*omap_pm_suspend)(void); | |
| #endif | |
| #ifdef CONFIG_PM | |
| /** | |
| * struct omap2_oscillator - Describe the board main oscillator latencies | |
| * @startup_time: oscillator startup latency | |
| * @shutdown_time: oscillator shutdown latency | |
| */ | |
| struct omap2_oscillator { | |
| u32 startup_time; | |
| u32 shutdown_time; | |
| }; | |
| static struct omap2_oscillator oscillator = { | |
| .startup_time = ULONG_MAX, | |
| .shutdown_time = ULONG_MAX, | |
| }; | |
| void omap_pm_setup_oscillator(u32 tstart, u32 tshut) | |
| { | |
| oscillator.startup_time = tstart; | |
| oscillator.shutdown_time = tshut; | |
| } | |
| void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) | |
| { | |
| if (!tstart || !tshut) | |
| return; | |
| *tstart = oscillator.startup_time; | |
| *tshut = oscillator.shutdown_time; | |
| } | |
| #endif | |
| int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) | |
| { | |
| clkdm_allow_idle(clkdm); | |
| return 0; | |
| } | |
| #ifdef CONFIG_SUSPEND | |
| static int omap_pm_enter(suspend_state_t suspend_state) | |
| { | |
| int ret = 0; | |
| if (!omap_pm_suspend) | |
| return -ENOENT; /* XXX doublecheck */ | |
| switch (suspend_state) { | |
| case PM_SUSPEND_MEM: | |
| ret = omap_pm_suspend(); | |
| break; | |
| default: | |
| ret = -EINVAL; | |
| } | |
| return ret; | |
| } | |
| static int omap_pm_begin(suspend_state_t state) | |
| { | |
| cpu_idle_poll_ctrl(true); | |
| if (soc_is_omap34xx()) | |
| omap_prcm_irq_prepare(); | |
| return 0; | |
| } | |
| static void omap_pm_end(void) | |
| { | |
| cpu_idle_poll_ctrl(false); | |
| } | |
| static void omap_pm_wake(void) | |
| { | |
| if (soc_is_omap34xx()) | |
| omap_prcm_irq_complete(); | |
| } | |
| static const struct platform_suspend_ops omap_pm_ops = { | |
| .begin = omap_pm_begin, | |
| .end = omap_pm_end, | |
| .enter = omap_pm_enter, | |
| .wake = omap_pm_wake, | |
| .valid = suspend_valid_only_mem, | |
| }; | |
| /** | |
| * omap_common_suspend_init - Set common suspend routines for OMAP SoCs | |
| * @pm_suspend: function pointer to SoC specific suspend function | |
| */ | |
| void omap_common_suspend_init(void *pm_suspend) | |
| { | |
| omap_pm_suspend = pm_suspend; | |
| suspend_set_ops(&omap_pm_ops); | |
| } | |
| #endif /* CONFIG_SUSPEND */ | |
| int __maybe_unused omap_pm_nop_init(void) | |
| { | |
| return 0; | |
| } | |
| int (*omap_pm_soc_init)(void); | |
| int __init omap2_common_pm_late_init(void) | |
| { | |
| int error; | |
| if (!omap_pm_soc_init) | |
| return 0; | |
| /* Init the voltage layer */ | |
| omap3_twl_init(); | |
| omap4_twl_init(); | |
| omap4_cpcap_init(); | |
| omap_voltage_late_init(); | |
| /* Smartreflex device init */ | |
| omap_devinit_smartreflex(); | |
| error = omap_pm_soc_init(); | |
| if (error) | |
| pr_warn("%s: pm soc init failed: %i\n", __func__, error); | |
| omap2_clk_enable_autoidle_all(); | |
| return 0; | |
| } | |
| omap_late_initcall(omap2_common_pm_late_init); |