HID: intel-ish-hid: ipc: check FW status to distinguish ISH resume paths
authorEven Xu <even.xu@intel.com>
Fri, 3 Feb 2017 06:24:53 +0000 (14:24 +0800)
committerJiri Kosina <jkosina@suse.cz>
Wed, 8 Feb 2017 03:11:57 +0000 (12:11 +0900)
For ISH resume, there are two paths, they need different way to handle: one
where ISH is not powered off, in that case a simple resume message is enough,
in other case we need a reset sequence.

We can use ISH FW status to distinguish those two cases and handle them
properly.

Signed-off-by: Even Xu <even.xu@intel.com>
Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h
drivers/hid/intel-ish-hid/ipc/hw-ish.h
drivers/hid/intel-ish-hid/ipc/pci-ish.c

index ab68afc..a5897b9 100644 (file)
 #define IPC_ILUP_BIT                   (1<<IPC_ILUP_OFFS)
 
 /*
+ * ISH FW status bits in ISH FW Status Register
+ */
+#define IPC_ISH_FWSTS_SHIFT            12
+#define IPC_ISH_FWSTS_MASK             GENMASK(15, 12)
+#define IPC_GET_ISH_FWSTS(status)      \
+       (((status) & IPC_ISH_FWSTS_MASK) >> IPC_ISH_FWSTS_SHIFT)
+
+/*
  * FW status bits (relevant)
  */
 #define        IPC_FWSTS_ILUP          0x1
index 46615a0..fd34307 100644 (file)
@@ -61,6 +61,18 @@ struct ish_hw {
        void __iomem *mem_addr;
 };
 
+/*
+ * ISH FW status type
+ */
+enum {
+       FWSTS_AFTER_RESET               = 0,
+       FWSTS_WAIT_FOR_HOST             = 4,
+       FWSTS_START_KERNEL_DMA          = 5,
+       FWSTS_FW_IS_RUNNING             = 7,
+       FWSTS_SENSOR_APP_LOADED         = 8,
+       FWSTS_SENSOR_APP_RUNNING        = 15
+};
+
 #define to_ish_hw(dev) (struct ish_hw *)((dev)->hw)
 
 irqreturn_t ish_irq_handler(int irq, void *dev_id);
index 34c95de..393f2e3 100644 (file)
@@ -206,12 +206,15 @@ static void ish_remove(struct pci_dev *pdev)
 #ifdef CONFIG_PM
 static struct device *ish_resume_device;
 
+/* 50ms to get resume response */
+#define WAIT_FOR_RESUME_ACK_MS         50
+
 /**
  * ish_resume_handler() - Work function to complete resume
  * @work:      work struct
  *
  * The resume work function to complete resume function asynchronously.
- * There are two types of platforms, one where ISH is not powered off,
+ * There are two resume paths, one where ISH is not powered off,
  * in that case a simple resume message is enough, others we need
  * a reset sequence.
  */
@@ -219,20 +222,31 @@ static void ish_resume_handler(struct work_struct *work)
 {
        struct pci_dev *pdev = to_pci_dev(ish_resume_device);
        struct ishtp_device *dev = pci_get_drvdata(pdev);
+       uint32_t fwsts;
        int ret;
 
-       ishtp_send_resume(dev);
+       /* Get ISH FW status */
+       fwsts = IPC_GET_ISH_FWSTS(dev->ops->get_fw_status(dev));
 
-       /* 50 ms to get resume response */
-       if (dev->resume_flag)
-               ret = wait_event_interruptible_timeout(dev->resume_wait,
-                                                      !dev->resume_flag,
-                                                      msecs_to_jiffies(50));
+       /*
+        * If currently, in ISH FW, sensor app is loaded or beyond that,
+        * it means ISH isn't powered off, in this case, send a resume message.
+        */
+       if (fwsts >= FWSTS_SENSOR_APP_LOADED) {
+               ishtp_send_resume(dev);
+
+               /* Waiting to get resume response */
+               if (dev->resume_flag)
+                       ret = wait_event_interruptible_timeout(dev->resume_wait,
+                               !dev->resume_flag,
+                               msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
+       }
 
        /*
-        * If no resume response. This platform  is not S0ix compatible
-        * So on resume full reboot of ISH processor will happen, so
-        * need to go through init sequence again
+        * If in ISH FW, sensor app isn't loaded yet, or no resume response.
+        * That means this platform is not S0ix compatible, or something is
+        * wrong with ISH FW. So on resume, full reboot of ISH processor will
+        * happen, so need to go through init sequence again.
         */
        if (dev->resume_flag)
                ish_init(dev);