diff options
Diffstat (limited to 'drivers/base/power/domain.c')
-rw-r--r-- | drivers/base/power/domain.c | 113 |
1 files changed, 70 insertions, 43 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index fb83d4acd400..5d7b7548873a 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/pm_runtime.h> | 12 | #include <linux/pm_runtime.h> |
13 | #include <linux/pm_domain.h> | 13 | #include <linux/pm_domain.h> |
14 | #include <linux/pm_qos.h> | 14 | #include <linux/pm_qos.h> |
15 | #include <linux/pm_clock.h> | ||
15 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
16 | #include <linux/err.h> | 17 | #include <linux/err.h> |
17 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
@@ -151,6 +152,59 @@ static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) | |||
151 | genpd->cpuidle_data->idle_state->exit_latency = usecs64; | 152 | genpd->cpuidle_data->idle_state->exit_latency = usecs64; |
152 | } | 153 | } |
153 | 154 | ||
155 | static int genpd_power_on(struct generic_pm_domain *genpd) | ||
156 | { | ||
157 | ktime_t time_start; | ||
158 | s64 elapsed_ns; | ||
159 | int ret; | ||
160 | |||
161 | if (!genpd->power_on) | ||
162 | return 0; | ||
163 | |||
164 | time_start = ktime_get(); | ||
165 | ret = genpd->power_on(genpd); | ||
166 | if (ret) | ||
167 | return ret; | ||
168 | |||
169 | elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); | ||
170 | if (elapsed_ns <= genpd->power_on_latency_ns) | ||
171 | return ret; | ||
172 | |||
173 | genpd->power_on_latency_ns = elapsed_ns; | ||
174 | genpd->max_off_time_changed = true; | ||
175 | genpd_recalc_cpu_exit_latency(genpd); | ||
176 | pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", | ||
177 | genpd->name, "on", elapsed_ns); | ||
178 | |||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | static int genpd_power_off(struct generic_pm_domain *genpd) | ||
183 | { | ||
184 | ktime_t time_start; | ||
185 | s64 elapsed_ns; | ||
186 | int ret; | ||
187 | |||
188 | if (!genpd->power_off) | ||
189 | return 0; | ||
190 | |||
191 | time_start = ktime_get(); | ||
192 | ret = genpd->power_off(genpd); | ||
193 | if (ret == -EBUSY) | ||
194 | return ret; | ||
195 | |||
196 | elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); | ||
197 | if (elapsed_ns <= genpd->power_off_latency_ns) | ||
198 | return ret; | ||
199 | |||
200 | genpd->power_off_latency_ns = elapsed_ns; | ||
201 | genpd->max_off_time_changed = true; | ||
202 | pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", | ||
203 | genpd->name, "off", elapsed_ns); | ||
204 | |||
205 | return ret; | ||
206 | } | ||
207 | |||
154 | /** | 208 | /** |
155 | * __pm_genpd_poweron - Restore power to a given PM domain and its masters. | 209 | * __pm_genpd_poweron - Restore power to a given PM domain and its masters. |
156 | * @genpd: PM domain to power up. | 210 | * @genpd: PM domain to power up. |
@@ -222,25 +276,9 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
222 | } | 276 | } |
223 | } | 277 | } |
224 | 278 | ||
225 | if (genpd->power_on) { | 279 | ret = genpd_power_on(genpd); |
226 | ktime_t time_start = ktime_get(); | 280 | if (ret) |
227 | s64 elapsed_ns; | 281 | goto err; |
228 | |||
229 | ret = genpd->power_on(genpd); | ||
230 | if (ret) | ||
231 | goto err; | ||
232 | |||
233 | elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); | ||
234 | if (elapsed_ns > genpd->power_on_latency_ns) { | ||
235 | genpd->power_on_latency_ns = elapsed_ns; | ||
236 | genpd->max_off_time_changed = true; | ||
237 | genpd_recalc_cpu_exit_latency(genpd); | ||
238 | if (genpd->name) | ||
239 | pr_warning("%s: Power-on latency exceeded, " | ||
240 | "new value %lld ns\n", genpd->name, | ||
241 | elapsed_ns); | ||
242 | } | ||
243 | } | ||
244 | 282 | ||
245 | out: | 283 | out: |
246 | genpd_set_active(genpd); | 284 | genpd_set_active(genpd); |
@@ -544,16 +582,11 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
544 | } | 582 | } |
545 | 583 | ||
546 | if (genpd->power_off) { | 584 | if (genpd->power_off) { |
547 | ktime_t time_start; | ||
548 | s64 elapsed_ns; | ||
549 | |||
550 | if (atomic_read(&genpd->sd_count) > 0) { | 585 | if (atomic_read(&genpd->sd_count) > 0) { |
551 | ret = -EBUSY; | 586 | ret = -EBUSY; |
552 | goto out; | 587 | goto out; |
553 | } | 588 | } |
554 | 589 | ||
555 | time_start = ktime_get(); | ||
556 | |||
557 | /* | 590 | /* |
558 | * If sd_count > 0 at this point, one of the subdomains hasn't | 591 | * If sd_count > 0 at this point, one of the subdomains hasn't |
559 | * managed to call pm_genpd_poweron() for the master yet after | 592 | * managed to call pm_genpd_poweron() for the master yet after |
@@ -562,21 +595,11 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
562 | * the pm_genpd_poweron() restore power for us (this shouldn't | 595 | * the pm_genpd_poweron() restore power for us (this shouldn't |
563 | * happen very often). | 596 | * happen very often). |
564 | */ | 597 | */ |
565 | ret = genpd->power_off(genpd); | 598 | ret = genpd_power_off(genpd); |
566 | if (ret == -EBUSY) { | 599 | if (ret == -EBUSY) { |
567 | genpd_set_active(genpd); | 600 | genpd_set_active(genpd); |
568 | goto out; | 601 | goto out; |
569 | } | 602 | } |
570 | |||
571 | elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); | ||
572 | if (elapsed_ns > genpd->power_off_latency_ns) { | ||
573 | genpd->power_off_latency_ns = elapsed_ns; | ||
574 | genpd->max_off_time_changed = true; | ||
575 | if (genpd->name) | ||
576 | pr_warning("%s: Power-off latency exceeded, " | ||
577 | "new value %lld ns\n", genpd->name, | ||
578 | elapsed_ns); | ||
579 | } | ||
580 | } | 603 | } |
581 | 604 | ||
582 | genpd->status = GPD_STATE_POWER_OFF; | 605 | genpd->status = GPD_STATE_POWER_OFF; |
@@ -779,9 +802,9 @@ static inline void genpd_power_off_work_fn(struct work_struct *work) {} | |||
779 | * pm_genpd_present - Check if the given PM domain has been initialized. | 802 | * pm_genpd_present - Check if the given PM domain has been initialized. |
780 | * @genpd: PM domain to check. | 803 | * @genpd: PM domain to check. |
781 | */ | 804 | */ |
782 | static bool pm_genpd_present(struct generic_pm_domain *genpd) | 805 | static bool pm_genpd_present(const struct generic_pm_domain *genpd) |
783 | { | 806 | { |
784 | struct generic_pm_domain *gpd; | 807 | const struct generic_pm_domain *gpd; |
785 | 808 | ||
786 | if (IS_ERR_OR_NULL(genpd)) | 809 | if (IS_ERR_OR_NULL(genpd)) |
787 | return false; | 810 | return false; |
@@ -822,8 +845,7 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) | |||
822 | || atomic_read(&genpd->sd_count) > 0) | 845 | || atomic_read(&genpd->sd_count) > 0) |
823 | return; | 846 | return; |
824 | 847 | ||
825 | if (genpd->power_off) | 848 | genpd_power_off(genpd); |
826 | genpd->power_off(genpd); | ||
827 | 849 | ||
828 | genpd->status = GPD_STATE_POWER_OFF; | 850 | genpd->status = GPD_STATE_POWER_OFF; |
829 | 851 | ||
@@ -854,8 +876,7 @@ static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) | |||
854 | genpd_sd_counter_inc(link->master); | 876 | genpd_sd_counter_inc(link->master); |
855 | } | 877 | } |
856 | 878 | ||
857 | if (genpd->power_on) | 879 | genpd_power_on(genpd); |
858 | genpd->power_on(genpd); | ||
859 | 880 | ||
860 | genpd->status = GPD_STATE_ACTIVE; | 881 | genpd->status = GPD_STATE_ACTIVE; |
861 | } | 882 | } |
@@ -1277,8 +1298,7 @@ static int pm_genpd_restore_noirq(struct device *dev) | |||
1277 | * If the domain was off before the hibernation, make | 1298 | * If the domain was off before the hibernation, make |
1278 | * sure it will be off going forward. | 1299 | * sure it will be off going forward. |
1279 | */ | 1300 | */ |
1280 | if (genpd->power_off) | 1301 | genpd_power_off(genpd); |
1281 | genpd->power_off(genpd); | ||
1282 | 1302 | ||
1283 | return 0; | 1303 | return 0; |
1284 | } | 1304 | } |
@@ -1929,6 +1949,12 @@ void pm_genpd_init(struct generic_pm_domain *genpd, | |||
1929 | genpd->domain.ops.complete = pm_genpd_complete; | 1949 | genpd->domain.ops.complete = pm_genpd_complete; |
1930 | genpd->dev_ops.save_state = pm_genpd_default_save_state; | 1950 | genpd->dev_ops.save_state = pm_genpd_default_save_state; |
1931 | genpd->dev_ops.restore_state = pm_genpd_default_restore_state; | 1951 | genpd->dev_ops.restore_state = pm_genpd_default_restore_state; |
1952 | |||
1953 | if (genpd->flags & GENPD_FLAG_PM_CLK) { | ||
1954 | genpd->dev_ops.stop = pm_clk_suspend; | ||
1955 | genpd->dev_ops.start = pm_clk_resume; | ||
1956 | } | ||
1957 | |||
1932 | mutex_lock(&gpd_list_lock); | 1958 | mutex_lock(&gpd_list_lock); |
1933 | list_add(&genpd->gpd_list_node, &gpd_list); | 1959 | list_add(&genpd->gpd_list_node, &gpd_list); |
1934 | mutex_unlock(&gpd_list_lock); | 1960 | mutex_unlock(&gpd_list_lock); |
@@ -2216,6 +2242,7 @@ int genpd_dev_pm_attach(struct device *dev) | |||
2216 | } | 2242 | } |
2217 | 2243 | ||
2218 | dev->pm_domain->detach = genpd_dev_pm_detach; | 2244 | dev->pm_domain->detach = genpd_dev_pm_detach; |
2245 | pm_genpd_poweron(pd); | ||
2219 | 2246 | ||
2220 | return 0; | 2247 | return 0; |
2221 | } | 2248 | } |