linux/arch/arm/mach-omap2/pm.c
<<
>>
Prefs
   1/*
   2 * pm.c - Common OMAP2+ power management-related code
   3 *
   4 * Copyright (C) 2010 Texas Instruments, Inc.
   5 * Copyright (C) 2010 Nokia Corporation
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/init.h>
  14#include <linux/io.h>
  15#include <linux/err.h>
  16#include <linux/pm_opp.h>
  17#include <linux/export.h>
  18#include <linux/suspend.h>
  19#include <linux/cpu.h>
  20
  21#include <asm/system_misc.h>
  22
  23#include "omap-pm.h"
  24#include "omap_device.h"
  25#include "common.h"
  26
  27#include "soc.h"
  28#include "prcm-common.h"
  29#include "voltage.h"
  30#include "powerdomain.h"
  31#include "clockdomain.h"
  32#include "pm.h"
  33#include "twl-common.h"
  34
  35#ifdef CONFIG_SUSPEND
  36/*
  37 * omap_pm_suspend: points to a function that does the SoC-specific
  38 * suspend work
  39 */
  40static int (*omap_pm_suspend)(void);
  41#endif
  42
  43#ifdef CONFIG_PM
  44/**
  45 * struct omap2_oscillator - Describe the board main oscillator latencies
  46 * @startup_time: oscillator startup latency
  47 * @shutdown_time: oscillator shutdown latency
  48 */
  49struct omap2_oscillator {
  50        u32 startup_time;
  51        u32 shutdown_time;
  52};
  53
  54static struct omap2_oscillator oscillator = {
  55        .startup_time = ULONG_MAX,
  56        .shutdown_time = ULONG_MAX,
  57};
  58
  59void omap_pm_setup_oscillator(u32 tstart, u32 tshut)
  60{
  61        oscillator.startup_time = tstart;
  62        oscillator.shutdown_time = tshut;
  63}
  64
  65void omap_pm_get_oscillator(u32 *tstart, u32 *tshut)
  66{
  67        if (!tstart || !tshut)
  68                return;
  69
  70        *tstart = oscillator.startup_time;
  71        *tshut = oscillator.shutdown_time;
  72}
  73#endif
  74
  75static int __init _init_omap_device(char *name)
  76{
  77        struct omap_hwmod *oh;
  78        struct platform_device *pdev;
  79
  80        oh = omap_hwmod_lookup(name);
  81        if (WARN(!oh, "%s: could not find omap_hwmod for %s\n",
  82                 __func__, name))
  83                return -ENODEV;
  84
  85        pdev = omap_device_build(oh->name, 0, oh, NULL, 0);
  86        if (WARN(IS_ERR(pdev), "%s: could not build omap_device for %s\n",
  87                 __func__, name))
  88                return -ENODEV;
  89
  90        return 0;
  91}
  92
  93/*
  94 * Build omap_devices for processors and bus.
  95 */
  96static void __init omap2_init_processor_devices(void)
  97{
  98        _init_omap_device("mpu");
  99        if (omap3_has_iva())
 100                _init_omap_device("iva");
 101
 102        if (cpu_is_omap44xx()) {
 103                _init_omap_device("l3_main_1");
 104                _init_omap_device("dsp");
 105                _init_omap_device("iva");
 106        } else {
 107                _init_omap_device("l3_main");
 108        }
 109}
 110
 111int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused)
 112{
 113        clkdm_allow_idle(clkdm);
 114        return 0;
 115}
 116
 117/*
 118 * This API is to be called during init to set the various voltage
 119 * domains to the voltage as per the opp table. Typically we boot up
 120 * at the nominal voltage. So this function finds out the rate of
 121 * the clock associated with the voltage domain, finds out the correct
 122 * opp entry and sets the voltage domain to the voltage specified
 123 * in the opp entry
 124 */
 125static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name,
 126                                         const char *oh_name)
 127{
 128        struct voltagedomain *voltdm;
 129        struct clk *clk;
 130        struct dev_pm_opp *opp;
 131        unsigned long freq, bootup_volt;
 132        struct device *dev;
 133
 134        if (!vdd_name || !clk_name || !oh_name) {
 135                pr_err("%s: invalid parameters\n", __func__);
 136                goto exit;
 137        }
 138
 139        if (!strncmp(oh_name, "mpu", 3))
 140                /* 
 141                 * All current OMAPs share voltage rail and clock
 142                 * source, so CPU0 is used to represent the MPU-SS.
 143                 */
 144                dev = get_cpu_device(0);
 145        else
 146                dev = omap_device_get_by_hwmod_name(oh_name);
 147
 148        if (IS_ERR(dev)) {
 149                pr_err("%s: Unable to get dev pointer for hwmod %s\n",
 150                        __func__, oh_name);
 151                goto exit;
 152        }
 153
 154        voltdm = voltdm_lookup(vdd_name);
 155        if (!voltdm) {
 156                pr_err("%s: unable to get vdd pointer for vdd_%s\n",
 157                        __func__, vdd_name);
 158                goto exit;
 159        }
 160
 161        clk =  clk_get(NULL, clk_name);
 162        if (IS_ERR(clk)) {
 163                pr_err("%s: unable to get clk %s\n", __func__, clk_name);
 164                goto exit;
 165        }
 166
 167        freq = clk_get_rate(clk);
 168        clk_put(clk);
 169
 170        rcu_read_lock();
 171        opp = dev_pm_opp_find_freq_ceil(dev, &freq);
 172        if (IS_ERR(opp)) {
 173                rcu_read_unlock();
 174                pr_err("%s: unable to find boot up OPP for vdd_%s\n",
 175                        __func__, vdd_name);
 176                goto exit;
 177        }
 178
 179        bootup_volt = dev_pm_opp_get_voltage(opp);
 180        rcu_read_unlock();
 181        if (!bootup_volt) {
 182                pr_err("%s: unable to find voltage corresponding to the bootup OPP for vdd_%s\n",
 183                       __func__, vdd_name);
 184                goto exit;
 185        }
 186
 187        voltdm_scale(voltdm, bootup_volt);
 188        return 0;
 189
 190exit:
 191        pr_err("%s: unable to set vdd_%s\n", __func__, vdd_name);
 192        return -EINVAL;
 193}
 194
 195#ifdef CONFIG_SUSPEND
 196static int omap_pm_enter(suspend_state_t suspend_state)
 197{
 198        int ret = 0;
 199
 200        if (!omap_pm_suspend)
 201                return -ENOENT; /* XXX doublecheck */
 202
 203        switch (suspend_state) {
 204        case PM_SUSPEND_STANDBY:
 205        case PM_SUSPEND_MEM:
 206                ret = omap_pm_suspend();
 207                break;
 208        default:
 209                ret = -EINVAL;
 210        }
 211
 212        return ret;
 213}
 214
 215static int omap_pm_begin(suspend_state_t state)
 216{
 217        cpu_idle_poll_ctrl(true);
 218        if (cpu_is_omap34xx())
 219                omap_prcm_irq_prepare();
 220        return 0;
 221}
 222
 223static void omap_pm_end(void)
 224{
 225        cpu_idle_poll_ctrl(false);
 226}
 227
 228static void omap_pm_finish(void)
 229{
 230        if (cpu_is_omap34xx())
 231                omap_prcm_irq_complete();
 232}
 233
 234static const struct platform_suspend_ops omap_pm_ops = {
 235        .begin          = omap_pm_begin,
 236        .end            = omap_pm_end,
 237        .enter          = omap_pm_enter,
 238        .finish         = omap_pm_finish,
 239        .valid          = suspend_valid_only_mem,
 240};
 241
 242/**
 243 * omap_common_suspend_init - Set common suspend routines for OMAP SoCs
 244 * @pm_suspend: function pointer to SoC specific suspend function
 245 */
 246void omap_common_suspend_init(void *pm_suspend)
 247{
 248        omap_pm_suspend = pm_suspend;
 249        suspend_set_ops(&omap_pm_ops);
 250}
 251#endif /* CONFIG_SUSPEND */
 252
 253static void __init omap3_init_voltages(void)
 254{
 255        if (!cpu_is_omap34xx())
 256                return;
 257
 258        omap2_set_init_voltage("mpu_iva", "dpll1_ck", "mpu");
 259        omap2_set_init_voltage("core", "l3_ick", "l3_main");
 260}
 261
 262static void __init omap4_init_voltages(void)
 263{
 264        if (!cpu_is_omap44xx())
 265                return;
 266
 267        omap2_set_init_voltage("mpu", "dpll_mpu_ck", "mpu");
 268        omap2_set_init_voltage("core", "l3_div_ck", "l3_main_1");
 269        omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", "iva");
 270}
 271
 272static inline void omap_init_cpufreq(void)
 273{
 274        struct platform_device_info devinfo = { .name = "omap-cpufreq" };
 275
 276        if (!of_have_populated_dt())
 277                platform_device_register_full(&devinfo);
 278}
 279
 280static int __init omap2_common_pm_init(void)
 281{
 282        if (!of_have_populated_dt())
 283                omap2_init_processor_devices();
 284        omap_pm_if_init();
 285
 286        return 0;
 287}
 288omap_postcore_initcall(omap2_common_pm_init);
 289
 290int __init omap2_common_pm_late_init(void)
 291{
 292        if (of_have_populated_dt()) {
 293                omap3_twl_init();
 294                omap4_twl_init();
 295        }
 296
 297        /* Init the voltage layer */
 298        omap_pmic_late_init();
 299        omap_voltage_late_init();
 300
 301        /* Initialize the voltages */
 302        omap3_init_voltages();
 303        omap4_init_voltages();
 304
 305        /* Smartreflex device init */
 306        omap_devinit_smartreflex();
 307
 308        /* cpufreq dummy device instantiation */
 309        omap_init_cpufreq();
 310
 311        return 0;
 312}
 313