diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-11-30 18:02:05 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-12-01 15:47:40 -0500 |
commit | b02c999ac325e977585abeb4caf6e0a2ee21e30b (patch) | |
tree | 1ddec5f020a80b9df443b03ffb578ef7f882abb6 /drivers/base | |
parent | d23b9b00cdde5c93b914a172cecd57d5625fcd04 (diff) |
PM / Domains: Add device stop governor function (v4)
Add a function deciding whether or not devices should be stopped in
pm_genpd_runtime_suspend() depending on their PM QoS constraints
and stop/start timing values. Make it possible to add information
used by this function to device objects.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Magnus Damm <damm@opensource.se>
Diffstat (limited to 'drivers/base')
-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 |
3 files changed, 60 insertions, 8 deletions
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 | }; | ||