]>
Commit | Line | Data |
---|---|---|
f14c4f14 MD |
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> | |
1d2b71f6 | 21 | #include <linux/slab.h> |
f14c4f14 MD |
22 | |
23 | #ifdef CONFIG_PM_RUNTIME | |
24 | #define BIT_ONCE 0 | |
25 | #define BIT_ACTIVE 1 | |
26 | #define BIT_CLK_ENABLED 2 | |
27 | ||
28 | struct pm_runtime_data { | |
29 | unsigned long flags; | |
30 | struct clk *clk; | |
31 | }; | |
32 | ||
f14c4f14 MD |
33 | static struct pm_runtime_data *__to_prd(struct device *dev) |
34 | { | |
1d2b71f6 | 35 | return dev ? dev->power.subsys_data : NULL; |
f14c4f14 MD |
36 | } |
37 | ||
38 | static 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 | ||
50 | static 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 | ||
38ade3a1 | 57 | static int default_platform_runtime_suspend(struct device *dev) |
f14c4f14 MD |
58 | { |
59 | struct pm_runtime_data *prd = __to_prd(dev); | |
60 | ||
38ade3a1 | 61 | dev_dbg(dev, "%s()\n", __func__); |
f14c4f14 MD |
62 | |
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 | ||
38ade3a1 | 73 | static int default_platform_runtime_resume(struct device *dev) |
f14c4f14 MD |
74 | { |
75 | struct pm_runtime_data *prd = __to_prd(dev); | |
76 | ||
38ade3a1 | 77 | dev_dbg(dev, "%s()\n", __func__); |
f14c4f14 MD |
78 | |
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 | ||
38ade3a1 | 89 | static int default_platform_runtime_idle(struct device *dev) |
f14c4f14 MD |
90 | { |
91 | /* suspend synchronously to disable clocks immediately */ | |
92 | return pm_runtime_suspend(dev); | |
93 | } | |
94 | ||
38ade3a1 RW |
95 | static 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 | ||
f14c4f14 MD |
104 | static 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 | ||
1d2b71f6 RW |
112 | switch (action) { |
113 | case BUS_NOTIFY_BIND_DRIVER: | |
114 | prd = kzalloc(sizeof(*prd), GFP_KERNEL); | |
38ade3a1 | 115 | if (prd) { |
1d2b71f6 | 116 | dev->power.subsys_data = prd; |
38ade3a1 RW |
117 | dev->pwr_domain = &default_power_domain; |
118 | } else { | |
f14c4f14 | 119 | dev_err(dev, "unable to alloc memory for runtime pm\n"); |
38ade3a1 | 120 | } |
1d2b71f6 RW |
121 | 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; | |
f14c4f14 MD |
132 | } |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | #else /* CONFIG_PM_RUNTIME */ | |
138 | ||
139 | static 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 | ||
171 | static struct notifier_block platform_bus_notifier = { | |
172 | .notifier_call = platform_bus_notify | |
173 | }; | |
174 | ||
175 | static int __init sh_pm_runtime_init(void) | |
176 | { | |
177 | bus_register_notifier(&platform_bus_type, &platform_bus_notifier); | |
178 | return 0; | |
179 | } | |
180 | core_initcall(sh_pm_runtime_init); |