root/arch/arm/mach-omap2/pm44xx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. omap4_pm_suspend
  2. pwrdms_setup
  3. omap_default_idle
  4. omap4plus_init_static_deps
  5. omap4_pm_init_early
  6. omap4_pm_init

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

/* [<][>][^][v][top][bottom][index][help] */