summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
authorTomas Winkler <tomas.winkler@intel.com>2014-03-19 00:51:59 +0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-04 03:20:24 +0400
commit964a2331e9a207fc15ef9eef833212347498bea1 (patch)
tree1c444ed7f440d280c84140ec7e142c7713bb7896 /drivers/misc
parentee7e5afd2c369b64ffcf419d38ce7ad1c709a53e (diff)
downloadlinux-964a2331e9a207fc15ef9eef833212347498bea1.tar.xz
mei: expose hardware power gating state to mei layer
Since the runtime pm and the internal power gating cannot be in complete sync in regards to I/O operations, we need to expose the device hardware internal power gating state to mei layer 2. We add pg_state handler that translate the hw internal pg state to mei layer 2. We add power gating event variable to keep power track of power gating transitions Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Reviewed-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/mei/hw-me.c15
-rw-r--r--drivers/misc/mei/hw-txe.c43
-rw-r--r--drivers/misc/mei/hw-txe.h14
-rw-r--r--drivers/misc/mei/init.c2
-rw-r--r--drivers/misc/mei/mei_dev.h38
5 files changed, 93 insertions, 19 deletions
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 7a7e66250dfd..02f3b0c3a2a3 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -113,6 +113,19 @@ static void mei_me_hw_config(struct mei_device *dev)
/* Doesn't change in runtime */
dev->hbuf_depth = (hcsr & H_CBD) >> 24;
}
+
+/**
+ * mei_me_pg_state - translate internal pg state
+ * to the mei power gating state
+ *
+ * @hw - me hardware
+ * returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
+ */
+static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
+{
+ return MEI_PG_OFF;
+}
+
/**
* mei_clear_interrupts - clear and stop interrupts
*
@@ -601,6 +614,8 @@ end:
}
static const struct mei_hw_ops mei_me_hw_ops = {
+ .pg_state = mei_me_pg_state,
+
.host_is_ready = mei_me_host_is_ready,
.hw_is_ready = mei_me_hw_is_ready,
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index 49f197a956c9..4a6f567f4f80 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -158,7 +158,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req)
dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n",
hw->aliveness, req);
if (do_req) {
- hw->recvd_aliveness = false;
+ dev->pg_event = MEI_PG_EVENT_WAIT;
mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req);
}
return do_req;
@@ -213,6 +213,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
do {
hw->aliveness = mei_txe_aliveness_get(dev);
if (hw->aliveness == expected) {
+ dev->pg_event = MEI_PG_EVENT_IDLE;
dev_dbg(&dev->pdev->dev,
"aliveness settled after %d msecs\n", t);
return t;
@@ -223,6 +224,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
t += MSEC_PER_SEC / 5;
} while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
+ dev->pg_event = MEI_PG_EVENT_IDLE;
dev_err(&dev->pdev->dev, "aliveness timed out\n");
return -ETIME;
}
@@ -249,19 +251,22 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected)
return 0;
mutex_unlock(&dev->device_lock);
- err = wait_event_timeout(hw->wait_aliveness,
- hw->recvd_aliveness, timeout);
+ err = wait_event_timeout(hw->wait_aliveness_resp,
+ dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
mutex_lock(&dev->device_lock);
hw->aliveness = mei_txe_aliveness_get(dev);
ret = hw->aliveness == expected ? 0 : -ETIME;
if (ret)
- dev_err(&dev->pdev->dev, "aliveness timed out");
+ dev_warn(&dev->pdev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n",
+ err, hw->aliveness, dev->pg_event);
else
- dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n",
- jiffies_to_msecs(timeout - err));
- hw->recvd_aliveness = false;
+ dev_dbg(&dev->pdev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n",
+ jiffies_to_msecs(timeout - err),
+ hw->aliveness, dev->pg_event);
+
+ dev->pg_event = MEI_PG_EVENT_IDLE;
return ret;
}
@@ -292,6 +297,20 @@ static bool mei_txe_pg_is_enabled(struct mei_device *dev)
}
/**
+ * mei_txe_pg_state - translate aliveness register value
+ * to the mei power gating state
+ *
+ * @dev: the device structure
+ *
+ * returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
+ */
+static inline enum mei_pg_state mei_txe_pg_state(struct mei_device *dev)
+{
+ struct mei_txe_hw *hw = to_txe_hw(dev);
+ return hw->aliveness ? MEI_PG_OFF : MEI_PG_ON;
+}
+
+/**
* mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt
*
* @dev: the device structure
@@ -972,9 +991,9 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
/* Clear the interrupt cause */
dev_dbg(&dev->pdev->dev,
"Aliveness Interrupt: Status: %d\n", hw->aliveness);
- hw->recvd_aliveness = true;
- if (waitqueue_active(&hw->wait_aliveness))
- wake_up(&hw->wait_aliveness);
+ dev->pg_event = MEI_PG_EVENT_RECEIVED;
+ if (waitqueue_active(&hw->wait_aliveness_resp))
+ wake_up(&hw->wait_aliveness_resp);
}
@@ -1024,6 +1043,8 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
.host_is_ready = mei_txe_host_is_ready,
+ .pg_state = mei_txe_pg_state,
+
.hw_is_ready = mei_txe_hw_is_ready,
.hw_reset = mei_txe_hw_reset,
.hw_config = mei_txe_hw_config,
@@ -1069,7 +1090,7 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
hw = to_txe_hw(dev);
- init_waitqueue_head(&hw->wait_aliveness);
+ init_waitqueue_head(&hw->wait_aliveness_resp);
dev->ops = &mei_txe_hw_ops;
diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h
index 0812d98633a4..799f9f25f424 100644
--- a/drivers/misc/mei/hw-txe.h
+++ b/drivers/misc/mei/hw-txe.h
@@ -35,12 +35,11 @@
/**
* struct mei_txe_hw - txe hardware specifics
*
- * @mem_addr: SeC and BRIDGE bars
- * @aliveness: aliveness (power gating) state of the hardware
- * @readiness: readiness state of the hardware
- * @wait_aliveness: aliveness wait queue
- * @recvd_aliveness: aliveness interrupt was recived
- * @intr_cause: translated interrupt cause
+ * @mem_addr: SeC and BRIDGE bars
+ * @aliveness: aliveness (power gating) state of the hardware
+ * @readiness: readiness state of the hardware
+ * @wait_aliveness_resp: aliveness wait queue
+ * @intr_cause: translated interrupt cause
*/
struct mei_txe_hw {
void __iomem *mem_addr[NUM_OF_MEM_BARS];
@@ -48,8 +47,7 @@ struct mei_txe_hw {
u32 readiness;
u32 slots;
- wait_queue_head_t wait_aliveness;
- bool recvd_aliveness;
+ wait_queue_head_t wait_aliveness_resp;
unsigned long intr_cause;
};
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index cc604e1d9457..4b1eb37344cb 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -341,6 +341,8 @@ void mei_device_init(struct mei_device *dev)
* 0: Reserved for MEI Bus Message communications
*/
bitmap_set(dev->host_clients_map, 0, 1);
+
+ dev->pg_event = MEI_PG_EVENT_IDLE;
}
EXPORT_SYMBOL_GPL(mei_device_init);
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index ca7581ce0722..bec6adef68bd 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -220,6 +220,7 @@ struct mei_cl {
* @hw_start - start hw after reset
* @hw_config - configure hw
+ * @pg_state - power gating state of the device
* @pg_is_enabled - is power gating enabled
* @intr_clear - clear pending interrupts
@@ -246,6 +247,7 @@ struct mei_hw_ops {
int (*hw_start)(struct mei_device *dev);
void (*hw_config)(struct mei_device *dev);
+ enum mei_pg_state (*pg_state)(struct mei_device *dev);
bool (*pg_is_enabled)(struct mei_device *dev);
void (*intr_clear)(struct mei_device *dev);
@@ -335,11 +337,37 @@ struct mei_cl_device {
void *priv_data;
};
+
+ /**
+ * enum mei_pg_event - power gating transition events
+ *
+ * @MEI_PG_EVENT_IDLE: the driver is not in power gating transition
+ * @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete
+ * @MEI_PG_EVENT_RECEIVED: the driver received pg event
+ */
+enum mei_pg_event {
+ MEI_PG_EVENT_IDLE,
+ MEI_PG_EVENT_WAIT,
+ MEI_PG_EVENT_RECEIVED,
+};
+
+/**
+ * enum mei_pg_state - device internal power gating state
+ *
+ * @MEI_PG_OFF: device is not power gated - it is active
+ * @MEI_PG_ON: device is power gated - it is in lower power state
+ */
+enum mei_pg_state {
+ MEI_PG_OFF = 0,
+ MEI_PG_ON = 1,
+};
+
/**
* struct mei_device - MEI private device struct
* @reset_count - limits the number of consecutive resets
* @hbm_state - state of host bus message protocol
+ * @pg_event - power gating event
* @mem_addr - mem mapped base register address
* @hbuf_depth - depth of hardware host/write buffer is slots
@@ -387,6 +415,11 @@ struct mei_device {
enum mei_hbm_state hbm_state;
u16 init_clients_timer;
+ /*
+ * Power Gating support
+ */
+ enum mei_pg_event pg_event;
+
unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */
u32 rd_msg_hdr;
@@ -563,6 +596,11 @@ static inline void mei_hw_config(struct mei_device *dev)
dev->ops->hw_config(dev);
}
+static inline enum mei_pg_state mei_pg_state(struct mei_device *dev)
+{
+ return dev->ops->pg_state(dev);
+}
+
static inline bool mei_pg_is_enabled(struct mei_device *dev)
{
return dev->ops->pg_is_enabled(dev);