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