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