18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SPDX-License-Identifier: MIT 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright © 2019 Intel Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/wait_bit.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "intel_runtime_pm.h" 108c2ecf20Sopenharmony_ci#include "intel_wakeref.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic void rpm_get(struct intel_wakeref *wf) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci wf->wakeref = intel_runtime_pm_get(wf->rpm); 158c2ecf20Sopenharmony_ci} 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic void rpm_put(struct intel_wakeref *wf) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci intel_wakeref_t wakeref = fetch_and_zero(&wf->wakeref); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci intel_runtime_pm_put(wf->rpm, wakeref); 228c2ecf20Sopenharmony_ci INTEL_WAKEREF_BUG_ON(!wakeref); 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciint __intel_wakeref_get_first(struct intel_wakeref *wf) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci /* 288c2ecf20Sopenharmony_ci * Treat get/put as different subclasses, as we may need to run 298c2ecf20Sopenharmony_ci * the put callback from under the shrinker and do not want to 308c2ecf20Sopenharmony_ci * cross-contanimate that callback with any extra work performed 318c2ecf20Sopenharmony_ci * upon acquiring the wakeref. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci mutex_lock_nested(&wf->mutex, SINGLE_DEPTH_NESTING); 348c2ecf20Sopenharmony_ci if (!atomic_read(&wf->count)) { 358c2ecf20Sopenharmony_ci int err; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci rpm_get(wf); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci err = wf->ops->get(wf); 408c2ecf20Sopenharmony_ci if (unlikely(err)) { 418c2ecf20Sopenharmony_ci rpm_put(wf); 428c2ecf20Sopenharmony_ci mutex_unlock(&wf->mutex); 438c2ecf20Sopenharmony_ci return err; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci smp_mb__before_atomic(); /* release wf->count */ 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci atomic_inc(&wf->count); 498c2ecf20Sopenharmony_ci mutex_unlock(&wf->mutex); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0); 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void ____intel_wakeref_put_last(struct intel_wakeref *wf) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0); 588c2ecf20Sopenharmony_ci if (unlikely(!atomic_dec_and_test(&wf->count))) 598c2ecf20Sopenharmony_ci goto unlock; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* ops->put() must reschedule its own release on error/deferral */ 628c2ecf20Sopenharmony_ci if (likely(!wf->ops->put(wf))) { 638c2ecf20Sopenharmony_ci rpm_put(wf); 648c2ecf20Sopenharmony_ci wake_up_var(&wf->wakeref); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciunlock: 688c2ecf20Sopenharmony_ci mutex_unlock(&wf->mutex); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_civoid __intel_wakeref_put_last(struct intel_wakeref *wf, unsigned long flags) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci INTEL_WAKEREF_BUG_ON(delayed_work_pending(&wf->work)); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Assume we are not in process context and so cannot sleep. */ 768c2ecf20Sopenharmony_ci if (flags & INTEL_WAKEREF_PUT_ASYNC || !mutex_trylock(&wf->mutex)) { 778c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &wf->work, 788c2ecf20Sopenharmony_ci FIELD_GET(INTEL_WAKEREF_PUT_DELAY, flags)); 798c2ecf20Sopenharmony_ci return; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ____intel_wakeref_put_last(wf); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void __intel_wakeref_put_work(struct work_struct *wrk) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct intel_wakeref *wf = container_of(wrk, typeof(*wf), work.work); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (atomic_add_unless(&wf->count, -1, 1)) 908c2ecf20Sopenharmony_ci return; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci mutex_lock(&wf->mutex); 938c2ecf20Sopenharmony_ci ____intel_wakeref_put_last(wf); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid __intel_wakeref_init(struct intel_wakeref *wf, 978c2ecf20Sopenharmony_ci struct intel_runtime_pm *rpm, 988c2ecf20Sopenharmony_ci const struct intel_wakeref_ops *ops, 998c2ecf20Sopenharmony_ci struct intel_wakeref_lockclass *key) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci wf->rpm = rpm; 1028c2ecf20Sopenharmony_ci wf->ops = ops; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci __mutex_init(&wf->mutex, "wakeref.mutex", &key->mutex); 1058c2ecf20Sopenharmony_ci atomic_set(&wf->count, 0); 1068c2ecf20Sopenharmony_ci wf->wakeref = 0; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&wf->work, __intel_wakeref_put_work); 1098c2ecf20Sopenharmony_ci lockdep_init_map(&wf->work.work.lockdep_map, 1108c2ecf20Sopenharmony_ci "wakeref.work", &key->work, 0); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciint intel_wakeref_wait_for_idle(struct intel_wakeref *wf) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int err; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci might_sleep(); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci err = wait_var_event_killable(&wf->wakeref, 1208c2ecf20Sopenharmony_ci !intel_wakeref_is_active(wf)); 1218c2ecf20Sopenharmony_ci if (err) 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci intel_wakeref_unlock_wait(wf); 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void wakeref_auto_timeout(struct timer_list *t) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct intel_wakeref_auto *wf = from_timer(wf, t, timer); 1318c2ecf20Sopenharmony_ci intel_wakeref_t wakeref; 1328c2ecf20Sopenharmony_ci unsigned long flags; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!refcount_dec_and_lock_irqsave(&wf->count, &wf->lock, &flags)) 1358c2ecf20Sopenharmony_ci return; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci wakeref = fetch_and_zero(&wf->wakeref); 1388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wf->lock, flags); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci intel_runtime_pm_put(wf->rpm, wakeref); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_civoid intel_wakeref_auto_init(struct intel_wakeref_auto *wf, 1448c2ecf20Sopenharmony_ci struct intel_runtime_pm *rpm) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci spin_lock_init(&wf->lock); 1478c2ecf20Sopenharmony_ci timer_setup(&wf->timer, wakeref_auto_timeout, 0); 1488c2ecf20Sopenharmony_ci refcount_set(&wf->count, 0); 1498c2ecf20Sopenharmony_ci wf->wakeref = 0; 1508c2ecf20Sopenharmony_ci wf->rpm = rpm; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_civoid intel_wakeref_auto(struct intel_wakeref_auto *wf, unsigned long timeout) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci unsigned long flags; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!timeout) { 1588c2ecf20Sopenharmony_ci if (del_timer_sync(&wf->timer)) 1598c2ecf20Sopenharmony_ci wakeref_auto_timeout(&wf->timer); 1608c2ecf20Sopenharmony_ci return; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Our mission is that we only extend an already active wakeref */ 1648c2ecf20Sopenharmony_ci assert_rpm_wakelock_held(wf->rpm); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (!refcount_inc_not_zero(&wf->count)) { 1678c2ecf20Sopenharmony_ci spin_lock_irqsave(&wf->lock, flags); 1688c2ecf20Sopenharmony_ci if (!refcount_inc_not_zero(&wf->count)) { 1698c2ecf20Sopenharmony_ci INTEL_WAKEREF_BUG_ON(wf->wakeref); 1708c2ecf20Sopenharmony_ci wf->wakeref = intel_runtime_pm_get_if_in_use(wf->rpm); 1718c2ecf20Sopenharmony_ci refcount_set(&wf->count, 1); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wf->lock, flags); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * If we extend a pending timer, we will only get a single timer 1788c2ecf20Sopenharmony_ci * callback and so need to cancel the local inc by running the 1798c2ecf20Sopenharmony_ci * elided callback to keep the wf->count balanced. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci if (mod_timer(&wf->timer, jiffies + timeout)) 1828c2ecf20Sopenharmony_ci wakeref_auto_timeout(&wf->timer); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_civoid intel_wakeref_auto_fini(struct intel_wakeref_auto *wf) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci intel_wakeref_auto(wf, 0); 1888c2ecf20Sopenharmony_ci INTEL_WAKEREF_BUG_ON(wf->wakeref); 1898c2ecf20Sopenharmony_ci} 190