mei: me: add pg exit and entry flow commands
authorTomas Winkler <tomas.winkler@intel.com>
Tue, 18 Mar 2014 20:52:00 +0000 (22:52 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 3 May 2014 23:20:24 +0000 (19:20 -0400)
For power gating entry we write hbm pg entry request command and
then we set pg register
For power gating exit we clear pg register and wait for exit request
hbm command.

Exit power gating request might also be initiated by the firmware
w/o explicit driver request

The power gating state is tracked by pg_state member of me_hw

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/mei/hbm.c
drivers/misc/mei/hw-me.c
drivers/misc/mei/hw-me.h
drivers/misc/mei/hw.h

index a16b47c855aa48262eb4f78f7c2e26b7cdbad104..a725365e2150827d636eb9d77f904e94390ae57a 100644 (file)
@@ -732,12 +732,14 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
 
        case MEI_PG_ISOLATION_ENTRY_RES_CMD:
                dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
+               dev->pg_event = MEI_PG_EVENT_RECEIVED;
                if (waitqueue_active(&dev->wait_pg))
                        wake_up(&dev->wait_pg);
                break;
 
        case MEI_PG_ISOLATION_EXIT_REQ_CMD:
                dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n");
+               dev->pg_event = MEI_PG_EVENT_RECEIVED;
                if (waitqueue_active(&dev->wait_pg))
                        wake_up(&dev->wait_pg);
                break;
index 02f3b0c3a2a3330659e021cb3791ff33009219ed..4b048b67a248375e1c033ec675d41e4452808ef1 100644 (file)
@@ -109,9 +109,12 @@ static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr)
  */
 static void mei_me_hw_config(struct mei_device *dev)
 {
+       struct mei_me_hw *hw = to_me_hw(dev);
        u32 hcsr = mei_hcsr_read(to_me_hw(dev));
        /* Doesn't change in runtime */
        dev->hbuf_depth = (hcsr & H_CBD) >> 24;
+
+       hw->pg_state = MEI_PG_OFF;
 }
 
 /**
@@ -123,7 +126,8 @@ static void mei_me_hw_config(struct mei_device *dev)
  */
 static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
 {
-       return MEI_PG_OFF;
+       struct mei_me_hw *hw = to_me_hw(dev);
+       return hw->pg_state;
 }
 
 /**
@@ -472,6 +476,80 @@ static void mei_me_pg_exit(struct mei_device *dev)
        mei_me_reg_write(hw, H_HPG_CSR, reg);
 }
 
+/**
+ * mei_me_pg_set_sync - perform pg entry procedure
+ *
+ * @dev: the device structure
+ *
+ * returns 0 on success an error code otherwise
+ */
+int mei_me_pg_set_sync(struct mei_device *dev)
+{
+       struct mei_me_hw *hw = to_me_hw(dev);
+       unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT);
+       int ret;
+
+       dev->pg_event = MEI_PG_EVENT_WAIT;
+
+       ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD);
+       if (ret)
+               return ret;
+
+       mutex_unlock(&dev->device_lock);
+       wait_event_timeout(dev->wait_pg,
+               dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
+       mutex_lock(&dev->device_lock);
+
+       if (dev->pg_event == MEI_PG_EVENT_RECEIVED) {
+               mei_me_pg_enter(dev);
+               ret = 0;
+       } else {
+               ret = -ETIME;
+       }
+
+       dev->pg_event = MEI_PG_EVENT_IDLE;
+       hw->pg_state = MEI_PG_ON;
+
+       return ret;
+}
+
+/**
+ * mei_me_pg_unset_sync - perform pg exit procedure
+ *
+ * @dev: the device structure
+ *
+ * returns 0 on success an error code otherwise
+ */
+int mei_me_pg_unset_sync(struct mei_device *dev)
+{
+       struct mei_me_hw *hw = to_me_hw(dev);
+       unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT);
+       int ret;
+
+       if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
+               goto reply;
+
+       dev->pg_event = MEI_PG_EVENT_WAIT;
+
+       mei_me_pg_exit(dev);
+
+       mutex_unlock(&dev->device_lock);
+       wait_event_timeout(dev->wait_pg,
+               dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
+       mutex_lock(&dev->device_lock);
+
+reply:
+       if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
+               ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
+       else
+               ret = -ETIME;
+
+       dev->pg_event = MEI_PG_EVENT_IDLE;
+       hw->pg_state = MEI_PG_OFF;
+
+       return ret;
+}
+
 /**
  * mei_me_pg_is_enabled - detect if PG is supported by HW
  *
@@ -601,9 +679,15 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
 
        dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
 
-       rets = mei_irq_write_handler(dev, &complete_list);
-
-       dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
+       /*
+        * During PG handshake only allowed write is the replay to the
+        * PG exit message, so block calling write function
+        * if the pg state is not idle
+        */
+       if (dev->pg_event == MEI_PG_EVENT_IDLE) {
+               rets = mei_irq_write_handler(dev, &complete_list);
+               dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
+       }
 
        mei_irq_compl_handler(dev, &complete_list);
 
index 893d5119fa9bb6eec02d6b82f4452611f4637f7b..8b412820cc6689cf6ceedcc95152b18fddf12972 100644 (file)
@@ -31,12 +31,16 @@ struct mei_me_hw {
         */
        u32 host_hw_state;
        u32 me_hw_state;
+       enum mei_pg_state pg_state;
 };
 
 #define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw)
 
 struct mei_device *mei_me_dev_init(struct pci_dev *pdev);
 
+int mei_me_pg_set_sync(struct mei_device *dev);
+int mei_me_pg_unset_sync(struct mei_device *dev);
+
 irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);
 irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id);
 
index ec4a91a534e65369d36231b6dcf17b8e65dd634a..9bad9cf7a8a02844e4642df99ba8c4a635d6c298 100644 (file)
@@ -31,6 +31,7 @@
 #define MEI_IAMTHIF_STALL_TIMER    12  /* HPS */
 #define MEI_IAMTHIF_READ_TIMER     10  /* HPS */
 
+#define MEI_PGI_TIMEOUT            1  /* PG Isolation time response 1 sec */
 #define MEI_HBM_TIMEOUT            1   /* 1 second */
 
 /*