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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
/* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (C) 2020-21 Intel Corporation.
*/
#ifndef IOSM_IPC_PM_H
#define IOSM_IPC_PM_H
/* Trigger the doorbell interrupt on cp to change the PM sleep/active status */
#define ipc_cp_irq_sleep_control(ipc_pcie, data) \
ipc_doorbell_fire(ipc_pcie, IPC_DOORBELL_IRQ_SLEEP, data)
/* Trigger the doorbell interrupt on CP to do hpda update */
#define ipc_cp_irq_hpda_update(ipc_pcie, data) \
ipc_doorbell_fire(ipc_pcie, IPC_DOORBELL_IRQ_HPDA, 0xFF & (data))
/**
* union ipc_pm_cond - Conditions for D3 and the sleep message to CP.
* @raw: raw/combined value for faster check
* @irq: IRQ towards CP
* @hs: Host Sleep
* @link: Device link state.
*/
union ipc_pm_cond {
unsigned int raw;
struct {
unsigned int irq:1,
hs:1,
link:1;
};
};
/**
* enum ipc_mem_host_pm_state - Possible states of the HOST SLEEP finite state
* machine.
* @IPC_MEM_HOST_PM_ACTIVE: Host is active
* @IPC_MEM_HOST_PM_ACTIVE_WAIT: Intermediate state before going to
* active
* @IPC_MEM_HOST_PM_SLEEP_WAIT_IDLE: Intermediate state to wait for idle
* before going into sleep
* @IPC_MEM_HOST_PM_SLEEP_WAIT_D3: Intermediate state to wait for D3
* before going to sleep
* @IPC_MEM_HOST_PM_SLEEP: after this state the interface is not
* accessible host is in suspend to RAM
* @IPC_MEM_HOST_PM_SLEEP_WAIT_EXIT_SLEEP: Intermediate state before exiting
* sleep
*/
enum ipc_mem_host_pm_state {
IPC_MEM_HOST_PM_ACTIVE,
IPC_MEM_HOST_PM_ACTIVE_WAIT,
IPC_MEM_HOST_PM_SLEEP_WAIT_IDLE,
IPC_MEM_HOST_PM_SLEEP_WAIT_D3,
IPC_MEM_HOST_PM_SLEEP,
IPC_MEM_HOST_PM_SLEEP_WAIT_EXIT_SLEEP,
};
/**
* enum ipc_mem_dev_pm_state - Possible states of the DEVICE SLEEP finite state
* machine.
* @IPC_MEM_DEV_PM_ACTIVE: IPC_MEM_DEV_PM_ACTIVE is the initial
* power management state.
* IRQ(struct ipc_mem_device_info:
* device_sleep_notification)
* and DOORBELL-IRQ-HPDA(data) values.
* @IPC_MEM_DEV_PM_SLEEP: IPC_MEM_DEV_PM_SLEEP is PM state for
* sleep.
* @IPC_MEM_DEV_PM_WAKEUP: DOORBELL-IRQ-DEVICE_WAKE(data).
* @IPC_MEM_DEV_PM_HOST_SLEEP: DOORBELL-IRQ-HOST_SLEEP(data).
* @IPC_MEM_DEV_PM_ACTIVE_WAIT: Local intermediate states.
* @IPC_MEM_DEV_PM_FORCE_SLEEP: DOORBELL-IRQ-FORCE_SLEEP.
* @IPC_MEM_DEV_PM_FORCE_ACTIVE: DOORBELL-IRQ-FORCE_ACTIVE.
*/
enum ipc_mem_dev_pm_state {
IPC_MEM_DEV_PM_ACTIVE,
IPC_MEM_DEV_PM_SLEEP,
IPC_MEM_DEV_PM_WAKEUP,
IPC_MEM_DEV_PM_HOST_SLEEP,
IPC_MEM_DEV_PM_ACTIVE_WAIT,
IPC_MEM_DEV_PM_FORCE_SLEEP = 7,
IPC_MEM_DEV_PM_FORCE_ACTIVE,
};
/**
* struct iosm_pm - Power management instance
* @pcie: Pointer to iosm_pcie structure
* @dev: Pointer to device structure
* @host_pm_state: PM states for host
* @host_sleep_pend: Variable to indicate Host Sleep Pending
* @host_sleep_complete: Generic wait-for-completion used in
* case of Host Sleep
* @pm_cond: Conditions for power management
* @ap_state: Current power management state, the
* initial state is IPC_MEM_DEV_PM_ACTIVE eq. 0.
* @cp_state: PM State of CP
* @device_sleep_notification: last handled device_sleep_notfication
* @pending_hpda_update: is a HPDA update pending?
*/
struct iosm_pm {
struct iosm_pcie *pcie;
struct device *dev;
enum ipc_mem_host_pm_state host_pm_state;
unsigned long host_sleep_pend;
struct completion host_sleep_complete;
union ipc_pm_cond pm_cond;
enum ipc_mem_dev_pm_state ap_state;
enum ipc_mem_dev_pm_state cp_state;
u32 device_sleep_notification;
u8 pending_hpda_update:1;
};
/**
* enum ipc_pm_unit - Power management units.
* @IPC_PM_UNIT_IRQ: IRQ towards CP
* @IPC_PM_UNIT_HS: Host Sleep for converged protocol
* @IPC_PM_UNIT_LINK: Link state controlled by CP.
*/
enum ipc_pm_unit {
IPC_PM_UNIT_IRQ,
IPC_PM_UNIT_HS,
IPC_PM_UNIT_LINK,
};
/**
* ipc_pm_init - Allocate power management component
* @ipc_protocol: Pointer to iosm_protocol structure
*/
void ipc_pm_init(struct iosm_protocol *ipc_protocol);
/**
* ipc_pm_deinit - Free power management component, invalidating its pointer.
* @ipc_protocol: Pointer to iosm_protocol structure
*/
void ipc_pm_deinit(struct iosm_protocol *ipc_protocol);
/**
* ipc_pm_dev_slp_notification - Handle a sleep notification message from the
* device. This can be called from interrupt state
* This function handles Host Sleep requests too
* if the Host Sleep protocol is register based.
* @ipc_pm: Pointer to power management component
* @sleep_notification: Actual notification from device
*
* Returns: true if dev sleep state has to be checked, false otherwise.
*/
bool ipc_pm_dev_slp_notification(struct iosm_pm *ipc_pm,
u32 sleep_notification);
/**
* ipc_pm_set_s2idle_sleep - Set PM variables to sleep/active
* @ipc_pm: Pointer to power management component
* @sleep: true to enter sleep/false to exit sleep
*/
void ipc_pm_set_s2idle_sleep(struct iosm_pm *ipc_pm, bool sleep);
/**
* ipc_pm_prepare_host_sleep - Prepare the PM for sleep by entering
* IPC_MEM_HOST_PM_SLEEP_WAIT_D3 state.
* @ipc_pm: Pointer to power management component
*
* Returns: true on success, false if the host was not active.
*/
bool ipc_pm_prepare_host_sleep(struct iosm_pm *ipc_pm);
/**
* ipc_pm_prepare_host_active - Prepare the PM for wakeup by entering
* IPC_MEM_HOST_PM_ACTIVE_WAIT state.
* @ipc_pm: Pointer to power management component
*
* Returns: true on success, false if the host was not sleeping.
*/
bool ipc_pm_prepare_host_active(struct iosm_pm *ipc_pm);
/**
* ipc_pm_wait_for_device_active - Wait up to IPC_PM_ACTIVE_TIMEOUT_MS ms
* for the device to reach active state
* @ipc_pm: Pointer to power management component
*
* Returns: true if device is active, false on timeout
*/
bool ipc_pm_wait_for_device_active(struct iosm_pm *ipc_pm);
/**
* ipc_pm_signal_hpda_doorbell - Wake up the device if it is in low power mode
* and trigger a head pointer update interrupt.
* @ipc_pm: Pointer to power management component
* @identifier: specifies what component triggered hpda update irq
* @host_slp_check: if set to true then Host Sleep state machine check will
* be performed. If Host Sleep state machine allows HP
* update then only doorbell is triggered otherwise pending
* flag will be set. If set to false then Host Sleep check
* will not be performed. This is helpful for Host Sleep
* negotiation through message ring.
*/
void ipc_pm_signal_hpda_doorbell(struct iosm_pm *ipc_pm, u32 identifier,
bool host_slp_check);
/**
* ipc_pm_trigger - Update power manager and wake up the link if needed
* @ipc_pm: Pointer to power management component
* @unit: Power management units
* @active: Device link state
*
* Returns: true if link is unchanged or active, false otherwise
*/
bool ipc_pm_trigger(struct iosm_pm *ipc_pm, enum ipc_pm_unit unit, bool active);
#endif
|