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 52afe761f8SDave Gerlach #ifdef CONFIG_SUSPEND 53afe761f8SDave Gerlach static int am33xx_pm_suspend(suspend_state_t suspend_state) 54afe761f8SDave Gerlach { 55afe761f8SDave Gerlach int i, ret = 0; 56afe761f8SDave Gerlach 57afe761f8SDave Gerlach ret = pm_ops->soc_suspend((unsigned long)suspend_state, 5874655749SDave Gerlach am33xx_do_wfi_sram, suspend_wfi_flags); 59afe761f8SDave Gerlach 60afe761f8SDave Gerlach if (ret) { 61afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Kernel suspend failure\n"); 62afe761f8SDave Gerlach } else { 63afe761f8SDave Gerlach i = m3_ipc->ops->request_pm_status(m3_ipc); 64afe761f8SDave Gerlach 65afe761f8SDave Gerlach switch (i) { 66afe761f8SDave Gerlach case 0: 67afe761f8SDave Gerlach dev_info(pm33xx_dev, 68afe761f8SDave Gerlach "PM: Successfully put all powerdomains to target state\n"); 69afe761f8SDave Gerlach break; 70afe761f8SDave Gerlach case 1: 71afe761f8SDave Gerlach dev_err(pm33xx_dev, 72afe761f8SDave Gerlach "PM: Could not transition all powerdomains to target state\n"); 73afe761f8SDave Gerlach ret = -1; 74afe761f8SDave Gerlach break; 75afe761f8SDave Gerlach default: 76afe761f8SDave Gerlach dev_err(pm33xx_dev, 77afe761f8SDave Gerlach "PM: CM3 returned unknown result = %d\n", i); 78afe761f8SDave Gerlach ret = -1; 79afe761f8SDave Gerlach } 80afe761f8SDave Gerlach } 81afe761f8SDave Gerlach 82afe761f8SDave Gerlach return ret; 83afe761f8SDave Gerlach } 84afe761f8SDave Gerlach 85afe761f8SDave Gerlach static int am33xx_pm_enter(suspend_state_t suspend_state) 86afe761f8SDave Gerlach { 87afe761f8SDave Gerlach int ret = 0; 88afe761f8SDave Gerlach 89afe761f8SDave Gerlach switch (suspend_state) { 90afe761f8SDave Gerlach case PM_SUSPEND_MEM: 91afe761f8SDave Gerlach case PM_SUSPEND_STANDBY: 92afe761f8SDave Gerlach ret = am33xx_pm_suspend(suspend_state); 93afe761f8SDave Gerlach break; 94afe761f8SDave Gerlach default: 95afe761f8SDave Gerlach ret = -EINVAL; 96afe761f8SDave Gerlach } 97afe761f8SDave Gerlach 98afe761f8SDave Gerlach return ret; 99afe761f8SDave Gerlach } 100afe761f8SDave Gerlach 101afe761f8SDave Gerlach static int am33xx_pm_begin(suspend_state_t state) 102afe761f8SDave Gerlach { 103afe761f8SDave Gerlach int ret = -EINVAL; 104afe761f8SDave Gerlach 105afe761f8SDave Gerlach switch (state) { 106afe761f8SDave Gerlach case PM_SUSPEND_MEM: 107afe761f8SDave Gerlach ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_DEEPSLEEP); 108afe761f8SDave Gerlach break; 109afe761f8SDave Gerlach case PM_SUSPEND_STANDBY: 110afe761f8SDave Gerlach ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_STANDBY); 111afe761f8SDave Gerlach break; 112afe761f8SDave Gerlach } 113afe761f8SDave Gerlach 114afe761f8SDave Gerlach return ret; 115afe761f8SDave Gerlach } 116afe761f8SDave Gerlach 117afe761f8SDave Gerlach static void am33xx_pm_end(void) 118afe761f8SDave Gerlach { 119afe761f8SDave Gerlach m3_ipc->ops->finish_low_power(m3_ipc); 120afe761f8SDave Gerlach } 121afe761f8SDave Gerlach 122afe761f8SDave Gerlach static int am33xx_pm_valid(suspend_state_t state) 123afe761f8SDave Gerlach { 124afe761f8SDave Gerlach switch (state) { 125afe761f8SDave Gerlach case PM_SUSPEND_STANDBY: 126afe761f8SDave Gerlach case PM_SUSPEND_MEM: 127afe761f8SDave Gerlach return 1; 128afe761f8SDave Gerlach default: 129afe761f8SDave Gerlach return 0; 130afe761f8SDave Gerlach } 131afe761f8SDave Gerlach } 132afe761f8SDave Gerlach 133afe761f8SDave Gerlach static const struct platform_suspend_ops am33xx_pm_ops = { 134afe761f8SDave Gerlach .begin = am33xx_pm_begin, 135afe761f8SDave Gerlach .end = am33xx_pm_end, 136afe761f8SDave Gerlach .enter = am33xx_pm_enter, 137afe761f8SDave Gerlach .valid = am33xx_pm_valid, 138afe761f8SDave Gerlach }; 139afe761f8SDave Gerlach #endif /* CONFIG_SUSPEND */ 140afe761f8SDave Gerlach 141afe761f8SDave Gerlach static void am33xx_pm_set_ipc_ops(void) 142afe761f8SDave Gerlach { 143afe761f8SDave Gerlach u32 resume_address; 144afe761f8SDave Gerlach int temp; 145afe761f8SDave Gerlach 146afe761f8SDave Gerlach temp = ti_emif_get_mem_type(); 147afe761f8SDave Gerlach if (temp < 0) { 148afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Cannot determine memory type, no PM available\n"); 149afe761f8SDave Gerlach return; 150afe761f8SDave Gerlach } 151afe761f8SDave Gerlach m3_ipc->ops->set_mem_type(m3_ipc, temp); 152afe761f8SDave Gerlach 153afe761f8SDave Gerlach /* Physical resume address to be used by ROM code */ 154afe761f8SDave Gerlach resume_address = am33xx_do_wfi_sram_phys + 155afe761f8SDave Gerlach *pm_sram->resume_offset + 0x4; 156afe761f8SDave Gerlach 157afe761f8SDave Gerlach m3_ipc->ops->set_resume_address(m3_ipc, (void *)resume_address); 158afe761f8SDave Gerlach } 159afe761f8SDave Gerlach 160afe761f8SDave Gerlach static void am33xx_pm_free_sram(void) 161afe761f8SDave Gerlach { 162afe761f8SDave Gerlach gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz); 163afe761f8SDave Gerlach gen_pool_free(sram_pool_data, ocmcram_location_data, 164afe761f8SDave Gerlach sizeof(struct am33xx_pm_ro_sram_data)); 165afe761f8SDave Gerlach } 166afe761f8SDave Gerlach 167afe761f8SDave Gerlach /* 168afe761f8SDave Gerlach * Push the minimal suspend-resume code to SRAM 169afe761f8SDave Gerlach */ 170afe761f8SDave Gerlach static int am33xx_pm_alloc_sram(void) 171afe761f8SDave Gerlach { 172afe761f8SDave Gerlach struct device_node *np; 173afe761f8SDave Gerlach int ret = 0; 174afe761f8SDave Gerlach 175afe761f8SDave Gerlach np = of_find_compatible_node(NULL, NULL, "ti,omap3-mpu"); 176afe761f8SDave Gerlach if (!np) { 177afe761f8SDave Gerlach np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu"); 178afe761f8SDave Gerlach if (!np) { 179afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to find device node for mpu\n", 180afe761f8SDave Gerlach __func__); 181afe761f8SDave Gerlach return -ENODEV; 182afe761f8SDave Gerlach } 183afe761f8SDave Gerlach } 184afe761f8SDave Gerlach 185afe761f8SDave Gerlach sram_pool = of_gen_pool_get(np, "pm-sram", 0); 186afe761f8SDave Gerlach if (!sram_pool) { 187afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to get sram pool for ocmcram\n", 188afe761f8SDave Gerlach __func__); 189afe761f8SDave Gerlach ret = -ENODEV; 190afe761f8SDave Gerlach goto mpu_put_node; 191afe761f8SDave Gerlach } 192afe761f8SDave Gerlach 193afe761f8SDave Gerlach sram_pool_data = of_gen_pool_get(np, "pm-sram", 1); 194afe761f8SDave Gerlach if (!sram_pool_data) { 195afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to get sram data pool for ocmcram\n", 196afe761f8SDave Gerlach __func__); 197afe761f8SDave Gerlach ret = -ENODEV; 198afe761f8SDave Gerlach goto mpu_put_node; 199afe761f8SDave Gerlach } 200afe761f8SDave Gerlach 201afe761f8SDave Gerlach ocmcram_location = gen_pool_alloc(sram_pool, *pm_sram->do_wfi_sz); 202afe761f8SDave Gerlach if (!ocmcram_location) { 203afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: %s: Unable to allocate memory from ocmcram\n", 204afe761f8SDave Gerlach __func__); 205afe761f8SDave Gerlach ret = -ENOMEM; 206afe761f8SDave Gerlach goto mpu_put_node; 207afe761f8SDave Gerlach } 208afe761f8SDave Gerlach 209afe761f8SDave Gerlach ocmcram_location_data = gen_pool_alloc(sram_pool_data, 210afe761f8SDave Gerlach sizeof(struct emif_regs_amx3)); 211afe761f8SDave Gerlach if (!ocmcram_location_data) { 212afe761f8SDave Gerlach dev_err(pm33xx_dev, "PM: Unable to allocate memory from ocmcram\n"); 213afe761f8SDave Gerlach gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz); 214afe761f8SDave Gerlach ret = -ENOMEM; 215afe761f8SDave Gerlach } 216afe761f8SDave Gerlach 217afe761f8SDave Gerlach mpu_put_node: 218afe761f8SDave Gerlach of_node_put(np); 219afe761f8SDave Gerlach return ret; 220afe761f8SDave Gerlach } 221afe761f8SDave Gerlach 222afe761f8SDave Gerlach static int am33xx_push_sram_idle(void) 223afe761f8SDave Gerlach { 224afe761f8SDave Gerlach struct am33xx_pm_ro_sram_data ro_sram_data; 225afe761f8SDave Gerlach int ret; 226afe761f8SDave Gerlach u32 table_addr, ro_data_addr; 227afe761f8SDave Gerlach void *copy_addr; 228afe761f8SDave Gerlach 229afe761f8SDave Gerlach ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data; 230afe761f8SDave Gerlach ro_sram_data.amx3_pm_sram_data_phys = 231afe761f8SDave Gerlach gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data); 232afe761f8SDave Gerlach 233afe761f8SDave Gerlach /* Save physical address to calculate resume offset during pm init */ 234afe761f8SDave Gerlach am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool, 235afe761f8SDave Gerlach ocmcram_location); 236afe761f8SDave Gerlach 237afe761f8SDave Gerlach am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location, 238afe761f8SDave Gerlach pm_sram->do_wfi, 239afe761f8SDave Gerlach *pm_sram->do_wfi_sz); 240afe761f8SDave Gerlach if (!am33xx_do_wfi_sram) { 241afe761f8SDave Gerlach dev_err(pm33xx_dev, 242afe761f8SDave Gerlach "PM: %s: am33xx_do_wfi copy to sram failed\n", 243afe761f8SDave Gerlach __func__); 244afe761f8SDave Gerlach return -ENODEV; 245afe761f8SDave Gerlach } 246afe761f8SDave Gerlach 247afe761f8SDave Gerlach table_addr = 248afe761f8SDave Gerlach sram_suspend_address((unsigned long)pm_sram->emif_sram_table); 249afe761f8SDave Gerlach ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr); 250afe761f8SDave Gerlach if (ret) { 251afe761f8SDave Gerlach dev_dbg(pm33xx_dev, 252afe761f8SDave Gerlach "PM: %s: EMIF function copy failed\n", __func__); 253afe761f8SDave Gerlach return -EPROBE_DEFER; 254afe761f8SDave Gerlach } 255afe761f8SDave Gerlach 256afe761f8SDave Gerlach ro_data_addr = 257afe761f8SDave Gerlach sram_suspend_address((unsigned long)pm_sram->ro_sram_data); 258afe761f8SDave Gerlach copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr, 259afe761f8SDave Gerlach &ro_sram_data, 260afe761f8SDave Gerlach sizeof(ro_sram_data)); 261afe761f8SDave Gerlach if (!copy_addr) { 262afe761f8SDave Gerlach dev_err(pm33xx_dev, 263afe761f8SDave Gerlach "PM: %s: ro_sram_data copy to sram failed\n", 264afe761f8SDave Gerlach __func__); 265afe761f8SDave Gerlach return -ENODEV; 266afe761f8SDave Gerlach } 267afe761f8SDave Gerlach 268afe761f8SDave Gerlach return 0; 269afe761f8SDave Gerlach } 270afe761f8SDave Gerlach 271afe761f8SDave Gerlach static int am33xx_pm_probe(struct platform_device *pdev) 272afe761f8SDave Gerlach { 273afe761f8SDave Gerlach struct device *dev = &pdev->dev; 274afe761f8SDave Gerlach int ret; 275afe761f8SDave Gerlach 276afe761f8SDave Gerlach if (!of_machine_is_compatible("ti,am33xx") && 277afe761f8SDave Gerlach !of_machine_is_compatible("ti,am43")) 278afe761f8SDave Gerlach return -ENODEV; 279afe761f8SDave Gerlach 280afe761f8SDave Gerlach pm_ops = dev->platform_data; 281afe761f8SDave Gerlach if (!pm_ops) { 282afe761f8SDave Gerlach dev_err(dev, "PM: Cannot get core PM ops!\n"); 283afe761f8SDave Gerlach return -ENODEV; 284afe761f8SDave Gerlach } 285afe761f8SDave Gerlach 286afe761f8SDave Gerlach pm_sram = pm_ops->get_sram_addrs(); 287afe761f8SDave Gerlach if (!pm_sram) { 288afe761f8SDave Gerlach dev_err(dev, "PM: Cannot get PM asm function addresses!!\n"); 289afe761f8SDave Gerlach return -ENODEV; 290afe761f8SDave Gerlach } 291afe761f8SDave Gerlach 292afe761f8SDave Gerlach pm33xx_dev = dev; 293afe761f8SDave Gerlach 294afe761f8SDave Gerlach ret = am33xx_pm_alloc_sram(); 295afe761f8SDave Gerlach if (ret) 296afe761f8SDave Gerlach return ret; 297afe761f8SDave Gerlach 298afe761f8SDave Gerlach ret = am33xx_push_sram_idle(); 299afe761f8SDave Gerlach if (ret) 300afe761f8SDave Gerlach goto err_free_sram; 301afe761f8SDave Gerlach 302afe761f8SDave Gerlach m3_ipc = wkup_m3_ipc_get(); 303afe761f8SDave Gerlach if (!m3_ipc) { 304afe761f8SDave Gerlach dev_dbg(dev, "PM: Cannot get wkup_m3_ipc handle\n"); 305afe761f8SDave Gerlach ret = -EPROBE_DEFER; 306afe761f8SDave Gerlach goto err_free_sram; 307afe761f8SDave Gerlach } 308afe761f8SDave Gerlach 309afe761f8SDave Gerlach am33xx_pm_set_ipc_ops(); 310afe761f8SDave Gerlach 311afe761f8SDave Gerlach #ifdef CONFIG_SUSPEND 312afe761f8SDave Gerlach suspend_set_ops(&am33xx_pm_ops); 313afe761f8SDave Gerlach #endif /* CONFIG_SUSPEND */ 314afe761f8SDave Gerlach 31574655749SDave Gerlach /* 31674655749SDave Gerlach * For a system suspend we must flush the caches, we want 31774655749SDave Gerlach * the DDR in self-refresh, we want to save the context 31874655749SDave Gerlach * of the EMIF, and we want the wkup_m3 to handle low-power 31974655749SDave Gerlach * transition. 32074655749SDave Gerlach */ 32174655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_FLUSH_CACHE; 32274655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_SELF_REFRESH; 32374655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_SAVE_EMIF; 32474655749SDave Gerlach suspend_wfi_flags |= WFI_FLAG_WAKE_M3; 32574655749SDave Gerlach 326afe761f8SDave Gerlach ret = pm_ops->init(); 327afe761f8SDave Gerlach if (ret) { 328afe761f8SDave Gerlach dev_err(dev, "Unable to call core pm init!\n"); 329afe761f8SDave Gerlach ret = -ENODEV; 330afe761f8SDave Gerlach goto err_put_wkup_m3_ipc; 331afe761f8SDave Gerlach } 332afe761f8SDave Gerlach 333afe761f8SDave Gerlach return 0; 334afe761f8SDave Gerlach 335afe761f8SDave Gerlach err_put_wkup_m3_ipc: 336afe761f8SDave Gerlach wkup_m3_ipc_put(m3_ipc); 337afe761f8SDave Gerlach err_free_sram: 338afe761f8SDave Gerlach am33xx_pm_free_sram(); 339afe761f8SDave Gerlach pm33xx_dev = NULL; 340afe761f8SDave Gerlach return ret; 341afe761f8SDave Gerlach } 342afe761f8SDave Gerlach 343afe761f8SDave Gerlach static int am33xx_pm_remove(struct platform_device *pdev) 344afe761f8SDave Gerlach { 345afe761f8SDave Gerlach suspend_set_ops(NULL); 346afe761f8SDave Gerlach wkup_m3_ipc_put(m3_ipc); 347afe761f8SDave Gerlach am33xx_pm_free_sram(); 348afe761f8SDave Gerlach return 0; 349afe761f8SDave Gerlach } 350afe761f8SDave Gerlach 351afe761f8SDave Gerlach static struct platform_driver am33xx_pm_driver = { 352afe761f8SDave Gerlach .driver = { 353afe761f8SDave Gerlach .name = "pm33xx", 354afe761f8SDave Gerlach }, 355afe761f8SDave Gerlach .probe = am33xx_pm_probe, 356afe761f8SDave Gerlach .remove = am33xx_pm_remove, 357afe761f8SDave Gerlach }; 358afe761f8SDave Gerlach module_platform_driver(am33xx_pm_driver); 359afe761f8SDave Gerlach 360afe761f8SDave Gerlach MODULE_ALIAS("platform:pm33xx"); 361afe761f8SDave Gerlach MODULE_LICENSE("GPL v2"); 362afe761f8SDave Gerlach MODULE_DESCRIPTION("am33xx power management driver"); 363