blob: 30bbe9a99ae16e4910ab19546da35f6ff85d9e00 [file] [log] [blame]
Magnus Dammf14c4f12010-07-27 08:14:35 +00001/*
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>
Rafael J. Wysocki1d2b71f2011-04-29 00:36:53 +020021#include <linux/slab.h>
Magnus Dammf14c4f12010-07-27 08:14:35 +000022
23#ifdef CONFIG_PM_RUNTIME
24#define BIT_ONCE 0
25#define BIT_ACTIVE 1
26#define BIT_CLK_ENABLED 2
27
28struct pm_runtime_data {
29 unsigned long flags;
30 struct clk *clk;
31};
32
Magnus Dammf14c4f12010-07-27 08:14:35 +000033static struct pm_runtime_data *__to_prd(struct device *dev)
34{
Rafael J. Wysocki1d2b71f2011-04-29 00:36:53 +020035 return dev ? dev->power.subsys_data : NULL;
Magnus Dammf14c4f12010-07-27 08:14:35 +000036}
37
38static void platform_pm_runtime_init(struct device *dev,
39 struct pm_runtime_data *prd)
40{
41 if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags)) {
42 prd->clk = clk_get(dev, NULL);
43 if (!IS_ERR(prd->clk)) {
44 set_bit(BIT_ACTIVE, &prd->flags);
45 dev_info(dev, "clocks managed by runtime pm\n");
46 }
47 }
48}
49
50static void platform_pm_runtime_bug(struct device *dev,
51 struct pm_runtime_data *prd)
52{
53 if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags))
54 dev_err(dev, "runtime pm suspend before resume\n");
55}
56
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +020057static int default_platform_runtime_suspend(struct device *dev)
Magnus Dammf14c4f12010-07-27 08:14:35 +000058{
59 struct pm_runtime_data *prd = __to_prd(dev);
60
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +020061 dev_dbg(dev, "%s()\n", __func__);
Magnus Dammf14c4f12010-07-27 08:14:35 +000062
63 platform_pm_runtime_bug(dev, prd);
64
65 if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
66 clk_disable(prd->clk);
67 clear_bit(BIT_CLK_ENABLED, &prd->flags);
68 }
69
70 return 0;
71}
72
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +020073static int default_platform_runtime_resume(struct device *dev)
Magnus Dammf14c4f12010-07-27 08:14:35 +000074{
75 struct pm_runtime_data *prd = __to_prd(dev);
76
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +020077 dev_dbg(dev, "%s()\n", __func__);
Magnus Dammf14c4f12010-07-27 08:14:35 +000078
79 platform_pm_runtime_init(dev, prd);
80
81 if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
82 clk_enable(prd->clk);
83 set_bit(BIT_CLK_ENABLED, &prd->flags);
84 }
85
86 return 0;
87}
88
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +020089static int default_platform_runtime_idle(struct device *dev)
Magnus Dammf14c4f12010-07-27 08:14:35 +000090{
91 /* suspend synchronously to disable clocks immediately */
92 return pm_runtime_suspend(dev);
93}
94
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +020095static struct dev_power_domain default_power_domain = {
96 .ops = {
97 .runtime_suspend = default_platform_runtime_suspend,
98 .runtime_resume = default_platform_runtime_resume,
99 .runtime_idle = default_platform_runtime_idle,
100 USE_PLATFORM_PM_SLEEP_OPS
101 },
102};
103
Magnus Dammf14c4f12010-07-27 08:14:35 +0000104static int platform_bus_notify(struct notifier_block *nb,
105 unsigned long action, void *data)
106{
107 struct device *dev = data;
108 struct pm_runtime_data *prd;
109
110 dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
111
Rafael J. Wysocki1d2b71f2011-04-29 00:36:53 +0200112 switch (action) {
113 case BUS_NOTIFY_BIND_DRIVER:
114 prd = kzalloc(sizeof(*prd), GFP_KERNEL);
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +0200115 if (prd) {
Rafael J. Wysocki1d2b71f2011-04-29 00:36:53 +0200116 dev->power.subsys_data = prd;
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +0200117 dev->pwr_domain = &default_power_domain;
118 } else {
Magnus Dammf14c4f12010-07-27 08:14:35 +0000119 dev_err(dev, "unable to alloc memory for runtime pm\n");
Rafael J. Wysocki38ade3a2011-04-29 00:36:21 +0200120 }
Rafael J. Wysocki1d2b71f2011-04-29 00:36:53 +0200121 break;
122 case BUS_NOTIFY_UNBOUND_DRIVER:
123 prd = __to_prd(dev);
124 if (prd) {
125 if (test_bit(BIT_CLK_ENABLED, &prd->flags))
126 clk_disable(prd->clk);
127
128 if (test_bit(BIT_ACTIVE, &prd->flags))
129 clk_put(prd->clk);
130 }
131 break;
Magnus Dammf14c4f12010-07-27 08:14:35 +0000132 }
133
134 return 0;
135}
136
137#else /* CONFIG_PM_RUNTIME */
138
139static int platform_bus_notify(struct notifier_block *nb,
140 unsigned long action, void *data)
141{
142 struct device *dev = data;
143 struct clk *clk;
144
145 dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
146
147 switch (action) {
148 case BUS_NOTIFY_BIND_DRIVER:
149 clk = clk_get(dev, NULL);
150 if (!IS_ERR(clk)) {
151 clk_enable(clk);
152 clk_put(clk);
153 dev_info(dev, "runtime pm disabled, clock forced on\n");
154 }
155 break;
156 case BUS_NOTIFY_UNBOUND_DRIVER:
157 clk = clk_get(dev, NULL);
158 if (!IS_ERR(clk)) {
159 clk_disable(clk);
160 clk_put(clk);
161 dev_info(dev, "runtime pm disabled, clock forced off\n");
162 }
163 break;
164 }
165
166 return 0;
167}
168
169#endif /* CONFIG_PM_RUNTIME */
170
171static struct notifier_block platform_bus_notifier = {
172 .notifier_call = platform_bus_notify
173};
174
175static int __init sh_pm_runtime_init(void)
176{
177 bus_register_notifier(&platform_bus_type, &platform_bus_notifier);
178 return 0;
179}
180core_initcall(sh_pm_runtime_init);