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