diff options
Diffstat (limited to 'drivers/base')
| -rw-r--r-- | drivers/base/cpu.c | 1 | ||||
| -rw-r--r-- | drivers/base/power/clock_ops.c | 13 | ||||
| -rw-r--r-- | drivers/base/power/main.c | 11 | ||||
| -rw-r--r-- | drivers/base/power/runtime.c | 70 | ||||
| -rw-r--r-- | drivers/base/power/sysfs.c | 17 |
5 files changed, 84 insertions, 28 deletions
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index eb9443d5bae1..6ce93a52bf3f 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c | |||
| @@ -427,6 +427,7 @@ __cpu_device_create(struct device *parent, void *drvdata, | |||
| 427 | dev->parent = parent; | 427 | dev->parent = parent; |
| 428 | dev->groups = groups; | 428 | dev->groups = groups; |
| 429 | dev->release = device_create_release; | 429 | dev->release = device_create_release; |
| 430 | device_set_pm_not_required(dev); | ||
| 430 | dev_set_drvdata(dev, drvdata); | 431 | dev_set_drvdata(dev, drvdata); |
| 431 | 432 | ||
| 432 | retval = kobject_set_name_vargs(&dev->kobj, fmt, args); | 433 | retval = kobject_set_name_vargs(&dev->kobj, fmt, args); |
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 5a42ae4078c2..365ad751ce0f 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c | |||
| @@ -65,10 +65,15 @@ static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) | |||
| 65 | if (IS_ERR(ce->clk)) { | 65 | if (IS_ERR(ce->clk)) { |
| 66 | ce->status = PCE_STATUS_ERROR; | 66 | ce->status = PCE_STATUS_ERROR; |
| 67 | } else { | 67 | } else { |
| 68 | clk_prepare(ce->clk); | 68 | if (clk_prepare(ce->clk)) { |
| 69 | ce->status = PCE_STATUS_ACQUIRED; | 69 | ce->status = PCE_STATUS_ERROR; |
| 70 | dev_dbg(dev, "Clock %pC con_id %s managed by runtime PM.\n", | 70 | dev_err(dev, "clk_prepare() failed\n"); |
| 71 | ce->clk, ce->con_id); | 71 | } else { |
| 72 | ce->status = PCE_STATUS_ACQUIRED; | ||
| 73 | dev_dbg(dev, | ||
| 74 | "Clock %pC con_id %s managed by runtime PM.\n", | ||
| 75 | ce->clk, ce->con_id); | ||
| 76 | } | ||
| 72 | } | 77 | } |
| 73 | } | 78 | } |
| 74 | 79 | ||
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0992e67e862b..893ae464bfd6 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
| @@ -124,6 +124,10 @@ void device_pm_unlock(void) | |||
| 124 | */ | 124 | */ |
| 125 | void device_pm_add(struct device *dev) | 125 | void device_pm_add(struct device *dev) |
| 126 | { | 126 | { |
| 127 | /* Skip PM setup/initialization. */ | ||
| 128 | if (device_pm_not_required(dev)) | ||
| 129 | return; | ||
| 130 | |||
| 127 | pr_debug("PM: Adding info for %s:%s\n", | 131 | pr_debug("PM: Adding info for %s:%s\n", |
| 128 | dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); | 132 | dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); |
| 129 | device_pm_check_callbacks(dev); | 133 | device_pm_check_callbacks(dev); |
| @@ -142,6 +146,9 @@ void device_pm_add(struct device *dev) | |||
| 142 | */ | 146 | */ |
| 143 | void device_pm_remove(struct device *dev) | 147 | void device_pm_remove(struct device *dev) |
| 144 | { | 148 | { |
| 149 | if (device_pm_not_required(dev)) | ||
| 150 | return; | ||
| 151 | |||
| 145 | pr_debug("PM: Removing info for %s:%s\n", | 152 | pr_debug("PM: Removing info for %s:%s\n", |
| 146 | dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); | 153 | dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); |
| 147 | complete_all(&dev->power.completion); | 154 | complete_all(&dev->power.completion); |
| @@ -1741,8 +1748,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
| 1741 | if (dev->power.direct_complete) { | 1748 | if (dev->power.direct_complete) { |
| 1742 | if (pm_runtime_status_suspended(dev)) { | 1749 | if (pm_runtime_status_suspended(dev)) { |
| 1743 | pm_runtime_disable(dev); | 1750 | pm_runtime_disable(dev); |
| 1744 | if (pm_runtime_status_suspended(dev)) | 1751 | if (pm_runtime_status_suspended(dev)) { |
| 1752 | pm_dev_dbg(dev, state, "direct-complete "); | ||
| 1745 | goto Complete; | 1753 | goto Complete; |
| 1754 | } | ||
| 1746 | 1755 | ||
| 1747 | pm_runtime_enable(dev); | 1756 | pm_runtime_enable(dev); |
| 1748 | } | 1757 | } |
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index ccd296dbb95c..78937c45278c 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
| @@ -66,20 +66,30 @@ static int rpm_suspend(struct device *dev, int rpmflags); | |||
| 66 | */ | 66 | */ |
| 67 | void update_pm_runtime_accounting(struct device *dev) | 67 | void update_pm_runtime_accounting(struct device *dev) |
| 68 | { | 68 | { |
| 69 | unsigned long now = jiffies; | 69 | u64 now, last, delta; |
| 70 | unsigned long delta; | ||
| 71 | 70 | ||
| 72 | delta = now - dev->power.accounting_timestamp; | 71 | if (dev->power.disable_depth > 0) |
| 72 | return; | ||
| 73 | |||
| 74 | last = dev->power.accounting_timestamp; | ||
| 73 | 75 | ||
| 76 | now = ktime_get_mono_fast_ns(); | ||
| 74 | dev->power.accounting_timestamp = now; | 77 | dev->power.accounting_timestamp = now; |
| 75 | 78 | ||
| 76 | if (dev->power.disable_depth > 0) | 79 | /* |
| 80 | * Because ktime_get_mono_fast_ns() is not monotonic during | ||
| 81 | * timekeeping updates, ensure that 'now' is after the last saved | ||
| 82 | * timesptamp. | ||
| 83 | */ | ||
| 84 | if (now < last) | ||
| 77 | return; | 85 | return; |
| 78 | 86 | ||
| 87 | delta = now - last; | ||
| 88 | |||
| 79 | if (dev->power.runtime_status == RPM_SUSPENDED) | 89 | if (dev->power.runtime_status == RPM_SUSPENDED) |
| 80 | dev->power.suspended_jiffies += delta; | 90 | dev->power.suspended_time += delta; |
| 81 | else | 91 | else |
| 82 | dev->power.active_jiffies += delta; | 92 | dev->power.active_time += delta; |
| 83 | } | 93 | } |
| 84 | 94 | ||
| 85 | static void __update_runtime_status(struct device *dev, enum rpm_status status) | 95 | static void __update_runtime_status(struct device *dev, enum rpm_status status) |
| @@ -88,6 +98,22 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status) | |||
| 88 | dev->power.runtime_status = status; | 98 | dev->power.runtime_status = status; |
| 89 | } | 99 | } |
| 90 | 100 | ||
| 101 | u64 pm_runtime_suspended_time(struct device *dev) | ||
| 102 | { | ||
| 103 | u64 time; | ||
| 104 | unsigned long flags; | ||
| 105 | |||
| 106 | spin_lock_irqsave(&dev->power.lock, flags); | ||
| 107 | |||
| 108 | update_pm_runtime_accounting(dev); | ||
| 109 | time = dev->power.suspended_time; | ||
| 110 | |||
| 111 | spin_unlock_irqrestore(&dev->power.lock, flags); | ||
| 112 | |||
| 113 | return time; | ||
| 114 | } | ||
| 115 | EXPORT_SYMBOL_GPL(pm_runtime_suspended_time); | ||
| 116 | |||
| 91 | /** | 117 | /** |
| 92 | * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. | 118 | * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. |
| 93 | * @dev: Device to handle. | 119 | * @dev: Device to handle. |
| @@ -129,24 +155,21 @@ static void pm_runtime_cancel_pending(struct device *dev) | |||
| 129 | u64 pm_runtime_autosuspend_expiration(struct device *dev) | 155 | u64 pm_runtime_autosuspend_expiration(struct device *dev) |
| 130 | { | 156 | { |
| 131 | int autosuspend_delay; | 157 | int autosuspend_delay; |
| 132 | u64 last_busy, expires = 0; | 158 | u64 expires; |
| 133 | u64 now = ktime_get_mono_fast_ns(); | ||
| 134 | 159 | ||
| 135 | if (!dev->power.use_autosuspend) | 160 | if (!dev->power.use_autosuspend) |
| 136 | goto out; | 161 | return 0; |
| 137 | 162 | ||
| 138 | autosuspend_delay = READ_ONCE(dev->power.autosuspend_delay); | 163 | autosuspend_delay = READ_ONCE(dev->power.autosuspend_delay); |
| 139 | if (autosuspend_delay < 0) | 164 | if (autosuspend_delay < 0) |
| 140 | goto out; | 165 | return 0; |
| 141 | |||
| 142 | last_busy = READ_ONCE(dev->power.last_busy); | ||
| 143 | 166 | ||
| 144 | expires = last_busy + (u64)autosuspend_delay * NSEC_PER_MSEC; | 167 | expires = READ_ONCE(dev->power.last_busy); |
| 145 | if (expires <= now) | 168 | expires += (u64)autosuspend_delay * NSEC_PER_MSEC; |
| 146 | expires = 0; /* Already expired. */ | 169 | if (expires > ktime_get_mono_fast_ns()) |
| 170 | return expires; /* Expires in the future */ | ||
| 147 | 171 | ||
| 148 | out: | 172 | return 0; |
| 149 | return expires; | ||
| 150 | } | 173 | } |
| 151 | EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); | 174 | EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); |
| 152 | 175 | ||
| @@ -1276,6 +1299,9 @@ void __pm_runtime_disable(struct device *dev, bool check_resume) | |||
| 1276 | pm_runtime_put_noidle(dev); | 1299 | pm_runtime_put_noidle(dev); |
| 1277 | } | 1300 | } |
| 1278 | 1301 | ||
| 1302 | /* Update time accounting before disabling PM-runtime. */ | ||
| 1303 | update_pm_runtime_accounting(dev); | ||
| 1304 | |||
| 1279 | if (!dev->power.disable_depth++) | 1305 | if (!dev->power.disable_depth++) |
| 1280 | __pm_runtime_barrier(dev); | 1306 | __pm_runtime_barrier(dev); |
| 1281 | 1307 | ||
| @@ -1294,10 +1320,15 @@ void pm_runtime_enable(struct device *dev) | |||
| 1294 | 1320 | ||
| 1295 | spin_lock_irqsave(&dev->power.lock, flags); | 1321 | spin_lock_irqsave(&dev->power.lock, flags); |
| 1296 | 1322 | ||
| 1297 | if (dev->power.disable_depth > 0) | 1323 | if (dev->power.disable_depth > 0) { |
| 1298 | dev->power.disable_depth--; | 1324 | dev->power.disable_depth--; |
| 1299 | else | 1325 | |
| 1326 | /* About to enable runtime pm, set accounting_timestamp to now */ | ||
| 1327 | if (!dev->power.disable_depth) | ||
| 1328 | dev->power.accounting_timestamp = ktime_get_mono_fast_ns(); | ||
| 1329 | } else { | ||
| 1300 | dev_warn(dev, "Unbalanced %s!\n", __func__); | 1330 | dev_warn(dev, "Unbalanced %s!\n", __func__); |
| 1331 | } | ||
| 1301 | 1332 | ||
| 1302 | WARN(!dev->power.disable_depth && | 1333 | WARN(!dev->power.disable_depth && |
| 1303 | dev->power.runtime_status == RPM_SUSPENDED && | 1334 | dev->power.runtime_status == RPM_SUSPENDED && |
| @@ -1494,7 +1525,6 @@ void pm_runtime_init(struct device *dev) | |||
| 1494 | dev->power.request_pending = false; | 1525 | dev->power.request_pending = false; |
| 1495 | dev->power.request = RPM_REQ_NONE; | 1526 | dev->power.request = RPM_REQ_NONE; |
| 1496 | dev->power.deferred_resume = false; | 1527 | dev->power.deferred_resume = false; |
| 1497 | dev->power.accounting_timestamp = jiffies; | ||
| 1498 | INIT_WORK(&dev->power.work, pm_runtime_work); | 1528 | INIT_WORK(&dev->power.work, pm_runtime_work); |
| 1499 | 1529 | ||
| 1500 | dev->power.timer_expires = 0; | 1530 | dev->power.timer_expires = 0; |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index d713738ce796..c6bf76124184 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
| @@ -125,9 +125,12 @@ static ssize_t runtime_active_time_show(struct device *dev, | |||
| 125 | struct device_attribute *attr, char *buf) | 125 | struct device_attribute *attr, char *buf) |
| 126 | { | 126 | { |
| 127 | int ret; | 127 | int ret; |
| 128 | u64 tmp; | ||
| 128 | spin_lock_irq(&dev->power.lock); | 129 | spin_lock_irq(&dev->power.lock); |
| 129 | update_pm_runtime_accounting(dev); | 130 | update_pm_runtime_accounting(dev); |
| 130 | ret = sprintf(buf, "%i\n", jiffies_to_msecs(dev->power.active_jiffies)); | 131 | tmp = dev->power.active_time; |
| 132 | do_div(tmp, NSEC_PER_MSEC); | ||
| 133 | ret = sprintf(buf, "%llu\n", tmp); | ||
| 131 | spin_unlock_irq(&dev->power.lock); | 134 | spin_unlock_irq(&dev->power.lock); |
| 132 | return ret; | 135 | return ret; |
| 133 | } | 136 | } |
| @@ -138,10 +141,12 @@ static ssize_t runtime_suspended_time_show(struct device *dev, | |||
| 138 | struct device_attribute *attr, char *buf) | 141 | struct device_attribute *attr, char *buf) |
| 139 | { | 142 | { |
| 140 | int ret; | 143 | int ret; |
| 144 | u64 tmp; | ||
| 141 | spin_lock_irq(&dev->power.lock); | 145 | spin_lock_irq(&dev->power.lock); |
| 142 | update_pm_runtime_accounting(dev); | 146 | update_pm_runtime_accounting(dev); |
| 143 | ret = sprintf(buf, "%i\n", | 147 | tmp = dev->power.suspended_time; |
| 144 | jiffies_to_msecs(dev->power.suspended_jiffies)); | 148 | do_div(tmp, NSEC_PER_MSEC); |
| 149 | ret = sprintf(buf, "%llu\n", tmp); | ||
| 145 | spin_unlock_irq(&dev->power.lock); | 150 | spin_unlock_irq(&dev->power.lock); |
| 146 | return ret; | 151 | return ret; |
| 147 | } | 152 | } |
| @@ -648,6 +653,10 @@ int dpm_sysfs_add(struct device *dev) | |||
| 648 | { | 653 | { |
| 649 | int rc; | 654 | int rc; |
| 650 | 655 | ||
| 656 | /* No need to create PM sysfs if explicitly disabled. */ | ||
| 657 | if (device_pm_not_required(dev)) | ||
| 658 | return 0; | ||
| 659 | |||
| 651 | rc = sysfs_create_group(&dev->kobj, &pm_attr_group); | 660 | rc = sysfs_create_group(&dev->kobj, &pm_attr_group); |
| 652 | if (rc) | 661 | if (rc) |
| 653 | return rc; | 662 | return rc; |
| @@ -727,6 +736,8 @@ void rpm_sysfs_remove(struct device *dev) | |||
| 727 | 736 | ||
| 728 | void dpm_sysfs_remove(struct device *dev) | 737 | void dpm_sysfs_remove(struct device *dev) |
| 729 | { | 738 | { |
| 739 | if (device_pm_not_required(dev)) | ||
| 740 | return; | ||
| 730 | sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group); | 741 | sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group); |
| 731 | dev_pm_qos_constraints_destroy(dev); | 742 | dev_pm_qos_constraints_destroy(dev); |
| 732 | rpm_sysfs_remove(dev); | 743 | rpm_sysfs_remove(dev); |
