summaryrefslogtreecommitdiff
path: root/gcip-kernel-driver/include/gcip/gcip-pm.h
blob: c7673d8b23f3a6e426b37479626894f6d757cc1b (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
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Power management support for GCIP devices.
 *
 * Copyright (C) 2023 Google LLC
 */

#ifndef __GCIP_PM_H__
#define __GCIP_PM_H__

#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>

struct gcip_pm {
	struct device *dev;
	/* Worker to handle async power down retry. */
	struct delayed_work power_down_work;

	/* Lock to protect the members listed below. */
	struct mutex lock;
	/* Power up counter. Protected by @lock */
	int count;
	/* Flag indicating a deferred power down is pending. Protected by @lock */
	bool power_down_pending;

	/* Callbacks. See struct gcip_pm_args. */
	void *data;
	int (*after_create)(void *data);
	void (*before_destroy)(void *data);
	int (*power_up)(void *data);
	int (*power_down)(void *data);
};

struct gcip_pm_args {
	/* Device struct for logging. */
	struct device *dev;

	/* Private data for the callbacks listed below. */
	void *data;
	/*
	 * Device-specific power up.
	 * Called with @pm->lock hold and nesting is handled at generic layer.
	 */
	int (*power_up)(void *data);
	/*
	 * Device-specific power down.
	 * Called with @pm->lock hold and nesting is handled at generic layer.
	 * Returning -EAGAIN will trigger a retry after GCIP_ASYNC_POWER_DOWN_RETRY_DELAY ms.
	 */
	int (*power_down)(void *data);
	/* Optional. For initial setup after the interface initialized. */
	int (*after_create)(void *data);
	/* Optional. For clean-up before the interface is destroyed. */
	void (*before_destroy)(void *data);
};

/* Allocates and initializes a power management interface for the GCIP device. */
struct gcip_pm *gcip_pm_create(const struct gcip_pm_args *args);

/* Destroys and frees the power management interface. */
void gcip_pm_destroy(struct gcip_pm *pm);

/*
 * These mimic the pm_runtime_{get|put} functions to keep a reference count of requests in order to
 * keep the device up and turn it off.
 * Note that we don't keep track of system suspend/resume state since the system power management
 * will respect the parent-child sequencing to use a bottom-up order to suspend devices and a
 * top-down order to resume devices. No one would have the ability to acquire or release a wakelock
 * when the device is suspending or resuming.
 */

/*
 * Increases @pm->count if the device is already powered on.
 *
 * Caller should call gcip_pm_put() to decrease @pm->count if this function returns 0.
 * If @blocking is true, it will wait until the ongoing power state transition finishes (i.e.,
 * gcip_pm_{get,put,shutdown} called by other thread returns) and then check the power state.
 * If @blocking is false, return -EAGAIN immediately when there is a ongoing power state transition.
 *
 * Returns 0 on success; otherwise -EAGAIN if the device is off or in power state transition when
 * @blocking is false.
 */
int gcip_pm_get_if_powered(struct gcip_pm *pm, bool blocking);

/*
 * Increases @pm->count and powers up the device if previous @pm->count was zero.
 *
 * Returns 0 on success; otherwise negative error values.
 */
int gcip_pm_get(struct gcip_pm *pm);

/*
 * Decreases @pm->count and powers off the device if @pm->count reaches zero.
 * If .power_down fails, async work will be scheduled to retry after
 * GCIP_ASYNC_POWER_DOWN_RETRY_DELAY ms.
 */
void gcip_pm_put(struct gcip_pm *pm);

/*
 * Same as gcip_pm_put, but the power off will be scheduled later.
 * Caller should use this async gcip_pm_put if they're on the power off path to prevent deadlock,
 * e.g., a workqueue that will be canceled during power off.
 */
void gcip_pm_put_async(struct gcip_pm *pm);

/* Gets the power up counter. Retures -EAGAIN if device is in power state transition. */
int gcip_pm_get_count(struct gcip_pm *pm);

/* Checks if device is already on. Retures false if device is off or in power state transition. */
bool gcip_pm_is_powered(struct gcip_pm *pm);

/* Shuts down the device if @pm->count equals to 0 or @force is true. */
void gcip_pm_shutdown(struct gcip_pm *pm, bool force);

/* Make sure @pm->lock is hold. */
static inline void gcip_pm_lockdep_assert_held(struct gcip_pm *pm)
{
	if (!pm)
		return;

	lockdep_assert_held(&pm->lock);
}

#endif /* __GCIP_PM_H__ */