diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-03-13 17:39:48 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-03-16 16:44:59 -0400 |
commit | 1e78a0c7fc92aee076965d516cf54475c39e9894 (patch) | |
tree | a1a47b1adee44700480ff35d3f85a5ae8fdae390 | |
parent | 65533bbf63b4f37723fdfedc73d0653958973323 (diff) |
PM / Domains: Introduce "always on" device flag
The TMU device on the Mackerel board belongs to the A4R power domain
and loses power when the domain is turned off. Unfortunately, the
TMU driver is not prepared to cope with such situations and crashes
the system when that happens. To work around this problem introduce
a new helper function, pm_genpd_dev_always_on(), allowing a device
driver to mark its device as "always on" in case it belongs to a PM
domain, which will make the generic PM domains core code avoid
powering off the domain containing the device, both at run time and
during system suspend.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Tested-by: Simon Horman <horms@verge.net.au>
Acked-by: Paul Mundt <lethal@linux-sh.org>
Cc: stable@vger.kernel.org
-rw-r--r-- | drivers/base/power/domain.c | 37 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 3 |
2 files changed, 34 insertions, 6 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 84f4beefa4f8..b6ff6ecf519d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -366,7 +366,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
366 | not_suspended = 0; | 366 | not_suspended = 0; |
367 | list_for_each_entry(pdd, &genpd->dev_list, list_node) | 367 | list_for_each_entry(pdd, &genpd->dev_list, list_node) |
368 | if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) | 368 | if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) |
369 | || pdd->dev->power.irq_safe)) | 369 | || pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on)) |
370 | not_suspended++; | 370 | not_suspended++; |
371 | 371 | ||
372 | if (not_suspended > genpd->in_progress) | 372 | if (not_suspended > genpd->in_progress) |
@@ -503,6 +503,9 @@ static int pm_genpd_runtime_suspend(struct device *dev) | |||
503 | 503 | ||
504 | might_sleep_if(!genpd->dev_irq_safe); | 504 | might_sleep_if(!genpd->dev_irq_safe); |
505 | 505 | ||
506 | if (dev_gpd_data(dev)->always_on) | ||
507 | return -EBUSY; | ||
508 | |||
506 | stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; | 509 | stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; |
507 | if (stop_ok && !stop_ok(dev)) | 510 | if (stop_ok && !stop_ok(dev)) |
508 | return -EBUSY; | 511 | return -EBUSY; |
@@ -859,7 +862,7 @@ static int pm_genpd_suspend_noirq(struct device *dev) | |||
859 | if (IS_ERR(genpd)) | 862 | if (IS_ERR(genpd)) |
860 | return -EINVAL; | 863 | return -EINVAL; |
861 | 864 | ||
862 | if (genpd->suspend_power_off | 865 | if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on |
863 | || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) | 866 | || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) |
864 | return 0; | 867 | return 0; |
865 | 868 | ||
@@ -892,7 +895,7 @@ static int pm_genpd_resume_noirq(struct device *dev) | |||
892 | if (IS_ERR(genpd)) | 895 | if (IS_ERR(genpd)) |
893 | return -EINVAL; | 896 | return -EINVAL; |
894 | 897 | ||
895 | if (genpd->suspend_power_off | 898 | if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on |
896 | || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) | 899 | || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) |
897 | return 0; | 900 | return 0; |
898 | 901 | ||
@@ -1012,7 +1015,8 @@ static int pm_genpd_freeze_noirq(struct device *dev) | |||
1012 | if (IS_ERR(genpd)) | 1015 | if (IS_ERR(genpd)) |
1013 | return -EINVAL; | 1016 | return -EINVAL; |
1014 | 1017 | ||
1015 | return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); | 1018 | return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ? |
1019 | 0 : genpd_stop_dev(genpd, dev); | ||
1016 | } | 1020 | } |
1017 | 1021 | ||
1018 | /** | 1022 | /** |
@@ -1032,7 +1036,8 @@ static int pm_genpd_thaw_noirq(struct device *dev) | |||
1032 | if (IS_ERR(genpd)) | 1036 | if (IS_ERR(genpd)) |
1033 | return -EINVAL; | 1037 | return -EINVAL; |
1034 | 1038 | ||
1035 | return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev); | 1039 | return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ? |
1040 | 0 : genpd_start_dev(genpd, dev); | ||
1036 | } | 1041 | } |
1037 | 1042 | ||
1038 | /** | 1043 | /** |
@@ -1124,7 +1129,7 @@ static int pm_genpd_restore_noirq(struct device *dev) | |||
1124 | 1129 | ||
1125 | pm_genpd_poweron(genpd); | 1130 | pm_genpd_poweron(genpd); |
1126 | 1131 | ||
1127 | return genpd_start_dev(genpd, dev); | 1132 | return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev); |
1128 | } | 1133 | } |
1129 | 1134 | ||
1130 | /** | 1135 | /** |
@@ -1320,6 +1325,26 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, | |||
1320 | } | 1325 | } |
1321 | 1326 | ||
1322 | /** | 1327 | /** |
1328 | * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device. | ||
1329 | * @dev: Device to set/unset the flag for. | ||
1330 | * @val: The new value of the device's "always on" flag. | ||
1331 | */ | ||
1332 | void pm_genpd_dev_always_on(struct device *dev, bool val) | ||
1333 | { | ||
1334 | struct pm_subsys_data *psd; | ||
1335 | unsigned long flags; | ||
1336 | |||
1337 | spin_lock_irqsave(&dev->power.lock, flags); | ||
1338 | |||
1339 | psd = dev_to_psd(dev); | ||
1340 | if (psd && psd->domain_data) | ||
1341 | to_gpd_data(psd->domain_data)->always_on = val; | ||
1342 | |||
1343 | spin_unlock_irqrestore(&dev->power.lock, flags); | ||
1344 | } | ||
1345 | EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on); | ||
1346 | |||
1347 | /** | ||
1323 | * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. | 1348 | * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. |
1324 | * @genpd: Master PM domain to add the subdomain to. | 1349 | * @genpd: Master PM domain to add the subdomain to. |
1325 | * @subdomain: Subdomain to be added. | 1350 | * @subdomain: Subdomain to be added. |
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 5c2bbc248c11..1236d262b3e8 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h | |||
@@ -99,6 +99,7 @@ struct generic_pm_domain_data { | |||
99 | struct gpd_dev_ops ops; | 99 | struct gpd_dev_ops ops; |
100 | struct gpd_timing_data td; | 100 | struct gpd_timing_data td; |
101 | bool need_restore; | 101 | bool need_restore; |
102 | bool always_on; | ||
102 | }; | 103 | }; |
103 | 104 | ||
104 | #ifdef CONFIG_PM_GENERIC_DOMAINS | 105 | #ifdef CONFIG_PM_GENERIC_DOMAINS |
@@ -137,6 +138,7 @@ static inline int pm_genpd_of_add_device(struct device_node *genpd_node, | |||
137 | 138 | ||
138 | extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, | 139 | extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, |
139 | struct device *dev); | 140 | struct device *dev); |
141 | extern void pm_genpd_dev_always_on(struct device *dev, bool val); | ||
140 | extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, | 142 | extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, |
141 | struct generic_pm_domain *new_subdomain); | 143 | struct generic_pm_domain *new_subdomain); |
142 | extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, | 144 | extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, |
@@ -179,6 +181,7 @@ static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd, | |||
179 | { | 181 | { |
180 | return -ENOSYS; | 182 | return -ENOSYS; |
181 | } | 183 | } |
184 | static inline void pm_genpd_dev_always_on(struct device *dev, bool val) {} | ||
182 | static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, | 185 | static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, |
183 | struct generic_pm_domain *new_sd) | 186 | struct generic_pm_domain *new_sd) |
184 | { | 187 | { |