summaryrefslogtreecommitdiff
path: root/gxp-wakelock.c
blob: feb5c88a54750d9a5553e6bb7cef6df5531529b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// SPDX-License-Identifier: GPL-2.0
/*
 * GXP wakelock support
 *
 * Copyright (C) 2022 Google LLC
 */

#include "gxp-dma.h"
#include "gxp-pm.h"
#include "gxp-wakelock.h"

int gxp_wakelock_init(struct gxp_dev *gxp)
{
	struct gxp_wakelock_manager *mgr;

	mgr = devm_kzalloc(gxp->dev, sizeof(*mgr), GFP_KERNEL);
	if (!mgr)
		return -ENOMEM;

	mutex_init(&mgr->lock);

	gxp->wakelock_mgr = mgr;

	return 0;
}

int gxp_wakelock_acquire(struct gxp_dev *gxp)
{
	struct gxp_wakelock_manager *mgr = gxp->wakelock_mgr;
	int ret = 0;

	mutex_lock(&mgr->lock);

	if (mgr->suspended) {
		/*
		 * Don't allow a new client to obtain a wakelock, powering up
		 * BLK_AUR, when the system is going to sleep.
		 */
		dev_warn(gxp->dev,
			 "Attempt to obtain wakelock while suspending.\n");
		ret = -EAGAIN;
		goto out;
	}

	if (!mgr->count++) {
		ret = gxp_pm_blk_on(gxp);
		if (ret) {
			dev_err(gxp->dev,
				"Failed to power on BLK_AUR (ret=%d, client count=%u)\n",
				ret, mgr->count);
			goto err_blk_on;
		}

		ret = gxp_dma_ssmt_program(gxp);
		if (ret) {
			dev_err(gxp->dev,
				"Failed to program SSMTs after powering on BLK_AUR (ret=%d)\n",
				ret);
			goto err_ssmt_program;
		}
	}

out:
	mutex_unlock(&mgr->lock);

	return ret;

err_ssmt_program:
	gxp_pm_blk_off(gxp);
err_blk_on:
	mgr->count--;
	mutex_unlock(&mgr->lock);
	return ret;
}

void gxp_wakelock_release(struct gxp_dev *gxp)
{
	struct gxp_wakelock_manager *mgr = gxp->wakelock_mgr;
	int ret = 0;

	mutex_lock(&mgr->lock);

	if (!mgr->count) {
		dev_err(gxp->dev,
			"Attempt to release wakelock with none held.\n");
		goto out;
	}

	if (!--mgr->count) {
		ret = gxp_pm_blk_off(gxp);
		if (ret)
			dev_err(gxp->dev,
				"Failed to power down BLK_AUR (ret=%d, client count=%u)\n",
				ret, mgr->count);
	}

out:
	mutex_unlock(&mgr->lock);
}

int gxp_wakelock_suspend(struct gxp_dev *gxp)
{
	struct gxp_wakelock_manager *mgr = gxp->wakelock_mgr;
	int ret;

	mutex_lock(&mgr->lock);

	/* Can't suspend if there are any active clients */
	mgr->suspended = mgr->count == 0;
	ret = mgr->suspended ? 0 : -EAGAIN;

	mutex_unlock(&mgr->lock);

	return ret;
}

int gxp_wakelock_resume(struct gxp_dev *gxp)
{
	struct gxp_wakelock_manager *mgr = gxp->wakelock_mgr;

	mutex_lock(&mgr->lock);

	mgr->suspended = false;

	mutex_unlock(&mgr->lock);

	return 0;
}