1afe761f8SDave Gerlach // SPDX-License-Identifier: GPL-2.0 2afe761f8SDave Gerlach /* 3afe761f8SDave Gerlach * AM33XX Power Management Routines 4afe761f8SDave Gerlach * 5afe761f8SDave Gerlach * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/ 6afe761f8SDave Gerlach * Vaibhav Bedia, Dave Gerlach 7afe761f8SDave Gerlach */ 8afe761f8SDave Gerlach 9afe761f8SDave Gerlach #include <linux/cpu.h> 10afe761f8SDave Gerlach #include <linux/err.h> 11afe761f8SDave Gerlach #include <linux/genalloc.h> 12afe761f8SDave Gerlach #include <linux/kernel.h> 13afe761f8SDave Gerlach #include <linux/init.h> 14afe761f8SDave Gerlach #include <linux/io.h> 15afe761f8SDave Gerlach #include <linux/module.h> 16afe761f8SDave Gerlach #include <linux/of.h> 17afe761f8SDave Gerlach #include <linux/platform_data/pm33xx.h> 18afe761f8SDave Gerlach #include <linux/platform_device.h> 19afe761f8SDave Gerlach #include <linux/sizes.h> 20afe761f8SDave Gerlach #include <linux/sram.h> 21afe761f8SDave Gerlach #include <linux/suspend.h> 22afe761f8SDave Gerlach #include <linux/ti-emif-sram.h> 23afe761f8SDave Gerlach #include <linux/wkup_m3_ipc.h> 24afe761f8SDave Gerlach 25afe761f8SDave Gerlach #include <asm/proc-fns.h> 26afe761f8SDave Gerlach #include <asm/suspend.h> 27afe761f8SDave Gerlach #include <asm/system_misc.h> 28afe761f8SDave Gerlach 29afe761f8SDave Gerlach #define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \ 30afe761f8SDave Gerlach (unsigned long)pm_sram->do_wfi) 31afe761f8SDave Gerlach 32afe761f8SDave Gerlach static int (*am33xx_do_wfi_sram)(unsigned long unused); 33afe761f8SDave Gerlach static phys_addr_t am33xx_do_wfi_sram_phys; 34afe761f8SDave Gerlach 35afe761f8SDave Gerlach static struct gen_pool *sram_pool, *sram_pool_data; 36afe761f8SDave Gerlach static unsigned long ocmcram_location, ocmcram_location_data; 37afe761f8SDave Gerlach 38afe761f8SDave Gerlach static struct am33xx_pm_platform_data *pm_ops; 39afe761f8SDave Gerlach static struct am33xx_pm_sram_addr *pm_sram; 40afe761f8SDave Gerlach 41afe761f8SDave Gerlach static struct device *pm33xx_dev; 42afe761f8SDave Gerlach static struct wkup_m3_ipc *m3_ipc; 43afe761f8SDave Gerlach 4474655749SDave Gerlach static unsigned long suspend_wfi_flags; 4574655749SDave Gerlach 46afe761f8SDave Gerlach static u32 sram_suspend_address(unsigned long addr) 47afe761f8SDave Gerlach { 48afe761f8SDave Gerlach return ((unsigned long)am33xx_do_wfi_sram + 49afe761f8SDave Gerlach AMX3_PM_SRAM_SYMBOL_OFFSET(addr)); 50afe761f8SDave Gerlach } 51afe761f8SDave Gerlach 521c6c0354SKeerthy static int am33xx_push_sram_idle(void) 531c6c0354SKeerthy { 541c6c0354SKeerthy struct am33xx_pm_ro_sram_data ro_sram_data; 551c6c0354SKeerthy int ret; 561c6c0354SKeerthy u32 table_addr, ro_data_addr; 571c6c0354SKeerthy void *copy_addr; 581c6c0354SKeerthy 591c6c0354SKeerthy ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data; 601c6c0354SKeerthy ro_sram_data.amx3_pm_sram_data_phys = 611c6c0354SKeerthy gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data); 621c6c0354SKeerthy ro_sram_data.rtc_base_virt = pm_ops->get_rtc_base_addr(); 631c6c0354SKeerthy 641c6c0354SKeerthy /* Save physical address to calculate resume offset during pm init */ 651c6c0354SKeerthy am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool, 661c6c0354SKeerthy ocmcram_location); 671c6c0354SKeerthy 681c6c0354SKeerthy am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location, 691c6c0354SKeerthy pm_sram->do_wfi, 701c6c0354SKeerthy *pm_sram->do_wfi_sz); 711c6c0354SKeerthy if (!am33xx_do_wfi_sram) { 721c6c0354SKeerthy dev_err(pm33xx_dev, 731c6c0354SKeerthy "PM: %s: am33xx_do_wfi copy to sram failed\n", 741c6c0354SKeerthy __func__); 751c6c0354SKeerthy return -ENODEV; 761c6c0354SKeerthy } 771c6c0354SKeerthy 781c6c0354SKeerthy table_addr = 791c6c0354SKeerthy sram_suspend_address((unsigned long)pm_sram->emif_sram_table); 801c6c0354SKeerthy ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr); 811c6c0354SKeerthy if (ret) { 821c6c0354SKeerthy dev_dbg(pm33xx_dev, 831c6c0354SKeerthy "PM: %s: EMIF function copy failed\n", __func__); 841c6c0354SKeerthy return -EPROBE_DEFER; 851c6c0354SKeerthy } 861c6c0354SKeerthy 871c6c0354SKeerthy ro_data_addr = 881c6c0354SKeerthy sram_suspend_address((unsigned long)pm_sram->ro_sram_data); 891c6c0354SKeerthy copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr, 901c6c0354SKeerthy &ro_sram_data, 911c6c0354SKeerthy sizeof(ro_sram_data)); 921c6c0354SKeerthy if (!copy_addr) { 931c6c0354SKeerthy dev_err(pm33xx_dev, 941c6c0354SKeerthy "PM: %s: ro_sram_data copy to sram failed\n", 951c6c0354SKeerthy __func__); 961c6c0354SKeerthy return -ENODEV; 971c6c0354SKeerthy } 981c6c0354SKeerthy 991c6c0354SKeerthy return 0; 1001c6c0354SKeerthy } 1011c6c0354SKeerthy 102afe761f8SDave Gerlach #ifdef CONFIG_SUSPEND 103afe761f8SDave Gerlach static int am33xx_pm_suspend(suspend_state_t suspend_state) 104afe761f8SDave Gerlach { 105afe761f8SDave Gerlach int i, ret = 0; 106afe761f8SDave Gerlach 107afe761f8SDave Gerlach ret = pm_ops->soc_suspend((unsigned long)suspend_state, 10874655749SDave Gerlach am33xx_do_wfi_sram, suspend_wfi_flags); 109afe761f8SDave Gerlach 110afe761f8SDave Gerlach if (ret) { 111afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Kernel suspend failure\n"); 112afe761f8SDave Gerlach } else { 113afe761f8SDave Gerlach i = m3_ipc->ops->request_pm_status(m3_ipc); 114afe761f8SDave Gerlach 115afe761f8SDave Gerlach switch (i) { 116afe761f8SDave Gerlach case 0: 117afe761f8SDave Gerlach dev_info(pm33xx_dev, 118afe761f8SDave Gerlach "PM: Successfully put all powerdomains to target state\n"); 119afe761f8SDave Gerlach break; 120afe761f8SDave Gerlach case 1: 121afe761f8SDave Gerlach dev_err(pm33xx_dev, 122afe761f8SDave Gerlach "PM: Could not transition all powerdomains to target state\n"); 123afe761f8SDave Gerlach ret = -1; 124afe761f8SDave Gerlach break; 125afe761f8SDave Gerlach default: 126afe761f8SDave Gerlach dev_err(pm33xx_dev, 127afe761f8SDave Gerlach "PM: CM3 returned unknown result = %d\n", i); 128afe761f8SDave Gerlach ret = -1; 129afe761f8SDave Gerlach } 130afe761f8SDave Gerlach } 131afe761f8SDave Gerlach 132afe761f8SDave Gerlach return ret; 133afe761f8SDave Gerlach } 134afe761f8SDave Gerlach 135afe761f8SDave Gerlach static int am33xx_pm_enter(suspend_state_t suspend_state) 136afe761f8SDave Gerlach { 137afe761f8SDave Gerlach int ret = 0; 138afe761f8SDave Gerlach 139afe761f8SDave Gerlach switch (suspend_state) { 140afe761f8SDave Gerlach case PM_SUSPEND_MEM: 141afe761f8SDave Gerlach case PM_SUSPEND_STANDBY: 142afe761f8SDave Gerlach ret = am33xx_pm_suspend(suspend_state); 143afe761f8SDave Gerlach break; 144afe761f8SDave Gerlach default: 145afe761f8SDave Gerlach ret = -EINVAL; 146afe761f8SDave Gerlach } 147afe761f8SDave Gerlach 148afe761f8SDave Gerlach return ret; 149afe761f8SDave Gerlach } 150afe761f8SDave Gerlach 151afe761f8SDave Gerlach static int am33xx_pm_begin(suspend_state_t state) 152afe761f8SDave Gerlach { 153afe761f8SDave Gerlach int ret = -EINVAL; 154afe761f8SDave Gerlach 155afe761f8SDave Gerlach switch (state) { 156afe761f8SDave Gerlach case PM_SUSPEND_MEM: 157afe761f8SDave Gerlach ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_DEEPSLEEP); 158afe761f8SDave Gerlach break; 159afe761f8SDave Gerlach case PM_SUSPEND_STANDBY: 160afe761f8SDave Gerlach ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_STANDBY); 161afe761f8SDave Gerlach break; 162afe761f8SDave Gerlach } 163afe761f8SDave Gerlach 164afe761f8SDave Gerlach return ret; 165afe761f8SDave Gerlach } 166afe761f8SDave Gerlach 167afe761f8SDave Gerlach static void am33xx_pm_end(void) 168afe761f8SDave Gerlach { 169afe761f8SDave Gerlach m3_ipc->ops->finish_low_power(m3_ipc); 170afe761f8SDave Gerlach } 171afe761f8SDave Gerlach 172afe761f8SDave Gerlach static int am33xx_pm_valid(suspend_state_t state) 173afe761f8SDave Gerlach { 174afe761f8SDave Gerlach switch (state) { 175afe761f8SDave Gerlach case PM_SUSPEND_STANDBY: 176afe761f8SDave Gerlach case PM_SUSPEND_MEM: 177afe761f8SDave Gerlach return 1; 178afe761f8SDave Gerlach default: 179afe761f8SDave Gerlach return 0; 180afe761f8SDave Gerlach } 181afe761f8SDave Gerlach } 182afe761f8SDave Gerlach 183afe761f8SDave Gerlach static const struct platform_suspend_ops am33xx_pm_ops = { 184afe761f8SDave Gerlach .begin = am33xx_pm_begin, 185afe761f8SDave Gerlach .end = am33xx_pm_end, 186afe761f8SDave Gerlach .enter = am33xx_pm_enter, 187afe761f8SDave Gerlach .valid = am33xx_pm_valid, 188afe761f8SDave Gerlach }; 189afe761f8SDave Gerlach #endif /* CONFIG_SUSPEND */ 190afe761f8SDave Gerlach 191afe761f8SDave Gerlach static void am33xx_pm_set_ipc_ops(void) 192afe761f8SDave Gerlach { 193afe761f8SDave Gerlach u32 resume_address; 194afe761f8SDave Gerlach int temp; 195afe761f8SDave Gerlach 196afe761f8SDave Gerlach temp = ti_emif_get_mem_type(); 197afe761f8SDave Gerlach if (temp < 0) { 198afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Cannot determine memory type, no PM available\n"); 199afe761f8SDave Gerlach return; 200afe761f8SDave Gerlach } 201afe761f8SDave Gerlach m3_ipc->ops->set_mem_type(m3_ipc, temp); 202afe761f8SDave Gerlach 203afe761f8SDave Gerlach /* Physical resume address to be used by ROM code */ 204afe761f8SDave Gerlach resume_address = am33xx_do_wfi_sram_phys + 205afe761f8SDave Gerlach *pm_sram->resume_offset + 0x4; 206afe761f8SDave Gerlach 207afe761f8SDave Gerlach m3_ipc->ops->set_resume_address(m3_ipc, (void *)resume_address); 208afe761f8SDave Gerlach } 209afe761f8SDave Gerlach 210afe761f8SDave Gerlach static void am33xx_pm_free_sram(void) 211afe761f8SDave Gerlach { 212afe761f8SDave Gerlach gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz); 213afe761f8SDave Gerlach gen_pool_free(sram_pool_data, ocmcram_location_data, 214afe761f8SDave Gerlach sizeof(struct am33xx_pm_ro_sram_data)); 215afe761f8SDave Gerlach } 216afe761f8SDave Gerlach 217afe761f8SDave Gerlach /* 218afe761f8SDave Gerlach * Push the minimal suspend-resume code to SRAM 219afe761f8SDave Gerlach */ 220afe761f8SDave Gerlach static int am33xx_pm_alloc_sram(void) 221afe761f8SDave Gerlach { 222afe761f8SDave Gerlach struct device_node *np; 223afe761f8SDave Gerlach int ret = 0; 224afe761f8SDave Gerlach 225afe761f8SDave Gerlach np = of_find_compatible_node(NULL, NULL, "ti,omap3-mpu"); 226afe761f8SDave Gerlach if (!np) { 227afe761f8SDave Gerlach np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu"); 228afe761f8SDave Gerlach if (!np) { 229afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to find device node for mpu\n", 230afe761f8SDave Gerlach __func__); 231afe761f8SDave Gerlach return -ENODEV; 232afe761f8SDave Gerlach } 233afe761f8SDave Gerlach } 234afe761f8SDave Gerlach 235afe761f8SDave Gerlach sram_pool = of_gen_pool_get(np, "pm-sram", 0); 236afe761f8SDave Gerlach if (!sram_pool) { 237afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to get sram pool for ocmcram\n", 238afe761f8SDave Gerlach __func__); 239afe761f8SDave Gerlach ret = -ENODEV; 240afe761f8SDave Gerlach goto mpu_put_node; 241afe761f8SDave Gerlach } 242afe761f8SDave Gerlach 243afe761f8SDave Gerlach sram_pool_data = of_gen_pool_get(np, "pm-sram", 1); 244afe761f8SDave Gerlach if (!sram_pool_data) { 245afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to get sram data pool for ocmcram\n", 246afe761f8SDave Gerlach __func__); 247afe761f8SDave Gerlach ret = -ENODEV; 248afe761f8SDave Gerlach goto mpu_put_node; 249afe761f8SDave Gerlach } 250afe761f8SDave Gerlach 251afe761f8SDave Gerlach ocmcram_location = gen_pool_alloc(sram_pool, *pm_sram->do_wfi_sz); 252afe761f8SDave Gerlach if (!ocmcram_location) { 253afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to allocate memory from ocmcram\n", 254afe761f8SDave Gerlach __func__); 255afe761f8SDave Gerlach ret = -ENOMEM; 256afe761f8SDave Gerlach goto mpu_put_node; 257afe761f8SDave Gerlach } 258afe761f8SDave Gerlach 259afe761f8SDave Gerlach ocmcram_location_data = gen_pool_alloc(sram_pool_data, 260afe761f8SDave Gerlach sizeof(struct emif_regs_amx3)); 261afe761f8SDave Gerlach if (!ocmcram_location_data) { 262afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Unable to allocate memory from ocmcram\n"); 263afe761f8SDave Gerlach gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz); 264afe761f8SDave Gerlach ret = -ENOMEM; 265afe761f8SDave Gerlach } 266afe761f8SDave Gerlach 267afe761f8SDave Gerlach mpu_put_node: 268afe761f8SDave Gerlach of_node_put(np); 269afe761f8SDave Gerlach return ret; 270afe761f8SDave Gerlach } 271afe761f8SDave Gerlach 272afe761f8SDave Gerlach static int am33xx_pm_probe(struct platform_device *pdev) 273afe761f8SDave Gerlach { 274afe761f8SDave Gerlach struct device *dev = &pdev->dev; 275afe761f8SDave Gerlach int ret; 276afe761f8SDave Gerlach 277afe761f8SDave Gerlach if (!of_machine_is_compatible("ti,am33xx") && 278afe761f8SDave Gerlach !of_machine_is_compatible("ti,am43")) 279afe761f8SDave Gerlach return -ENODEV; 280afe761f8SDave Gerlach 281afe761f8SDave Gerlach pm_ops = dev->platform_data; 282afe761f8SDave Gerlach if (!pm_ops) { 283afe761f8SDave Gerlach dev_err(dev, "PM: Cannot get core PM ops!\n"); 284afe761f8SDave Gerlach return -ENODEV; 285afe761f8SDave Gerlach } 286afe761f8SDave Gerlach 287afe761f8SDave Gerlach pm_sram = pm_ops->get_sram_addrs(); 288afe761f8SDave Gerlach if (!pm_sram) { 289afe761f8SDave Gerlach dev_err(dev, "PM: Cannot get PM asm function addresses!!\n"); 290afe761f8SDave Gerlach return -ENODEV; 291afe761f8SDave Gerlach } 292afe761f8SDave Gerlach 293afe761f8SDave Gerlach pm33xx_dev = dev; 294afe761f8SDave Gerlach 295afe761f8SDave Gerlach ret = am33xx_pm_alloc_sram(); 296afe761f8SDave Gerlach if (ret) 297afe761f8SDave Gerlach return ret; 298afe761f8SDave Gerlach 299afe761f8SDave Gerlach ret = am33xx_push_sram_idle(); 300afe761f8SDave Gerlach if (ret) 301afe761f8SDave Gerlach goto err_free_sram; 302afe761f8SDave Gerlach 303afe761f8SDave Gerlach m3_ipc = wkup_m3_ipc_get(); 304afe761f8SDave Gerlach if (!m3_ipc) { 305afe761f8SDave Gerlach dev_dbg(dev, "PM: Cannot get wkup_m3_ipc handle\n"); 306afe761f8SDave Gerlach ret = -EPROBE_DEFER; 307afe761f8SDave Gerlach goto err_free_sram; 308afe761f8SDave Gerlach } 309afe761f8SDave Gerlach 310afe761f8SDave Gerlach am33xx_pm_set_ipc_ops(); 311afe761f8SDave Gerlach 312afe761f8SDave Gerlach #ifdef CONFIG_SUSPEND 313afe761f8SDave Gerlach suspend_set_ops(&am33xx_pm_ops); 314afe761f8SDave Gerlach #endif /* CONFIG_SUSPEND */ 315afe761f8SDave Gerlach 31674655749SDave Gerlach /* 31774655749SDave Gerlach * For a system suspend we must flush the caches, we want 31874655749SDave Gerlach * the DDR in self-refresh, we want to save the context 31974655749SDave Gerlach * of the EMIF, and we want the wkup_m3 to handle low-power 32074655749SDave Gerlach * transition. 32174655749SDave Gerlach */ 32274655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_FLUSH_CACHE; 32374655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_SELF_REFRESH; 32474655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_SAVE_EMIF; 32574655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_WAKE_M3; 32674655749SDave Gerlach 327afe761f8SDave Gerlach ret = pm_ops->init(); 328afe761f8SDave Gerlach if (ret) { 329afe761f8SDave Gerlach dev_err(dev, "Unable to call core pm init!\n"); 330afe761f8SDave Gerlach ret = -ENODEV; 331afe761f8SDave Gerlach goto err_put_wkup_m3_ipc; 332afe761f8SDave Gerlach } 333afe761f8SDave Gerlach 334afe761f8SDave Gerlach return 0; 335afe761f8SDave Gerlach 336afe761f8SDave Gerlach err_put_wkup_m3_ipc: 337afe761f8SDave Gerlach wkup_m3_ipc_put(m3_ipc); 338afe761f8SDave Gerlach err_free_sram: 339afe761f8SDave Gerlach am33xx_pm_free_sram(); 340afe761f8SDave Gerlach pm33xx_dev = NULL; 341afe761f8SDave Gerlach return ret; 342afe761f8SDave Gerlach } 343afe761f8SDave Gerlach 344afe761f8SDave Gerlach static int am33xx_pm_remove(struct platform_device *pdev) 345afe761f8SDave Gerlach { 346afe761f8SDave Gerlach suspend_set_ops(NULL); 347afe761f8SDave Gerlach wkup_m3_ipc_put(m3_ipc); 348afe761f8SDave Gerlach am33xx_pm_free_sram(); 349afe761f8SDave Gerlach return 0; 350afe761f8SDave Gerlach } 351afe761f8SDave Gerlach 352afe761f8SDave Gerlach static struct platform_driver am33xx_pm_driver = { 353afe761f8SDave Gerlach .driver = { 354afe761f8SDave Gerlach .name = "pm33xx", 355afe761f8SDave Gerlach }, 356afe761f8SDave Gerlach .probe = am33xx_pm_probe, 357afe761f8SDave Gerlach .remove = am33xx_pm_remove, 358afe761f8SDave Gerlach }; 359afe761f8SDave Gerlach module_platform_driver(am33xx_pm_driver); 360afe761f8SDave Gerlach 361afe761f8SDave Gerlach MODULE_ALIAS("platform:pm33xx"); 362afe761f8SDave Gerlach MODULE_LICENSE("GPL v2"); 363afe761f8SDave Gerlach MODULE_DESCRIPTION("am33xx power management driver"); 364