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__ */
|