18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AM33XX Power Management Routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/ 68c2ecf20Sopenharmony_ci * Vaibhav Bedia, Dave Gerlach 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/cpu.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_address.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_data/pm33xx.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/rtc.h> 248c2ecf20Sopenharmony_ci#include <linux/rtc/rtc-omap.h> 258c2ecf20Sopenharmony_ci#include <linux/sizes.h> 268c2ecf20Sopenharmony_ci#include <linux/sram.h> 278c2ecf20Sopenharmony_ci#include <linux/suspend.h> 288c2ecf20Sopenharmony_ci#include <linux/ti-emif-sram.h> 298c2ecf20Sopenharmony_ci#include <linux/wkup_m3_ipc.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <asm/proc-fns.h> 328c2ecf20Sopenharmony_ci#include <asm/suspend.h> 338c2ecf20Sopenharmony_ci#include <asm/system_misc.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \ 368c2ecf20Sopenharmony_ci (unsigned long)pm_sram->do_wfi) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define RTC_SCRATCH_RESUME_REG 0 398c2ecf20Sopenharmony_ci#define RTC_SCRATCH_MAGIC_REG 1 408c2ecf20Sopenharmony_ci#define RTC_REG_BOOT_MAGIC 0x8cd0 /* RTC */ 418c2ecf20Sopenharmony_ci#define GIC_INT_SET_PENDING_BASE 0x200 428c2ecf20Sopenharmony_ci#define AM43XX_GIC_DIST_BASE 0x48241000 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void __iomem *rtc_base_virt; 458c2ecf20Sopenharmony_cistatic struct clk *rtc_fck; 468c2ecf20Sopenharmony_cistatic u32 rtc_magic_val; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int (*am33xx_do_wfi_sram)(unsigned long unused); 498c2ecf20Sopenharmony_cistatic phys_addr_t am33xx_do_wfi_sram_phys; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic struct gen_pool *sram_pool, *sram_pool_data; 528c2ecf20Sopenharmony_cistatic unsigned long ocmcram_location, ocmcram_location_data; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic struct rtc_device *omap_rtc; 558c2ecf20Sopenharmony_cistatic void __iomem *gic_dist_base; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic struct am33xx_pm_platform_data *pm_ops; 588c2ecf20Sopenharmony_cistatic struct am33xx_pm_sram_addr *pm_sram; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic struct device *pm33xx_dev; 618c2ecf20Sopenharmony_cistatic struct wkup_m3_ipc *m3_ipc; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 648c2ecf20Sopenharmony_cistatic int rtc_only_idle; 658c2ecf20Sopenharmony_cistatic int retrigger_irq; 668c2ecf20Sopenharmony_cistatic unsigned long suspend_wfi_flags; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic struct wkup_m3_wakeup_src wakeup_src = {.irq_nr = 0, 698c2ecf20Sopenharmony_ci .src = "Unknown", 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct wkup_m3_wakeup_src rtc_alarm_wakeup = { 738c2ecf20Sopenharmony_ci .irq_nr = 108, .src = "RTC Alarm", 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct wkup_m3_wakeup_src rtc_ext_wakeup = { 778c2ecf20Sopenharmony_ci .irq_nr = 0, .src = "Ext wakeup", 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci#endif 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic u32 sram_suspend_address(unsigned long addr) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci return ((unsigned long)am33xx_do_wfi_sram + 848c2ecf20Sopenharmony_ci AMX3_PM_SRAM_SYMBOL_OFFSET(addr)); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int am33xx_push_sram_idle(void) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct am33xx_pm_ro_sram_data ro_sram_data; 908c2ecf20Sopenharmony_ci int ret; 918c2ecf20Sopenharmony_ci u32 table_addr, ro_data_addr; 928c2ecf20Sopenharmony_ci void *copy_addr; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data; 958c2ecf20Sopenharmony_ci ro_sram_data.amx3_pm_sram_data_phys = 968c2ecf20Sopenharmony_ci gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data); 978c2ecf20Sopenharmony_ci ro_sram_data.rtc_base_virt = rtc_base_virt; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Save physical address to calculate resume offset during pm init */ 1008c2ecf20Sopenharmony_ci am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool, 1018c2ecf20Sopenharmony_ci ocmcram_location); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location, 1048c2ecf20Sopenharmony_ci pm_sram->do_wfi, 1058c2ecf20Sopenharmony_ci *pm_sram->do_wfi_sz); 1068c2ecf20Sopenharmony_ci if (!am33xx_do_wfi_sram) { 1078c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, 1088c2ecf20Sopenharmony_ci "PM: %s: am33xx_do_wfi copy to sram failed\n", 1098c2ecf20Sopenharmony_ci __func__); 1108c2ecf20Sopenharmony_ci return -ENODEV; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci table_addr = 1148c2ecf20Sopenharmony_ci sram_suspend_address((unsigned long)pm_sram->emif_sram_table); 1158c2ecf20Sopenharmony_ci ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr); 1168c2ecf20Sopenharmony_ci if (ret) { 1178c2ecf20Sopenharmony_ci dev_dbg(pm33xx_dev, 1188c2ecf20Sopenharmony_ci "PM: %s: EMIF function copy failed\n", __func__); 1198c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ro_data_addr = 1238c2ecf20Sopenharmony_ci sram_suspend_address((unsigned long)pm_sram->ro_sram_data); 1248c2ecf20Sopenharmony_ci copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr, 1258c2ecf20Sopenharmony_ci &ro_sram_data, 1268c2ecf20Sopenharmony_ci sizeof(ro_sram_data)); 1278c2ecf20Sopenharmony_ci if (!copy_addr) { 1288c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, 1298c2ecf20Sopenharmony_ci "PM: %s: ro_sram_data copy to sram failed\n", 1308c2ecf20Sopenharmony_ci __func__); 1318c2ecf20Sopenharmony_ci return -ENODEV; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int am33xx_do_sram_idle(u32 wfi_flags) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci int ret = 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (!m3_ipc || !pm_ops) 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (wfi_flags & WFI_FLAG_WAKE_M3) 1458c2ecf20Sopenharmony_ci ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_IDLE); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return pm_ops->cpu_suspend(am33xx_do_wfi_sram, wfi_flags); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int __init am43xx_map_gic(void) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci gic_dist_base = ioremap(AM43XX_GIC_DIST_BASE, SZ_4K); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (!gic_dist_base) 1558c2ecf20Sopenharmony_ci return -ENOMEM; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 1618c2ecf20Sopenharmony_cistatic struct wkup_m3_wakeup_src rtc_wake_src(void) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci u32 i; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci i = __raw_readl(rtc_base_virt + 0x44) & 0x40; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (i) { 1688c2ecf20Sopenharmony_ci retrigger_irq = rtc_alarm_wakeup.irq_nr; 1698c2ecf20Sopenharmony_ci return rtc_alarm_wakeup; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci retrigger_irq = rtc_ext_wakeup.irq_nr; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return rtc_ext_wakeup; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int am33xx_rtc_only_idle(unsigned long wfi_flags) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci omap_rtc_power_off_program(&omap_rtc->dev); 1808c2ecf20Sopenharmony_ci am33xx_do_wfi_sram(wfi_flags); 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * Note that the RTC module clock must be re-enabled only for rtc+ddr suspend. 1868c2ecf20Sopenharmony_ci * And looks like the module can stay in SYSC_IDLE_SMART_WKUP mode configured 1878c2ecf20Sopenharmony_ci * by the interconnect code just fine for both rtc+ddr suspend and retention 1888c2ecf20Sopenharmony_ci * suspend. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic int am33xx_pm_suspend(suspend_state_t suspend_state) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci int i, ret = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (suspend_state == PM_SUSPEND_MEM && 1958c2ecf20Sopenharmony_ci pm_ops->check_off_mode_enable()) { 1968c2ecf20Sopenharmony_ci ret = clk_prepare_enable(rtc_fck); 1978c2ecf20Sopenharmony_ci if (ret) { 1988c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, "Failed to enable clock: %i\n", ret); 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci pm_ops->save_context(); 2038c2ecf20Sopenharmony_ci suspend_wfi_flags |= WFI_FLAG_RTC_ONLY; 2048c2ecf20Sopenharmony_ci clk_save_context(); 2058c2ecf20Sopenharmony_ci ret = pm_ops->soc_suspend(suspend_state, am33xx_rtc_only_idle, 2068c2ecf20Sopenharmony_ci suspend_wfi_flags); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci suspend_wfi_flags &= ~WFI_FLAG_RTC_ONLY; 2098c2ecf20Sopenharmony_ci dev_info(pm33xx_dev, "Entering RTC Only mode with DDR in self-refresh\n"); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (!ret) { 2128c2ecf20Sopenharmony_ci clk_restore_context(); 2138c2ecf20Sopenharmony_ci pm_ops->restore_context(); 2148c2ecf20Sopenharmony_ci m3_ipc->ops->set_rtc_only(m3_ipc); 2158c2ecf20Sopenharmony_ci am33xx_push_sram_idle(); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci } else { 2188c2ecf20Sopenharmony_ci ret = pm_ops->soc_suspend(suspend_state, am33xx_do_wfi_sram, 2198c2ecf20Sopenharmony_ci suspend_wfi_flags); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (ret) { 2238c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, "PM: Kernel suspend failure\n"); 2248c2ecf20Sopenharmony_ci } else { 2258c2ecf20Sopenharmony_ci i = m3_ipc->ops->request_pm_status(m3_ipc); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci switch (i) { 2288c2ecf20Sopenharmony_ci case 0: 2298c2ecf20Sopenharmony_ci dev_info(pm33xx_dev, 2308c2ecf20Sopenharmony_ci "PM: Successfully put all powerdomains to target state\n"); 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case 1: 2338c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, 2348c2ecf20Sopenharmony_ci "PM: Could not transition all powerdomains to target state\n"); 2358c2ecf20Sopenharmony_ci ret = -1; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci default: 2388c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, 2398c2ecf20Sopenharmony_ci "PM: CM3 returned unknown result = %d\n", i); 2408c2ecf20Sopenharmony_ci ret = -1; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* print the wakeup reason */ 2448c2ecf20Sopenharmony_ci if (rtc_only_idle) { 2458c2ecf20Sopenharmony_ci wakeup_src = rtc_wake_src(); 2468c2ecf20Sopenharmony_ci pr_info("PM: Wakeup source %s\n", wakeup_src.src); 2478c2ecf20Sopenharmony_ci } else { 2488c2ecf20Sopenharmony_ci pr_info("PM: Wakeup source %s\n", 2498c2ecf20Sopenharmony_ci m3_ipc->ops->request_wake_src(m3_ipc)); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (suspend_state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) 2548c2ecf20Sopenharmony_ci clk_disable_unprepare(rtc_fck); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int am33xx_pm_enter(suspend_state_t suspend_state) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci int ret = 0; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci switch (suspend_state) { 2648c2ecf20Sopenharmony_ci case PM_SUSPEND_MEM: 2658c2ecf20Sopenharmony_ci case PM_SUSPEND_STANDBY: 2668c2ecf20Sopenharmony_ci ret = am33xx_pm_suspend(suspend_state); 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci default: 2698c2ecf20Sopenharmony_ci ret = -EINVAL; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return ret; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int am33xx_pm_begin(suspend_state_t state) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci int ret = -EINVAL; 2788c2ecf20Sopenharmony_ci struct nvmem_device *nvmem; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) { 2818c2ecf20Sopenharmony_ci nvmem = devm_nvmem_device_get(&omap_rtc->dev, 2828c2ecf20Sopenharmony_ci "omap_rtc_scratch0"); 2838c2ecf20Sopenharmony_ci if (!IS_ERR(nvmem)) 2848c2ecf20Sopenharmony_ci nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, 2858c2ecf20Sopenharmony_ci (void *)&rtc_magic_val); 2868c2ecf20Sopenharmony_ci rtc_only_idle = 1; 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci rtc_only_idle = 0; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci pm_ops->begin_suspend(); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci switch (state) { 2948c2ecf20Sopenharmony_ci case PM_SUSPEND_MEM: 2958c2ecf20Sopenharmony_ci ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_DEEPSLEEP); 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case PM_SUSPEND_STANDBY: 2988c2ecf20Sopenharmony_ci ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_STANDBY); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void am33xx_pm_end(void) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci u32 val = 0; 3088c2ecf20Sopenharmony_ci struct nvmem_device *nvmem; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0"); 3118c2ecf20Sopenharmony_ci if (IS_ERR(nvmem)) 3128c2ecf20Sopenharmony_ci return; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci m3_ipc->ops->finish_low_power(m3_ipc); 3158c2ecf20Sopenharmony_ci if (rtc_only_idle) { 3168c2ecf20Sopenharmony_ci if (retrigger_irq) { 3178c2ecf20Sopenharmony_ci /* 3188c2ecf20Sopenharmony_ci * 32 bits of Interrupt Set-Pending correspond to 32 3198c2ecf20Sopenharmony_ci * 32 interrupts. Compute the bit offset of the 3208c2ecf20Sopenharmony_ci * Interrupt and set that particular bit 3218c2ecf20Sopenharmony_ci * Compute the register offset by dividing interrupt 3228c2ecf20Sopenharmony_ci * number by 32 and mutiplying by 4 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci writel_relaxed(1 << (retrigger_irq & 31), 3258c2ecf20Sopenharmony_ci gic_dist_base + GIC_INT_SET_PENDING_BASE 3268c2ecf20Sopenharmony_ci + retrigger_irq / 32 * 4); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, 3308c2ecf20Sopenharmony_ci (void *)&val); 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci rtc_only_idle = 0; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci pm_ops->finish_suspend(); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int am33xx_pm_valid(suspend_state_t state) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci switch (state) { 3418c2ecf20Sopenharmony_ci case PM_SUSPEND_STANDBY: 3428c2ecf20Sopenharmony_ci case PM_SUSPEND_MEM: 3438c2ecf20Sopenharmony_ci return 1; 3448c2ecf20Sopenharmony_ci default: 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops am33xx_pm_ops = { 3508c2ecf20Sopenharmony_ci .begin = am33xx_pm_begin, 3518c2ecf20Sopenharmony_ci .end = am33xx_pm_end, 3528c2ecf20Sopenharmony_ci .enter = am33xx_pm_enter, 3538c2ecf20Sopenharmony_ci .valid = am33xx_pm_valid, 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void am33xx_pm_set_ipc_ops(void) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci u32 resume_address; 3608c2ecf20Sopenharmony_ci int temp; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci temp = ti_emif_get_mem_type(); 3638c2ecf20Sopenharmony_ci if (temp < 0) { 3648c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, "PM: Cannot determine memory type, no PM available\n"); 3658c2ecf20Sopenharmony_ci return; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci m3_ipc->ops->set_mem_type(m3_ipc, temp); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Physical resume address to be used by ROM code */ 3708c2ecf20Sopenharmony_ci resume_address = am33xx_do_wfi_sram_phys + 3718c2ecf20Sopenharmony_ci *pm_sram->resume_offset + 0x4; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci m3_ipc->ops->set_resume_address(m3_ipc, (void *)resume_address); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic void am33xx_pm_free_sram(void) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz); 3798c2ecf20Sopenharmony_ci gen_pool_free(sram_pool_data, ocmcram_location_data, 3808c2ecf20Sopenharmony_ci sizeof(struct am33xx_pm_ro_sram_data)); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* 3848c2ecf20Sopenharmony_ci * Push the minimal suspend-resume code to SRAM 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic int am33xx_pm_alloc_sram(void) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct device_node *np; 3898c2ecf20Sopenharmony_ci int ret = 0; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ti,omap3-mpu"); 3928c2ecf20Sopenharmony_ci if (!np) { 3938c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu"); 3948c2ecf20Sopenharmony_ci if (!np) { 3958c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, "PM: %s: Unable to find device node for mpu\n", 3968c2ecf20Sopenharmony_ci __func__); 3978c2ecf20Sopenharmony_ci return -ENODEV; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci sram_pool = of_gen_pool_get(np, "pm-sram", 0); 4028c2ecf20Sopenharmony_ci if (!sram_pool) { 4038c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, "PM: %s: Unable to get sram pool for ocmcram\n", 4048c2ecf20Sopenharmony_ci __func__); 4058c2ecf20Sopenharmony_ci ret = -ENODEV; 4068c2ecf20Sopenharmony_ci goto mpu_put_node; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci sram_pool_data = of_gen_pool_get(np, "pm-sram", 1); 4108c2ecf20Sopenharmony_ci if (!sram_pool_data) { 4118c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, "PM: %s: Unable to get sram data pool for ocmcram\n", 4128c2ecf20Sopenharmony_ci __func__); 4138c2ecf20Sopenharmony_ci ret = -ENODEV; 4148c2ecf20Sopenharmony_ci goto mpu_put_node; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci ocmcram_location = gen_pool_alloc(sram_pool, *pm_sram->do_wfi_sz); 4188c2ecf20Sopenharmony_ci if (!ocmcram_location) { 4198c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, "PM: %s: Unable to allocate memory from ocmcram\n", 4208c2ecf20Sopenharmony_ci __func__); 4218c2ecf20Sopenharmony_ci ret = -ENOMEM; 4228c2ecf20Sopenharmony_ci goto mpu_put_node; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ocmcram_location_data = gen_pool_alloc(sram_pool_data, 4268c2ecf20Sopenharmony_ci sizeof(struct emif_regs_amx3)); 4278c2ecf20Sopenharmony_ci if (!ocmcram_location_data) { 4288c2ecf20Sopenharmony_ci dev_err(pm33xx_dev, "PM: Unable to allocate memory from ocmcram\n"); 4298c2ecf20Sopenharmony_ci gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz); 4308c2ecf20Sopenharmony_ci ret = -ENOMEM; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cimpu_put_node: 4348c2ecf20Sopenharmony_ci of_node_put(np); 4358c2ecf20Sopenharmony_ci return ret; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int am33xx_pm_rtc_setup(void) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct device_node *np; 4418c2ecf20Sopenharmony_ci unsigned long val = 0; 4428c2ecf20Sopenharmony_ci struct nvmem_device *nvmem; 4438c2ecf20Sopenharmony_ci int error; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci np = of_find_node_by_name(NULL, "rtc"); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (of_device_is_available(np)) { 4488c2ecf20Sopenharmony_ci /* RTC interconnect target module clock */ 4498c2ecf20Sopenharmony_ci rtc_fck = of_clk_get_by_name(np->parent, "fck"); 4508c2ecf20Sopenharmony_ci if (IS_ERR(rtc_fck)) 4518c2ecf20Sopenharmony_ci return PTR_ERR(rtc_fck); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci rtc_base_virt = of_iomap(np, 0); 4548c2ecf20Sopenharmony_ci if (!rtc_base_virt) { 4558c2ecf20Sopenharmony_ci pr_warn("PM: could not iomap rtc"); 4568c2ecf20Sopenharmony_ci error = -ENODEV; 4578c2ecf20Sopenharmony_ci goto err_clk_put; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci omap_rtc = rtc_class_open("rtc0"); 4618c2ecf20Sopenharmony_ci if (!omap_rtc) { 4628c2ecf20Sopenharmony_ci pr_warn("PM: rtc0 not available"); 4638c2ecf20Sopenharmony_ci error = -EPROBE_DEFER; 4648c2ecf20Sopenharmony_ci goto err_iounmap; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci nvmem = devm_nvmem_device_get(&omap_rtc->dev, 4688c2ecf20Sopenharmony_ci "omap_rtc_scratch0"); 4698c2ecf20Sopenharmony_ci if (!IS_ERR(nvmem)) { 4708c2ecf20Sopenharmony_ci nvmem_device_read(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4718c2ecf20Sopenharmony_ci 4, (void *)&rtc_magic_val); 4728c2ecf20Sopenharmony_ci if ((rtc_magic_val & 0xffff) != RTC_REG_BOOT_MAGIC) 4738c2ecf20Sopenharmony_ci pr_warn("PM: bootloader does not support rtc-only!\n"); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4768c2ecf20Sopenharmony_ci 4, (void *)&val); 4778c2ecf20Sopenharmony_ci val = pm_sram->resume_address; 4788c2ecf20Sopenharmony_ci nvmem_device_write(nvmem, RTC_SCRATCH_RESUME_REG * 4, 4798c2ecf20Sopenharmony_ci 4, (void *)&val); 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci pr_warn("PM: no-rtc available, rtc-only mode disabled.\n"); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cierr_iounmap: 4888c2ecf20Sopenharmony_ci iounmap(rtc_base_virt); 4898c2ecf20Sopenharmony_cierr_clk_put: 4908c2ecf20Sopenharmony_ci clk_put(rtc_fck); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return error; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int am33xx_pm_probe(struct platform_device *pdev) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4988c2ecf20Sopenharmony_ci int ret; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (!of_machine_is_compatible("ti,am33xx") && 5018c2ecf20Sopenharmony_ci !of_machine_is_compatible("ti,am43")) 5028c2ecf20Sopenharmony_ci return -ENODEV; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci pm_ops = dev->platform_data; 5058c2ecf20Sopenharmony_ci if (!pm_ops) { 5068c2ecf20Sopenharmony_ci dev_err(dev, "PM: Cannot get core PM ops!\n"); 5078c2ecf20Sopenharmony_ci return -ENODEV; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ret = am43xx_map_gic(); 5118c2ecf20Sopenharmony_ci if (ret) { 5128c2ecf20Sopenharmony_ci pr_err("PM: Could not ioremap GIC base\n"); 5138c2ecf20Sopenharmony_ci return ret; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci pm_sram = pm_ops->get_sram_addrs(); 5178c2ecf20Sopenharmony_ci if (!pm_sram) { 5188c2ecf20Sopenharmony_ci dev_err(dev, "PM: Cannot get PM asm function addresses!!\n"); 5198c2ecf20Sopenharmony_ci return -ENODEV; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci m3_ipc = wkup_m3_ipc_get(); 5238c2ecf20Sopenharmony_ci if (!m3_ipc) { 5248c2ecf20Sopenharmony_ci pr_err("PM: Cannot get wkup_m3_ipc handle\n"); 5258c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci pm33xx_dev = dev; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci ret = am33xx_pm_alloc_sram(); 5318c2ecf20Sopenharmony_ci if (ret) 5328c2ecf20Sopenharmony_ci goto err_wkup_m3_ipc_put; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci ret = am33xx_pm_rtc_setup(); 5358c2ecf20Sopenharmony_ci if (ret) 5368c2ecf20Sopenharmony_ci goto err_free_sram; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci ret = am33xx_push_sram_idle(); 5398c2ecf20Sopenharmony_ci if (ret) 5408c2ecf20Sopenharmony_ci goto err_unsetup_rtc; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci am33xx_pm_set_ipc_ops(); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 5458c2ecf20Sopenharmony_ci suspend_set_ops(&am33xx_pm_ops); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* 5488c2ecf20Sopenharmony_ci * For a system suspend we must flush the caches, we want 5498c2ecf20Sopenharmony_ci * the DDR in self-refresh, we want to save the context 5508c2ecf20Sopenharmony_ci * of the EMIF, and we want the wkup_m3 to handle low-power 5518c2ecf20Sopenharmony_ci * transition. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci suspend_wfi_flags |= WFI_FLAG_FLUSH_CACHE; 5548c2ecf20Sopenharmony_ci suspend_wfi_flags |= WFI_FLAG_SELF_REFRESH; 5558c2ecf20Sopenharmony_ci suspend_wfi_flags |= WFI_FLAG_SAVE_EMIF; 5568c2ecf20Sopenharmony_ci suspend_wfi_flags |= WFI_FLAG_WAKE_M3; 5578c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 5608c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 5618c2ecf20Sopenharmony_ci if (ret < 0) { 5628c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 5638c2ecf20Sopenharmony_ci goto err_pm_runtime_disable; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci ret = pm_ops->init(am33xx_do_sram_idle); 5678c2ecf20Sopenharmony_ci if (ret) { 5688c2ecf20Sopenharmony_ci dev_err(dev, "Unable to call core pm init!\n"); 5698c2ecf20Sopenharmony_ci ret = -ENODEV; 5708c2ecf20Sopenharmony_ci goto err_pm_runtime_put; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cierr_pm_runtime_put: 5768c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 5778c2ecf20Sopenharmony_cierr_pm_runtime_disable: 5788c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 5798c2ecf20Sopenharmony_cierr_unsetup_rtc: 5808c2ecf20Sopenharmony_ci iounmap(rtc_base_virt); 5818c2ecf20Sopenharmony_ci clk_put(rtc_fck); 5828c2ecf20Sopenharmony_cierr_free_sram: 5838c2ecf20Sopenharmony_ci am33xx_pm_free_sram(); 5848c2ecf20Sopenharmony_ci pm33xx_dev = NULL; 5858c2ecf20Sopenharmony_cierr_wkup_m3_ipc_put: 5868c2ecf20Sopenharmony_ci wkup_m3_ipc_put(m3_ipc); 5878c2ecf20Sopenharmony_ci return ret; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int am33xx_pm_remove(struct platform_device *pdev) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 5938c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5948c2ecf20Sopenharmony_ci if (pm_ops->deinit) 5958c2ecf20Sopenharmony_ci pm_ops->deinit(); 5968c2ecf20Sopenharmony_ci suspend_set_ops(NULL); 5978c2ecf20Sopenharmony_ci wkup_m3_ipc_put(m3_ipc); 5988c2ecf20Sopenharmony_ci am33xx_pm_free_sram(); 5998c2ecf20Sopenharmony_ci iounmap(rtc_base_virt); 6008c2ecf20Sopenharmony_ci clk_put(rtc_fck); 6018c2ecf20Sopenharmony_ci return 0; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic struct platform_driver am33xx_pm_driver = { 6058c2ecf20Sopenharmony_ci .driver = { 6068c2ecf20Sopenharmony_ci .name = "pm33xx", 6078c2ecf20Sopenharmony_ci }, 6088c2ecf20Sopenharmony_ci .probe = am33xx_pm_probe, 6098c2ecf20Sopenharmony_ci .remove = am33xx_pm_remove, 6108c2ecf20Sopenharmony_ci}; 6118c2ecf20Sopenharmony_cimodule_platform_driver(am33xx_pm_driver); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pm33xx"); 6148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 6158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("am33xx power management driver"); 616