linux/arch/arm/mach-shmobile/pm_runtime.c
<<
>>
Prefs
   1/*
   2 * arch/arm/mach-shmobile/pm_runtime.c
   3 *
   4 * Runtime PM support code for SuperH Mobile ARM
   5 *
   6 *  Copyright (C) 2009-2010 Magnus Damm
   7 *
   8 * This file is subject to the terms and conditions of the GNU General Public
   9 * License.  See the file "COPYING" in the main directory of this archive
  10 * for more details.
  11 */
  12
  13#include <linux/init.h>
  14#include <linux/kernel.h>
  15#include <linux/io.h>
  16#include <linux/pm_runtime.h>
  17#include <linux/platform_device.h>
  18#include <linux/clk.h>
  19#include <linux/sh_clk.h>
  20#include <linux/bitmap.h>
  21
  22#ifdef CONFIG_PM_RUNTIME
  23#define BIT_ONCE 0
  24#define BIT_ACTIVE 1
  25#define BIT_CLK_ENABLED 2
  26
  27struct pm_runtime_data {
  28        unsigned long flags;
  29        struct clk *clk;
  30};
  31
  32static void __devres_release(struct device *dev, void *res)
  33{
  34        struct pm_runtime_data *prd = res;
  35
  36        dev_dbg(dev, "__devres_release()\n");
  37
  38        if (test_bit(BIT_CLK_ENABLED, &prd->flags))
  39                clk_disable(prd->clk);
  40
  41        if (test_bit(BIT_ACTIVE, &prd->flags))
  42                clk_put(prd->clk);
  43}
  44
  45static struct pm_runtime_data *__to_prd(struct device *dev)
  46{
  47        return devres_find(dev, __devres_release, NULL, NULL);
  48}
  49
  50static void platform_pm_runtime_init(struct device *dev,
  51                                     struct pm_runtime_data *prd)
  52{
  53        if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags)) {
  54                prd->clk = clk_get(dev, NULL);
  55                if (!IS_ERR(prd->clk)) {
  56                        set_bit(BIT_ACTIVE, &prd->flags);
  57                        dev_info(dev, "clocks managed by runtime pm\n");
  58                }
  59        }
  60}
  61
  62static void platform_pm_runtime_bug(struct device *dev,
  63                                    struct pm_runtime_data *prd)
  64{
  65        if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags))
  66                dev_err(dev, "runtime pm suspend before resume\n");
  67}
  68
  69int platform_pm_runtime_suspend(struct device *dev)
  70{
  71        struct pm_runtime_data *prd = __to_prd(dev);
  72
  73        dev_dbg(dev, "platform_pm_runtime_suspend()\n");
  74
  75        platform_pm_runtime_bug(dev, prd);
  76
  77        if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
  78                clk_disable(prd->clk);
  79                clear_bit(BIT_CLK_ENABLED, &prd->flags);
  80        }
  81
  82        return 0;
  83}
  84
  85int platform_pm_runtime_resume(struct device *dev)
  86{
  87        struct pm_runtime_data *prd = __to_prd(dev);
  88
  89        dev_dbg(dev, "platform_pm_runtime_resume()\n");
  90
  91        platform_pm_runtime_init(dev, prd);
  92
  93        if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
  94                clk_enable(prd->clk);
  95                set_bit(BIT_CLK_ENABLED, &prd->flags);
  96        }
  97
  98        return 0;
  99}
 100
 101int platform_pm_runtime_idle(struct device *dev)
 102{
 103        /* suspend synchronously to disable clocks immediately */
 104        return pm_runtime_suspend(dev);
 105}
 106
 107static int platform_bus_notify(struct notifier_block *nb,
 108                               unsigned long action, void *data)
 109{
 110        struct device *dev = data;
 111        struct pm_runtime_data *prd;
 112
 113        dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
 114
 115        if (action == BUS_NOTIFY_BIND_DRIVER) {
 116                prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
 117                if (prd)
 118                        devres_add(dev, prd);
 119                else
 120                        dev_err(dev, "unable to alloc memory for runtime pm\n");
 121        }
 122
 123        return 0;
 124}
 125
 126#else /* CONFIG_PM_RUNTIME */
 127
 128static int platform_bus_notify(struct notifier_block *nb,
 129                               unsigned long action, void *data)
 130{
 131        struct device *dev = data;
 132        struct clk *clk;
 133
 134        dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
 135
 136        switch (action) {
 137        case BUS_NOTIFY_BIND_DRIVER:
 138                clk = clk_get(dev, NULL);
 139                if (!IS_ERR(clk)) {
 140                        clk_enable(clk);
 141                        clk_put(clk);
 142                        dev_info(dev, "runtime pm disabled, clock forced on\n");
 143                }
 144                break;
 145        case BUS_NOTIFY_UNBOUND_DRIVER:
 146                clk = clk_get(dev, NULL);
 147                if (!IS_ERR(clk)) {
 148                        clk_disable(clk);
 149                        clk_put(clk);
 150                        dev_info(dev, "runtime pm disabled, clock forced off\n");
 151                }
 152                break;
 153        }
 154
 155        return 0;
 156}
 157
 158#endif /* CONFIG_PM_RUNTIME */
 159
 160static struct notifier_block platform_bus_notifier = {
 161        .notifier_call = platform_bus_notify
 162};
 163
 164static int __init sh_pm_runtime_init(void)
 165{
 166        bus_register_notifier(&platform_bus_type, &platform_bus_notifier);
 167        return 0;
 168}
 169core_initcall(sh_pm_runtime_init);
 170