14
14
#include <linux/types.h>
15
15
#include <linux/pci.h>
16
16
#include <linux/sched.h>
17
+ #include <linux/suspend.h>
17
18
#include <linux/interrupt.h>
18
19
#include <linux/workqueue.h>
19
20
#define CREATE_TRACE_POINTS
@@ -98,6 +99,11 @@ static const struct pci_device_id ish_invalid_pci_ids[] = {
98
99
{}
99
100
};
100
101
102
+ static inline bool ish_should_enter_d0i3 (struct pci_dev * pdev )
103
+ {
104
+ return !pm_suspend_via_firmware () || pdev -> device == CHV_DEVICE_ID ;
105
+ }
106
+
101
107
/**
102
108
* ish_probe() - PCI driver probe callback
103
109
* @pdev: pci device
@@ -148,7 +154,6 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
148
154
/* mapping IO device memory */
149
155
hw -> mem_addr = pcim_iomap_table (pdev )[0 ];
150
156
ishtp -> pdev = pdev ;
151
- pdev -> dev_flags |= PCI_DEV_FLAGS_NO_D3 ;
152
157
153
158
/* request and enable interrupt */
154
159
ret = pci_alloc_irq_vectors (pdev , 1 , 1 , PCI_IRQ_ALL_TYPES );
@@ -185,7 +190,6 @@ static void ish_remove(struct pci_dev *pdev)
185
190
struct ishtp_device * ishtp_dev = pci_get_drvdata (pdev );
186
191
187
192
ishtp_bus_remove_all_clients (ishtp_dev , false);
188
- pdev -> dev_flags &= ~PCI_DEV_FLAGS_NO_D3 ;
189
193
ish_device_disable (ishtp_dev );
190
194
}
191
195
@@ -207,34 +211,34 @@ static void __maybe_unused ish_resume_handler(struct work_struct *work)
207
211
{
208
212
struct pci_dev * pdev = to_pci_dev (ish_resume_device );
209
213
struct ishtp_device * dev = pci_get_drvdata (pdev );
210
- uint32_t fwsts ;
211
214
int ret ;
212
215
213
- /* Get ISH FW status */
214
- fwsts = IPC_GET_ISH_FWSTS (dev -> ops -> get_fw_status (dev ));
216
+ /* Check the NO_D3 flag to distinguish the resume paths */
217
+ if (pdev -> dev_flags & PCI_DEV_FLAGS_NO_D3 ) {
218
+ pdev -> dev_flags &= ~PCI_DEV_FLAGS_NO_D3 ;
219
+ disable_irq_wake (pdev -> irq );
215
220
216
- /*
217
- * If currently, in ISH FW, sensor app is loaded or beyond that,
218
- * it means ISH isn't powered off, in this case, send a resume message.
219
- */
220
- if (fwsts >= FWSTS_SENSOR_APP_LOADED ) {
221
221
ishtp_send_resume (dev );
222
222
223
223
/* Waiting to get resume response */
224
224
if (dev -> resume_flag )
225
225
ret = wait_event_interruptible_timeout (dev -> resume_wait ,
226
226
!dev -> resume_flag ,
227
227
msecs_to_jiffies (WAIT_FOR_RESUME_ACK_MS ));
228
- }
229
228
230
- /*
231
- * If in ISH FW, sensor app isn't loaded yet, or no resume response.
232
- * That means this platform is not S0ix compatible, or something is
233
- * wrong with ISH FW. So on resume, full reboot of ISH processor will
234
- * happen, so need to go through init sequence again.
235
- */
236
- if (dev -> resume_flag )
229
+ /*
230
+ * If the flag is not cleared, something is wrong with ISH FW.
231
+ * So on resume, need to go through init sequence again.
232
+ */
233
+ if (dev -> resume_flag )
234
+ ish_init (dev );
235
+ } else {
236
+ /*
237
+ * Resume from the D3, full reboot of ISH processor will happen,
238
+ * so need to go through init sequence again.
239
+ */
237
240
ish_init (dev );
241
+ }
238
242
}
239
243
240
244
/**
@@ -250,23 +254,43 @@ static int __maybe_unused ish_suspend(struct device *device)
250
254
struct pci_dev * pdev = to_pci_dev (device );
251
255
struct ishtp_device * dev = pci_get_drvdata (pdev );
252
256
253
- enable_irq_wake (pdev -> irq );
254
- /*
255
- * If previous suspend hasn't been asnwered then ISH is likely dead,
256
- * don't attempt nested notification
257
- */
258
- if (dev -> suspend_flag )
259
- return 0 ;
260
-
261
- dev -> resume_flag = 0 ;
262
- dev -> suspend_flag = 1 ;
263
- ishtp_send_suspend (dev );
264
-
265
- /* 25 ms should be enough for live ISH to flush all IPC buf */
266
- if (dev -> suspend_flag )
267
- wait_event_interruptible_timeout (dev -> suspend_wait ,
268
- !dev -> suspend_flag ,
269
- msecs_to_jiffies (25 ));
257
+ if (ish_should_enter_d0i3 (pdev )) {
258
+ /*
259
+ * If previous suspend hasn't been asnwered then ISH is likely
260
+ * dead, don't attempt nested notification
261
+ */
262
+ if (dev -> suspend_flag )
263
+ return 0 ;
264
+
265
+ dev -> resume_flag = 0 ;
266
+ dev -> suspend_flag = 1 ;
267
+ ishtp_send_suspend (dev );
268
+
269
+ /* 25 ms should be enough for live ISH to flush all IPC buf */
270
+ if (dev -> suspend_flag )
271
+ wait_event_interruptible_timeout (dev -> suspend_wait ,
272
+ !dev -> suspend_flag ,
273
+ msecs_to_jiffies (25 ));
274
+
275
+ if (dev -> suspend_flag ) {
276
+ /*
277
+ * It looks like FW halt, clear the DMA bit, and put
278
+ * ISH into D3, and FW would reset on resume.
279
+ */
280
+ ish_disable_dma (dev );
281
+ } else {
282
+ /* Set the NO_D3 flag, the ISH would enter D0i3 */
283
+ pdev -> dev_flags |= PCI_DEV_FLAGS_NO_D3 ;
284
+
285
+ enable_irq_wake (pdev -> irq );
286
+ }
287
+ } else {
288
+ /*
289
+ * Clear the DMA bit before putting ISH into D3,
290
+ * or ISH FW would reset automatically.
291
+ */
292
+ ish_disable_dma (dev );
293
+ }
270
294
271
295
return 0 ;
272
296
}
@@ -288,7 +312,6 @@ static int __maybe_unused ish_resume(struct device *device)
288
312
ish_resume_device = device ;
289
313
dev -> resume_flag = 1 ;
290
314
291
- disable_irq_wake (pdev -> irq );
292
315
schedule_work (& resume_work );
293
316
294
317
return 0 ;
0 commit comments