aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-03-13 17:39:48 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2012-03-16 16:44:59 -0400
commit1e78a0c7fc92aee076965d516cf54475c39e9894 (patch)
treea1a47b1adee44700480ff35d3f85a5ae8fdae390
parent65533bbf63b4f37723fdfedc73d0653958973323 (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.c37
-rw-r--r--include/linux/pm_domain.h3
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 */
1332void 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}
1345EXPORT_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
138extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, 139extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
139 struct device *dev); 140 struct device *dev);
141extern void pm_genpd_dev_always_on(struct device *dev, bool val);
140extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 142extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
141 struct generic_pm_domain *new_subdomain); 143 struct generic_pm_domain *new_subdomain);
142extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 144extern 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}
184static inline void pm_genpd_dev_always_on(struct device *dev, bool val) {}
182static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 185static 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{