diff options
-rw-r--r-- | arch/arm/mach-shmobile/pm-sh7372.c | 4 | ||||
-rw-r--r-- | drivers/base/power/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/power/domain.c | 33 | ||||
-rw-r--r-- | drivers/base/power/domain_governor.c | 33 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 63 |
5 files changed, 118 insertions, 17 deletions
diff --git a/arch/arm/mach-shmobile/pm-sh7372.c b/arch/arm/mach-shmobile/pm-sh7372.c index 6777bb1be059..adf1765e69c6 100644 --- a/arch/arm/mach-shmobile/pm-sh7372.c +++ b/arch/arm/mach-shmobile/pm-sh7372.c | |||
@@ -169,6 +169,7 @@ static bool sh7372_power_down_forbidden(struct dev_pm_domain *domain) | |||
169 | 169 | ||
170 | struct dev_power_governor sh7372_always_on_gov = { | 170 | struct dev_power_governor sh7372_always_on_gov = { |
171 | .power_down_ok = sh7372_power_down_forbidden, | 171 | .power_down_ok = sh7372_power_down_forbidden, |
172 | .stop_ok = default_stop_ok, | ||
172 | }; | 173 | }; |
173 | 174 | ||
174 | static int sh7372_stop_dev(struct device *dev) | 175 | static int sh7372_stop_dev(struct device *dev) |
@@ -203,8 +204,9 @@ static int sh7372_start_dev(struct device *dev) | |||
203 | void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) | 204 | void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) |
204 | { | 205 | { |
205 | struct generic_pm_domain *genpd = &sh7372_pd->genpd; | 206 | struct generic_pm_domain *genpd = &sh7372_pd->genpd; |
207 | struct dev_power_governor *gov = sh7372_pd->gov; | ||
206 | 208 | ||
207 | pm_genpd_init(genpd, sh7372_pd->gov, false); | 209 | pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); |
208 | genpd->dev_ops.stop = sh7372_stop_dev; | 210 | genpd->dev_ops.stop = sh7372_stop_dev; |
209 | genpd->dev_ops.start = sh7372_start_dev; | 211 | genpd->dev_ops.start = sh7372_start_dev; |
210 | genpd->dev_ops.active_wakeup = pd_active_wakeup; | 212 | genpd->dev_ops.active_wakeup = pd_active_wakeup; |
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 81676dd17900..2e58ebb1f6c0 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile | |||
@@ -3,7 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o | |||
3 | obj-$(CONFIG_PM_RUNTIME) += runtime.o | 3 | obj-$(CONFIG_PM_RUNTIME) += runtime.o |
4 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o | 4 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o |
5 | obj-$(CONFIG_PM_OPP) += opp.o | 5 | obj-$(CONFIG_PM_OPP) += opp.o |
6 | obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o | 6 | obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o |
7 | obj-$(CONFIG_HAVE_CLK) += clock_ops.o | 7 | obj-$(CONFIG_HAVE_CLK) += clock_ops.o |
8 | 8 | ||
9 | ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG | 9 | ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG |
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 9a77080cb799..3af9f5a71ad5 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -38,7 +38,7 @@ static DEFINE_MUTEX(gpd_list_lock); | |||
38 | 38 | ||
39 | #ifdef CONFIG_PM | 39 | #ifdef CONFIG_PM |
40 | 40 | ||
41 | static struct generic_pm_domain *dev_to_genpd(struct device *dev) | 41 | struct generic_pm_domain *dev_to_genpd(struct device *dev) |
42 | { | 42 | { |
43 | if (IS_ERR_OR_NULL(dev->pm_domain)) | 43 | if (IS_ERR_OR_NULL(dev->pm_domain)) |
44 | return ERR_PTR(-EINVAL); | 44 | return ERR_PTR(-EINVAL); |
@@ -436,6 +436,7 @@ static void genpd_power_off_work_fn(struct work_struct *work) | |||
436 | static int pm_genpd_runtime_suspend(struct device *dev) | 436 | static int pm_genpd_runtime_suspend(struct device *dev) |
437 | { | 437 | { |
438 | struct generic_pm_domain *genpd; | 438 | struct generic_pm_domain *genpd; |
439 | bool (*stop_ok)(struct device *__dev); | ||
439 | int ret; | 440 | int ret; |
440 | 441 | ||
441 | dev_dbg(dev, "%s()\n", __func__); | 442 | dev_dbg(dev, "%s()\n", __func__); |
@@ -446,10 +447,17 @@ static int pm_genpd_runtime_suspend(struct device *dev) | |||
446 | 447 | ||
447 | might_sleep_if(!genpd->dev_irq_safe); | 448 | might_sleep_if(!genpd->dev_irq_safe); |
448 | 449 | ||
450 | stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; | ||
451 | if (stop_ok && !stop_ok(dev)) | ||
452 | return -EBUSY; | ||
453 | |||
449 | ret = genpd_stop_dev(genpd, dev); | 454 | ret = genpd_stop_dev(genpd, dev); |
450 | if (ret) | 455 | if (ret) |
451 | return ret; | 456 | return ret; |
452 | 457 | ||
458 | pm_runtime_update_max_time_suspended(dev, | ||
459 | dev_gpd_data(dev)->td.start_latency_ns); | ||
460 | |||
453 | /* | 461 | /* |
454 | * If power.irq_safe is set, this routine will be run with interrupts | 462 | * If power.irq_safe is set, this routine will be run with interrupts |
455 | * off, so it can't use mutexes. | 463 | * off, so it can't use mutexes. |
@@ -1048,11 +1056,13 @@ static void pm_genpd_complete(struct device *dev) | |||
1048 | #endif /* CONFIG_PM_SLEEP */ | 1056 | #endif /* CONFIG_PM_SLEEP */ |
1049 | 1057 | ||
1050 | /** | 1058 | /** |
1051 | * pm_genpd_add_device - Add a device to an I/O PM domain. | 1059 | * __pm_genpd_add_device - Add a device to an I/O PM domain. |
1052 | * @genpd: PM domain to add the device to. | 1060 | * @genpd: PM domain to add the device to. |
1053 | * @dev: Device to be added. | 1061 | * @dev: Device to be added. |
1062 | * @td: Set of PM QoS timing parameters to attach to the device. | ||
1054 | */ | 1063 | */ |
1055 | int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) | 1064 | int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, |
1065 | struct gpd_timing_data *td) | ||
1056 | { | 1066 | { |
1057 | struct generic_pm_domain_data *gpd_data; | 1067 | struct generic_pm_domain_data *gpd_data; |
1058 | struct pm_domain_data *pdd; | 1068 | struct pm_domain_data *pdd; |
@@ -1095,6 +1105,8 @@ int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) | |||
1095 | gpd_data->base.dev = dev; | 1105 | gpd_data->base.dev = dev; |
1096 | gpd_data->need_restore = false; | 1106 | gpd_data->need_restore = false; |
1097 | list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); | 1107 | list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); |
1108 | if (td) | ||
1109 | gpd_data->td = *td; | ||
1098 | 1110 | ||
1099 | out: | 1111 | out: |
1100 | genpd_release_lock(genpd); | 1112 | genpd_release_lock(genpd); |
@@ -1255,8 +1267,10 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, | |||
1255 | * pm_genpd_add_callbacks - Add PM domain callbacks to a given device. | 1267 | * pm_genpd_add_callbacks - Add PM domain callbacks to a given device. |
1256 | * @dev: Device to add the callbacks to. | 1268 | * @dev: Device to add the callbacks to. |
1257 | * @ops: Set of callbacks to add. | 1269 | * @ops: Set of callbacks to add. |
1270 | * @td: Timing data to add to the device along with the callbacks (optional). | ||
1258 | */ | 1271 | */ |
1259 | int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops) | 1272 | int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, |
1273 | struct gpd_timing_data *td) | ||
1260 | { | 1274 | { |
1261 | struct pm_domain_data *pdd; | 1275 | struct pm_domain_data *pdd; |
1262 | int ret = 0; | 1276 | int ret = 0; |
@@ -1272,6 +1286,8 @@ int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops) | |||
1272 | struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); | 1286 | struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); |
1273 | 1287 | ||
1274 | gpd_data->ops = *ops; | 1288 | gpd_data->ops = *ops; |
1289 | if (td) | ||
1290 | gpd_data->td = *td; | ||
1275 | } else { | 1291 | } else { |
1276 | ret = -EINVAL; | 1292 | ret = -EINVAL; |
1277 | } | 1293 | } |
@@ -1284,10 +1300,11 @@ int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops) | |||
1284 | EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); | 1300 | EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); |
1285 | 1301 | ||
1286 | /** | 1302 | /** |
1287 | * pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. | 1303 | * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. |
1288 | * @dev: Device to remove the callbacks from. | 1304 | * @dev: Device to remove the callbacks from. |
1305 | * @clear_td: If set, clear the device's timing data too. | ||
1289 | */ | 1306 | */ |
1290 | int pm_genpd_remove_callbacks(struct device *dev) | 1307 | int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) |
1291 | { | 1308 | { |
1292 | struct pm_domain_data *pdd; | 1309 | struct pm_domain_data *pdd; |
1293 | int ret = 0; | 1310 | int ret = 0; |
@@ -1303,6 +1320,8 @@ int pm_genpd_remove_callbacks(struct device *dev) | |||
1303 | struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); | 1320 | struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); |
1304 | 1321 | ||
1305 | gpd_data->ops = (struct gpd_dev_ops){ 0 }; | 1322 | gpd_data->ops = (struct gpd_dev_ops){ 0 }; |
1323 | if (clear_td) | ||
1324 | gpd_data->td = (struct gpd_timing_data){ 0 }; | ||
1306 | } else { | 1325 | } else { |
1307 | ret = -EINVAL; | 1326 | ret = -EINVAL; |
1308 | } | 1327 | } |
@@ -1312,7 +1331,7 @@ int pm_genpd_remove_callbacks(struct device *dev) | |||
1312 | 1331 | ||
1313 | return ret; | 1332 | return ret; |
1314 | } | 1333 | } |
1315 | EXPORT_SYMBOL_GPL(pm_genpd_remove_callbacks); | 1334 | EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); |
1316 | 1335 | ||
1317 | /* Default device callbacks for generic PM domains. */ | 1336 | /* Default device callbacks for generic PM domains. */ |
1318 | 1337 | ||
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c new file mode 100644 index 000000000000..97b21c10c496 --- /dev/null +++ b/drivers/base/power/domain_governor.c | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * drivers/base/power/domain_governor.c - Governors for device PM domains. | ||
3 | * | ||
4 | * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. | ||
5 | * | ||
6 | * This file is released under the GPLv2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/pm_domain.h> | ||
12 | #include <linux/pm_qos.h> | ||
13 | |||
14 | /** | ||
15 | * default_stop_ok - Default PM domain governor routine for stopping devices. | ||
16 | * @dev: Device to check. | ||
17 | */ | ||
18 | bool default_stop_ok(struct device *dev) | ||
19 | { | ||
20 | struct gpd_timing_data *td = &dev_gpd_data(dev)->td; | ||
21 | |||
22 | dev_dbg(dev, "%s()\n", __func__); | ||
23 | |||
24 | if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0) | ||
25 | return true; | ||
26 | |||
27 | return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns | ||
28 | && td->break_even_ns < dev->power.max_time_suspended_ns; | ||
29 | } | ||
30 | |||
31 | struct dev_power_governor simple_qos_governor = { | ||
32 | .stop_ok = default_stop_ok, | ||
33 | }; | ||
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 10a197dce07e..f6745c213a57 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h | |||
@@ -21,6 +21,7 @@ enum gpd_status { | |||
21 | 21 | ||
22 | struct dev_power_governor { | 22 | struct dev_power_governor { |
23 | bool (*power_down_ok)(struct dev_pm_domain *domain); | 23 | bool (*power_down_ok)(struct dev_pm_domain *domain); |
24 | bool (*stop_ok)(struct device *dev); | ||
24 | }; | 25 | }; |
25 | 26 | ||
26 | struct gpd_dev_ops { | 27 | struct gpd_dev_ops { |
@@ -76,9 +77,16 @@ struct gpd_link { | |||
76 | struct list_head slave_node; | 77 | struct list_head slave_node; |
77 | }; | 78 | }; |
78 | 79 | ||
80 | struct gpd_timing_data { | ||
81 | s64 stop_latency_ns; | ||
82 | s64 start_latency_ns; | ||
83 | s64 break_even_ns; | ||
84 | }; | ||
85 | |||
79 | struct generic_pm_domain_data { | 86 | struct generic_pm_domain_data { |
80 | struct pm_domain_data base; | 87 | struct pm_domain_data base; |
81 | struct gpd_dev_ops ops; | 88 | struct gpd_dev_ops ops; |
89 | struct gpd_timing_data td; | ||
82 | bool need_restore; | 90 | bool need_restore; |
83 | }; | 91 | }; |
84 | 92 | ||
@@ -93,20 +101,48 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) | |||
93 | return to_gpd_data(dev->power.subsys_data->domain_data); | 101 | return to_gpd_data(dev->power.subsys_data->domain_data); |
94 | } | 102 | } |
95 | 103 | ||
96 | extern int pm_genpd_add_device(struct generic_pm_domain *genpd, | 104 | extern struct dev_power_governor simple_qos_governor; |
97 | struct device *dev); | 105 | |
106 | extern struct generic_pm_domain *dev_to_genpd(struct device *dev); | ||
107 | extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, | ||
108 | struct device *dev, | ||
109 | struct gpd_timing_data *td); | ||
110 | |||
111 | static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, | ||
112 | struct device *dev) | ||
113 | { | ||
114 | return __pm_genpd_add_device(genpd, dev, NULL); | ||
115 | } | ||
116 | |||
98 | extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, | 117 | extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, |
99 | struct device *dev); | 118 | struct device *dev); |
100 | extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, | 119 | extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, |
101 | struct generic_pm_domain *new_subdomain); | 120 | struct generic_pm_domain *new_subdomain); |
102 | extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, | 121 | extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, |
103 | struct generic_pm_domain *target); | 122 | struct generic_pm_domain *target); |
104 | extern int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops); | 123 | extern int pm_genpd_add_callbacks(struct device *dev, |
105 | extern int pm_genpd_remove_callbacks(struct device *dev); | 124 | struct gpd_dev_ops *ops, |
125 | struct gpd_timing_data *td); | ||
126 | extern int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td); | ||
106 | extern void pm_genpd_init(struct generic_pm_domain *genpd, | 127 | extern void pm_genpd_init(struct generic_pm_domain *genpd, |
107 | struct dev_power_governor *gov, bool is_off); | 128 | struct dev_power_governor *gov, bool is_off); |
129 | |||
108 | extern int pm_genpd_poweron(struct generic_pm_domain *genpd); | 130 | extern int pm_genpd_poweron(struct generic_pm_domain *genpd); |
131 | |||
132 | extern bool default_stop_ok(struct device *dev); | ||
133 | |||
109 | #else | 134 | #else |
135 | |||
136 | static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) | ||
137 | { | ||
138 | return ERR_PTR(-ENOSYS); | ||
139 | } | ||
140 | static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, | ||
141 | struct device *dev, | ||
142 | struct gpd_timing_data *td) | ||
143 | { | ||
144 | return -ENOSYS; | ||
145 | } | ||
110 | static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, | 146 | static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, |
111 | struct device *dev) | 147 | struct device *dev) |
112 | { | 148 | { |
@@ -128,22 +164,33 @@ static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, | |||
128 | return -ENOSYS; | 164 | return -ENOSYS; |
129 | } | 165 | } |
130 | static inline int pm_genpd_add_callbacks(struct device *dev, | 166 | static inline int pm_genpd_add_callbacks(struct device *dev, |
131 | struct gpd_dev_ops *ops) | 167 | struct gpd_dev_ops *ops, |
168 | struct gpd_timing_data *td) | ||
132 | { | 169 | { |
133 | return -ENOSYS; | 170 | return -ENOSYS; |
134 | } | 171 | } |
135 | static inline int pm_genpd_remove_callbacks(struct device *dev) | 172 | static inline int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) |
136 | { | 173 | { |
137 | return -ENOSYS; | 174 | return -ENOSYS; |
138 | } | 175 | } |
139 | static inline void pm_genpd_init(struct generic_pm_domain *genpd, | 176 | static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off) |
140 | struct dev_power_governor *gov, bool is_off) {} | 177 | { |
178 | } | ||
141 | static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) | 179 | static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) |
142 | { | 180 | { |
143 | return -ENOSYS; | 181 | return -ENOSYS; |
144 | } | 182 | } |
183 | static inline bool default_stop_ok(struct device *dev) | ||
184 | { | ||
185 | return false; | ||
186 | } | ||
145 | #endif | 187 | #endif |
146 | 188 | ||
189 | static inline int pm_genpd_remove_callbacks(struct device *dev) | ||
190 | { | ||
191 | return __pm_genpd_remove_callbacks(dev, true); | ||
192 | } | ||
193 | |||
147 | #ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME | 194 | #ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME |
148 | extern void genpd_queue_power_off_work(struct generic_pm_domain *genpd); | 195 | extern void genpd_queue_power_off_work(struct generic_pm_domain *genpd); |
149 | extern void pm_genpd_poweroff_unused(void); | 196 | extern void pm_genpd_poweroff_unused(void); |