summaryrefslogtreecommitdiff
path: root/drivers/edgetpu/edgetpu-wakelock.h
blob: 713f58b02c91c915d4a5f7b4fe1c8ca0cbb4a582 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Wakelock for the runtime to explicitly claim it's going to use the EdgeTPU
 * device.
 *
 * Copyright (C) 2021 Google, Inc.
 */
#ifndef __EDGETPU_WAKELOCK_H__
#define __EDGETPU_WAKELOCK_H__

#include <linux/err.h>
#include <linux/mutex.h>

#include "edgetpu-internal.h"

/*
 * See edgetpu_wakelock_alloc() for when this value is returned.
 * Define as an errno so we can use IS_ERR* macros.
 */
#define EDGETPU_NO_WAKELOCK ERR_PTR(-EOPNOTSUPP)

#define NO_WAKELOCK(wakelock) ((wakelock) == EDGETPU_NO_WAKELOCK)

/*
 * Events that could block the wakelock from being released.
 * Use inc_event() and dec_event() to increase and decrease the counters when
 * the event happens.
 */
enum edgetpu_wakelock_event {
	EDGETPU_WAKELOCK_EVENT_FULL_CSR = 0,
	EDGETPU_WAKELOCK_EVENT_MBOX_CSR = 1,
	EDGETPU_WAKELOCK_EVENT_CMD_QUEUE = 2,
	EDGETPU_WAKELOCK_EVENT_RESP_QUEUE = 3,

	EDGETPU_WAKELOCK_EVENT_END = 4
};

struct edgetpu_wakelock {
	struct edgetpu_dev *etdev; /* only for logging */
	/* Protects every field below */
	struct mutex lock;
	/*
	 * The request counter, increments on "acquire" and decrements on
	 * "release".
	 */
	uint req_count;
	/*
	 * Events counter.
	 * release() would fail if one of the slots is not zero.
	 */
	uint event_count[EDGETPU_WAKELOCK_EVENT_END];
};

/*
 * Allocates and initializes a wakelock object.
 *
 * Returns the pointer on success, or NULL when out of memory.
 *
 * When the chipset doesn't support wakelock (judged by EDGETPU_HAS_WAKELOCK):
 *   Nothing is allocated and this function returns EDGETPU_NO_WAKELOCK.
 */
struct edgetpu_wakelock *edgetpu_wakelock_alloc(struct edgetpu_dev *etdev);
/* Frees the allocated wakelock. */
void edgetpu_wakelock_free(struct edgetpu_wakelock *wakelock);

/*
 * Increases the event counter of @evt by one.
 *
 * Returns true if the counter is increased successfully.
 * Returns false when one of the following errors happens:
 *   - the wakelock is released
 *   - integer overflow on the counter
 *
 * When the chipset doesn't support wakelock:
 *   Does nothing and returns true.
 */
bool edgetpu_wakelock_inc_event(struct edgetpu_wakelock *wakelock,
				enum edgetpu_wakelock_event evt);
/*
 * Decreases the event counter of @evt by one.
 *
 * Returns true if the counter is decreased successfully.
 * Returns false when one of the following errors happens:
 *   - the counter is zero
 *
 * When the chipset doesn't support wakelock:
 *   Does nothing and returns true.
 */
bool edgetpu_wakelock_dec_event(struct edgetpu_wakelock *wakelock,
				enum edgetpu_wakelock_event evt);

/*
 * Holds the internal lock of @wakelock. Fields in @wakelock are protected when
 * this lock is holding.
 *
 * Returns the non-negative request counter of @wakelock.
 *
 * Example:
 *   if (edgetpu_wakelock_lock(wakelock)) {
 *      <..works that need the state of wakelock unchanged..>
 *   }
 *   edgetpu_wakelock_unlock(wakelock);
 *
 * When the chipset doesn't support wakelock:
 *   Does nothing and returns 1.
 */
uint edgetpu_wakelock_lock(struct edgetpu_wakelock *wakelock);
void edgetpu_wakelock_unlock(struct edgetpu_wakelock *wakelock);

/*
 * Returns the request counter of @wakelock.
 *
 * Caller calls edgetpu_wakelock_lock() before calling this function.
 *
 * When the chipset doesn't support wakelock:
 *   Returns 1.
 */
static inline uint
edgetpu_wakelock_count_locked(struct edgetpu_wakelock *wakelock)
{
	return NO_WAKELOCK(wakelock) ? 1 : wakelock->req_count;
}

/*
 * Acquires the wakelock, increases @wakelock->req_count by one.
 *
 * This function should be surrounded by edgetpu_wakelock_lock() and
 * edgetpu_wakelock_unlock().
 *
 * Returns the value of request counter *before* being increased.
 * Returns -EOVERFLOW if the request counter would overflow after increment.
 *
 * When the chipset doesn't support wakelock:
 *   Does nothing and returns 1.
 */
int edgetpu_wakelock_acquire(struct edgetpu_wakelock *wakelock);
/*
 * Requests to release the wakelock, decreases @wakelock->req_count by one on
 * success.
 *
 * This function should be surrounded by edgetpu_wakelock_lock() and
 * edgetpu_wakelock_unlock().
 *
 * Returns the value of request counter *after* being decreased.
 * Returns -EINVAL if the request counter is already zero.
 * Returns -EAGAIN when there are events blocking wakelock from being released.
 *
 * When the chipset doesn't support wakelock:
 *   Does nothing and returns 1.
 */
int edgetpu_wakelock_release(struct edgetpu_wakelock *wakelock);

#endif /* __EDGETPU_WAKELOCK_H__ */