1 /* 2 * OMAP4+ Power Management Routines 3 * 4 * Copyright (C) 2010-2013 Texas Instruments, Inc. 5 * Rajendra Nayak <rnayak@ti.com> 6 * Santosh Shilimkar <santosh.shilimkar@ti.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/pm.h> 14 #include <linux/suspend.h> 15 #include <linux/module.h> 16 #include <linux/list.h> 17 #include <linux/err.h> 18 #include <linux/slab.h> 19 #include <asm/system_misc.h> 20 21 #include "soc.h" 22 #include "common.h" 23 #include "clockdomain.h" 24 #include "powerdomain.h" 25 #include "pm.h" 26 27 u16 pm44xx_errata; 28 29 struct power_state { 30 struct powerdomain *pwrdm; 31 u32 next_state; 32 #ifdef CONFIG_SUSPEND 33 u32 saved_state; 34 u32 saved_logic_state; 35 #endif 36 struct list_head node; 37 }; 38 39 static LIST_HEAD(pwrst_list); 40 41 #ifdef CONFIG_SUSPEND 42 static int omap4_pm_suspend(void) 43 { 44 struct power_state *pwrst; 45 int state, ret = 0; 46 u32 cpu_id = smp_processor_id(); 47 48 /* Save current powerdomain state */ 49 list_for_each_entry(pwrst, &pwrst_list, node) { 50 pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm); 51 pwrst->saved_logic_state = pwrdm_read_logic_retst(pwrst->pwrdm); 52 } 53 54 /* Set targeted power domain states by suspend */ 55 list_for_each_entry(pwrst, &pwrst_list, node) { 56 omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); 57 pwrdm_set_logic_retst(pwrst->pwrdm, PWRDM_POWER_OFF); 58 } 59 60 /* 61 * For MPUSS to hit power domain retention(CSWR or OSWR), 62 * CPU0 and CPU1 power domains need to be in OFF or DORMANT state, 63 * since CPU power domain CSWR is not supported by hardware 64 * Only master CPU follows suspend path. All other CPUs follow 65 * CPU hotplug path in system wide suspend. On OMAP4, CPU power 66 * domain CSWR is not supported by hardware. 67 * More details can be found in OMAP4430 TRM section 4.3.4.2. 68 */ 69 omap4_enter_lowpower(cpu_id, PWRDM_POWER_OFF); 70 71 /* Restore next powerdomain state */ 72 list_for_each_entry(pwrst, &pwrst_list, node) { 73 state = pwrdm_read_prev_pwrst(pwrst->pwrdm); 74 if (state > pwrst->next_state) { 75 pr_info("Powerdomain (%s) didn't enter target state %d\n", 76 pwrst->pwrdm->name, pwrst->next_state); 77 ret = -1; 78 } 79 omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); 80 pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->saved_logic_state); 81 } 82 if (ret) { 83 pr_crit("Could not enter target state in pm_suspend\n"); 84 /* 85 * OMAP4 chip PM currently works only with certain (newer) 86 * versions of bootloaders. This is due to missing code in the 87 * kernel to properly reset and initialize some devices. 88 * Warn the user about the bootloader version being one of the 89 * possible causes. 90 * http://www.spinics.net/lists/arm-kernel/msg218641.html 91 */ 92 pr_warn("A possible cause could be an old bootloader - try u-boot >= v2012.07\n"); 93 } else { 94 pr_info("Successfully put all powerdomains to target state\n"); 95 } 96 97 return 0; 98 } 99 #endif /* CONFIG_SUSPEND */ 100 101 static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) 102 { 103 struct power_state *pwrst; 104 105 if (!pwrdm->pwrsts) 106 return 0; 107 108 /* 109 * Skip CPU0 and CPU1 power domains. CPU1 is programmed 110 * through hotplug path and CPU0 explicitly programmed 111 * further down in the code path 112 */ 113 if (!strncmp(pwrdm->name, "cpu", 3)) 114 return 0; 115 116 pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC); 117 if (!pwrst) 118 return -ENOMEM; 119 120 pwrst->pwrdm = pwrdm; 121 pwrst->next_state = PWRDM_POWER_RET; 122 list_add(&pwrst->node, &pwrst_list); 123 124 return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); 125 } 126 127 /** 128 * omap_default_idle - OMAP4 default ilde routine.' 129 * 130 * Implements OMAP4 memory, IO ordering requirements which can't be addressed 131 * with default cpu_do_idle() hook. Used by all CPUs with !CONFIG_CPU_IDLE and 132 * by secondary CPU with CONFIG_CPU_IDLE. 133 */ 134 static void omap_default_idle(void) 135 { 136 omap_do_wfi(); 137 } 138 139 /** 140 * omap4_init_static_deps - Add OMAP4 static dependencies 141 * 142 * Add needed static clockdomain dependencies on OMAP4 devices. 143 * Return: 0 on success or 'err' on failures 144 */ 145 static inline int omap4_init_static_deps(void) 146 { 147 struct clockdomain *emif_clkdm, *mpuss_clkdm, *l3_1_clkdm; 148 struct clockdomain *ducati_clkdm, *l3_2_clkdm; 149 int ret = 0; 150 151 if (omap_rev() == OMAP4430_REV_ES1_0) { 152 WARN(1, "Power Management not supported on OMAP4430 ES1.0\n"); 153 return -ENODEV; 154 } 155 156 pr_err("Power Management for TI OMAP4.\n"); 157 /* 158 * OMAP4 chip PM currently works only with certain (newer) 159 * versions of bootloaders. This is due to missing code in the 160 * kernel to properly reset and initialize some devices. 161 * http://www.spinics.net/lists/arm-kernel/msg218641.html 162 */ 163 pr_warn("OMAP4 PM: u-boot >= v2012.07 is required for full PM support\n"); 164 165 ret = pwrdm_for_each(pwrdms_setup, NULL); 166 if (ret) { 167 pr_err("Failed to setup powerdomains\n"); 168 return ret; 169 } 170 171 /* 172 * The dynamic dependency between MPUSS -> MEMIF and 173 * MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as 174 * expected. The hardware recommendation is to enable static 175 * dependencies for these to avoid system lock ups or random crashes. 176 * The L4 wakeup depedency is added to workaround the OCP sync hardware 177 * BUG with 32K synctimer which lead to incorrect timer value read 178 * from the 32K counter. The BUG applies for GPTIMER1 and WDT2 which 179 * are part of L4 wakeup clockdomain. 180 */ 181 mpuss_clkdm = clkdm_lookup("mpuss_clkdm"); 182 emif_clkdm = clkdm_lookup("l3_emif_clkdm"); 183 l3_1_clkdm = clkdm_lookup("l3_1_clkdm"); 184 l3_2_clkdm = clkdm_lookup("l3_2_clkdm"); 185 ducati_clkdm = clkdm_lookup("ducati_clkdm"); 186 if ((!mpuss_clkdm) || (!emif_clkdm) || (!l3_1_clkdm) || 187 (!l3_2_clkdm) || (!ducati_clkdm)) 188 return -EINVAL; 189 190 ret = clkdm_add_wkdep(mpuss_clkdm, emif_clkdm); 191 ret |= clkdm_add_wkdep(mpuss_clkdm, l3_1_clkdm); 192 ret |= clkdm_add_wkdep(mpuss_clkdm, l3_2_clkdm); 193 ret |= clkdm_add_wkdep(ducati_clkdm, l3_1_clkdm); 194 ret |= clkdm_add_wkdep(ducati_clkdm, l3_2_clkdm); 195 if (ret) { 196 pr_err("Failed to add MPUSS -> L3/EMIF/L4PER, DUCATI -> L3 wakeup dependency\n"); 197 return -EINVAL; 198 } 199 200 return ret; 201 } 202 203 /** 204 * omap4_pm_init_early - Does early initialization necessary for OMAP4+ devices 205 * 206 * Initializes basic stuff for power management functionality. 207 */ 208 int __init omap4_pm_init_early(void) 209 { 210 if (cpu_is_omap446x()) 211 pm44xx_errata |= PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD; 212 213 return 0; 214 } 215 216 /** 217 * omap4_pm_init - Init routine for OMAP4+ devices 218 * 219 * Initializes all powerdomain and clockdomain target states 220 * and all PRCM settings. 221 * Return: Returns the error code returned by called functions. 222 */ 223 int __init omap4_pm_init(void) 224 { 225 int ret = 0; 226 227 if (omap_rev() == OMAP4430_REV_ES1_0) { 228 WARN(1, "Power Management not supported on OMAP4430 ES1.0\n"); 229 return -ENODEV; 230 } 231 232 pr_info("Power Management for TI OMAP4+ devices.\n"); 233 234 ret = pwrdm_for_each(pwrdms_setup, NULL); 235 if (ret) { 236 pr_err("Failed to setup powerdomains.\n"); 237 goto err2; 238 } 239 240 if (cpu_is_omap44xx()) { 241 ret = omap4_init_static_deps(); 242 if (ret) 243 goto err2; 244 } 245 246 ret = omap4_mpuss_init(); 247 if (ret) { 248 pr_err("Failed to initialise OMAP4 MPUSS\n"); 249 goto err2; 250 } 251 252 (void) clkdm_for_each(omap_pm_clkdms_setup, NULL); 253 254 #ifdef CONFIG_SUSPEND 255 omap_pm_suspend = omap4_pm_suspend; 256 #endif 257 258 /* Overwrite the default cpu_do_idle() */ 259 arm_pm_idle = omap_default_idle; 260 261 if (cpu_is_omap44xx()) 262 omap4_idle_init(); 263 264 err2: 265 return ret; 266 } 267