1414#include <linux/types.h>
1515#include <linux/pci.h>
1616#include <linux/sched.h>
17+ #include <linux/suspend.h>
1718#include <linux/interrupt.h>
1819#include <linux/workqueue.h>
1920#define CREATE_TRACE_POINTS
@@ -98,6 +99,11 @@ static const struct pci_device_id ish_invalid_pci_ids[] = {
9899 {}
99100};
100101
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+
101107/**
102108 * ish_probe() - PCI driver probe callback
103109 * @pdev: pci device
@@ -148,7 +154,6 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
148154 /* mapping IO device memory */
149155 hw -> mem_addr = pcim_iomap_table (pdev )[0 ];
150156 ishtp -> pdev = pdev ;
151- pdev -> dev_flags |= PCI_DEV_FLAGS_NO_D3 ;
152157
153158 /* request and enable interrupt */
154159 ret = pci_alloc_irq_vectors (pdev , 1 , 1 , PCI_IRQ_ALL_TYPES );
@@ -185,7 +190,6 @@ static void ish_remove(struct pci_dev *pdev)
185190 struct ishtp_device * ishtp_dev = pci_get_drvdata (pdev );
186191
187192 ishtp_bus_remove_all_clients (ishtp_dev , false);
188- pdev -> dev_flags &= ~PCI_DEV_FLAGS_NO_D3 ;
189193 ish_device_disable (ishtp_dev );
190194}
191195
@@ -207,34 +211,34 @@ static void __maybe_unused ish_resume_handler(struct work_struct *work)
207211{
208212 struct pci_dev * pdev = to_pci_dev (ish_resume_device );
209213 struct ishtp_device * dev = pci_get_drvdata (pdev );
210- uint32_t fwsts ;
211214 int ret ;
212215
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 );
215220
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 ) {
221221 ishtp_send_resume (dev );
222222
223223 /* Waiting to get resume response */
224224 if (dev -> resume_flag )
225225 ret = wait_event_interruptible_timeout (dev -> resume_wait ,
226226 !dev -> resume_flag ,
227227 msecs_to_jiffies (WAIT_FOR_RESUME_ACK_MS ));
228- }
229228
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+ */
237240 ish_init (dev );
241+ }
238242}
239243
240244/**
@@ -250,23 +254,43 @@ static int __maybe_unused ish_suspend(struct device *device)
250254 struct pci_dev * pdev = to_pci_dev (device );
251255 struct ishtp_device * dev = pci_get_drvdata (pdev );
252256
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+ }
270294
271295 return 0 ;
272296}
@@ -288,7 +312,6 @@ static int __maybe_unused ish_resume(struct device *device)
288312 ish_resume_device = device ;
289313 dev -> resume_flag = 1 ;
290314
291- disable_irq_wake (pdev -> irq );
292315 schedule_work (& resume_work );
293316
294317 return 0 ;
0 commit comments