1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * AM33XX Arch Power Management Routines 4 * 5 * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ 6 * Dave Gerlach 7 */ 8 9 #include <asm/smp_scu.h> 10 #include <asm/suspend.h> 11 #include <linux/errno.h> 12 #include <linux/platform_data/pm33xx.h> 13 14 #include "cm33xx.h" 15 #include "common.h" 16 #include "control.h" 17 #include "clockdomain.h" 18 #include "iomap.h" 19 #include "omap_hwmod.h" 20 #include "pm.h" 21 #include "powerdomain.h" 22 #include "prm33xx.h" 23 #include "soc.h" 24 #include "sram.h" 25 26 static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm; 27 static struct clockdomain *gfx_l4ls_clkdm; 28 static void __iomem *scu_base; 29 30 static int __init am43xx_map_scu(void) 31 { 32 scu_base = ioremap(scu_a9_get_base(), SZ_256); 33 34 if (!scu_base) 35 return -ENOMEM; 36 37 return 0; 38 } 39 40 static int amx3_common_init(void) 41 { 42 gfx_pwrdm = pwrdm_lookup("gfx_pwrdm"); 43 per_pwrdm = pwrdm_lookup("per_pwrdm"); 44 mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); 45 46 if ((!gfx_pwrdm) || (!per_pwrdm) || (!mpu_pwrdm)) 47 return -ENODEV; 48 49 (void)clkdm_for_each(omap_pm_clkdms_setup, NULL); 50 51 /* CEFUSE domain can be turned off post bootup */ 52 cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm"); 53 if (cefuse_pwrdm) 54 omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF); 55 else 56 pr_err("PM: Failed to get cefuse_pwrdm\n"); 57 58 return 0; 59 } 60 61 static int am33xx_suspend_init(void) 62 { 63 int ret; 64 65 gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm"); 66 67 if (!gfx_l4ls_clkdm) { 68 pr_err("PM: Cannot lookup gfx_l4ls_clkdm clockdomains\n"); 69 return -ENODEV; 70 } 71 72 ret = amx3_common_init(); 73 74 return ret; 75 } 76 77 static int am43xx_suspend_init(void) 78 { 79 int ret = 0; 80 81 ret = am43xx_map_scu(); 82 if (ret) { 83 pr_err("PM: Could not ioremap SCU\n"); 84 return ret; 85 } 86 87 ret = amx3_common_init(); 88 89 return ret; 90 } 91 92 static void amx3_pre_suspend_common(void) 93 { 94 omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF); 95 } 96 97 static void amx3_post_suspend_common(void) 98 { 99 int status; 100 /* 101 * Because gfx_pwrdm is the only one under MPU control, 102 * comment on transition status 103 */ 104 status = pwrdm_read_pwrst(gfx_pwrdm); 105 if (status != PWRDM_POWER_OFF) 106 pr_err("PM: GFX domain did not transition: %x\n", status); 107 } 108 109 static int am33xx_suspend(unsigned int state, int (*fn)(unsigned long)) 110 { 111 int ret = 0; 112 113 amx3_pre_suspend_common(); 114 ret = cpu_suspend(0, fn); 115 amx3_post_suspend_common(); 116 117 /* 118 * BUG: GFX_L4LS clock domain needs to be woken up to 119 * ensure thet L4LS clock domain does not get stuck in 120 * transition. If that happens L3 module does not get 121 * disabled, thereby leading to PER power domain 122 * transition failing 123 */ 124 125 clkdm_wakeup(gfx_l4ls_clkdm); 126 clkdm_sleep(gfx_l4ls_clkdm); 127 128 return ret; 129 } 130 131 static int am43xx_suspend(unsigned int state, int (*fn)(unsigned long)) 132 { 133 int ret = 0; 134 135 amx3_pre_suspend_common(); 136 scu_power_mode(scu_base, SCU_PM_POWEROFF); 137 ret = cpu_suspend(0, fn); 138 scu_power_mode(scu_base, SCU_PM_NORMAL); 139 amx3_post_suspend_common(); 140 141 return ret; 142 } 143 144 static struct am33xx_pm_sram_addr *amx3_get_sram_addrs(void) 145 { 146 if (soc_is_am33xx()) 147 return &am33xx_pm_sram; 148 else if (soc_is_am437x()) 149 return &am43xx_pm_sram; 150 else 151 return NULL; 152 } 153 154 static struct am33xx_pm_platform_data am33xx_ops = { 155 .init = am33xx_suspend_init, 156 .soc_suspend = am33xx_suspend, 157 .get_sram_addrs = amx3_get_sram_addrs, 158 }; 159 160 static struct am33xx_pm_platform_data am43xx_ops = { 161 .init = am43xx_suspend_init, 162 .soc_suspend = am43xx_suspend, 163 .get_sram_addrs = amx3_get_sram_addrs, 164 }; 165 166 static struct am33xx_pm_platform_data *am33xx_pm_get_pdata(void) 167 { 168 if (soc_is_am33xx()) 169 return &am33xx_ops; 170 else if (soc_is_am437x()) 171 return &am43xx_ops; 172 else 173 return NULL; 174 } 175 176 int __init amx3_common_pm_init(void) 177 { 178 struct am33xx_pm_platform_data *pdata; 179 struct platform_device_info devinfo; 180 181 pdata = am33xx_pm_get_pdata(); 182 183 memset(&devinfo, 0, sizeof(devinfo)); 184 devinfo.name = "pm33xx"; 185 devinfo.data = pdata; 186 devinfo.size_data = sizeof(*pdata); 187 devinfo.id = -1; 188 platform_device_register_full(&devinfo); 189 190 return 0; 191 } 192