* [char-misc-next 01/13] mei: implement power gating isolation hbm layer
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
@ 2014-03-18 20:51 ` Tomas Winkler
2014-03-18 20:51 ` [char-misc-next 02/13] mei: me: introduce power gating registers Tomas Winkler
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:51 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
Add send message functions and receive dispatch stubs
for power gating isolation hbm protocol.
The protocol consist of requests for entering and exiting
the power gating isolation state and their responses.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
drivers/misc/mei/hbm.c | 41 +++++++++++++++++++++++++++++++++++++++++
drivers/misc/mei/hbm.h | 1 +
drivers/misc/mei/hw.h | 16 ++++++++++++++++
drivers/misc/mei/init.c | 1 +
drivers/misc/mei/mei_dev.h | 1 +
5 files changed, 60 insertions(+)
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 4960288..a16b47c 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -14,6 +14,7 @@
*
*/
+#include <linux/export.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/wait.h>
@@ -289,6 +290,34 @@ static int mei_hbm_prop_req(struct mei_device *dev)
return 0;
}
+/*
+ * mei_hbm_pg - sends pg command
+ *
+ * @dev: the device structure
+ * @pg_cmd: the pg command code
+ *
+ * This function returns -EIO on write failure
+ */
+int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
+{
+ struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
+ struct hbm_power_gate *req;
+ const size_t len = sizeof(struct hbm_power_gate);
+ int ret;
+
+ mei_hbm_hdr(mei_hdr, len);
+
+ req = (struct hbm_power_gate *)dev->wr_msg.data;
+ memset(req, 0, len);
+ req->hbm_cmd = pg_cmd;
+
+ ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
+ if (ret)
+ dev_err(&dev->pdev->dev, "power gate command write failed.\n");
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mei_hbm_pg);
+
/**
* mei_hbm_stop_req - send stop request message
*
@@ -701,6 +730,18 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
mei_hbm_cl_flow_control_res(dev, flow_control);
break;
+ case MEI_PG_ISOLATION_ENTRY_RES_CMD:
+ dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
+ 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");
+ if (waitqueue_active(&dev->wait_pg))
+ wake_up(&dev->wait_pg);
+ break;
+
case HOST_CLIENT_PROPERTIES_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h
index 20e8782..8e39cee 100644
--- a/drivers/misc/mei/hbm.h
+++ b/drivers/misc/mei/hbm.h
@@ -57,6 +57,7 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl);
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
bool mei_hbm_version_is_supported(struct mei_device *dev);
+int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd);
#endif /* _MEI_HBM_H_ */
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 6b476ab..1d70968 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -69,6 +69,11 @@
#define MEI_FLOW_CONTROL_CMD 0x08
+#define MEI_PG_ISOLATION_ENTRY_REQ_CMD 0x0a
+#define MEI_PG_ISOLATION_ENTRY_RES_CMD 0x8a
+#define MEI_PG_ISOLATION_EXIT_REQ_CMD 0x0b
+#define MEI_PG_ISOLATION_EXIT_RES_CMD 0x8b
+
/*
* MEI Stop Reason
* used by hbm_host_stop_request.reason
@@ -208,6 +213,17 @@ struct hbm_props_response {
} __packed;
/**
+ * struct hbm_power_gate - power gate request/response
+ *
+ * @hbm_cmd - bus message command header
+ * @reserved[3]
+ */
+struct hbm_power_gate {
+ u8 hbm_cmd;
+ u8 reserved[3];
+} __packed;
+
+/**
* struct hbm_client_connect_request - connect/disconnect request
*
* @hbm_cmd - bus message command header
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index 4460975..cc604e1 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -312,6 +312,7 @@ void mei_device_init(struct mei_device *dev)
INIT_LIST_HEAD(&dev->device_list);
mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_hw_ready);
+ init_waitqueue_head(&dev->wait_pg);
init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd);
dev->dev_state = MEI_DEV_INITIALIZING;
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 94a5167..4d4c041 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -371,6 +371,7 @@ struct mei_device {
* waiting queue for receive message from FW
*/
wait_queue_head_t wait_hw_ready;
+ wait_queue_head_t wait_pg;
wait_queue_head_t wait_recvd_msg;
wait_queue_head_t wait_stop_wd;
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 02/13] mei: me: introduce power gating registers
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
2014-03-18 20:51 ` [char-misc-next 01/13] mei: implement power gating isolation hbm layer Tomas Winkler
@ 2014-03-18 20:51 ` Tomas Winkler
2014-03-18 20:51 ` [char-misc-next 03/13] mei: me: add power gating isolation register write wrappers Tomas Winkler
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:51 UTC (permalink / raw)
To: gregkh; +Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler
LPT devices have internal power gating handled
through registers and hbm calls
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
drivers/misc/mei/hw-me-regs.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index 66f411a..72ba4d5 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -128,6 +128,8 @@
#define ME_CB_RW 8
/* ME_CSR_HA - ME Control Status Host Access register (read only) */
#define ME_CSR_HA 0xC
+/* H_HGC_CSR - PGI register */
+#define H_HPG_CSR 0x10
/* register bits of H_CSR (Host Control Status register) */
@@ -157,6 +159,8 @@ access to ME_CBD */
#define ME_CBWP_HRA 0x00FF0000
/* ME CB Read Pointer HRA - host read only access to ME_CBRP */
#define ME_CBRP_HRA 0x0000FF00
+/* ME Power Gate Isolation Capability HRA - host ready only access */
+#define ME_PGIC_HRA 0x00000040
/* ME Reset HRA - host read only access to ME_RST */
#define ME_RST_HRA 0x00000010
/* ME Ready HRA - host read only access to ME_RDY */
@@ -168,4 +172,9 @@ access to ME_CBD */
/* ME Interrupt Enable HRA - host read only access to ME_IE */
#define ME_IE_HRA 0x00000001
+
+/* register bits - H_HPG_CSR */
+#define H_HPG_CSR_PGIHEXR 0x00000001
+#define H_HPG_CSR_PGI 0x00000002
+
#endif /* _MEI_HW_MEI_REGS_H_ */
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 03/13] mei: me: add power gating isolation register write wrappers
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
2014-03-18 20:51 ` [char-misc-next 01/13] mei: implement power gating isolation hbm layer Tomas Winkler
2014-03-18 20:51 ` [char-misc-next 02/13] mei: me: introduce power gating registers Tomas Winkler
@ 2014-03-18 20:51 ` Tomas Winkler
2014-03-18 20:51 ` [char-misc-next 04/13] mei: condition PGI support on HW and HBM version Tomas Winkler
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:51 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
Add entry and exit power gating isolation register
write handler.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
drivers/misc/mei/hw-me.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 8dbdaae..fc5d001 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -431,6 +431,35 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
}
/**
+ * mei_me_pg_enter - write pg enter register to mei device.
+ *
+ * @dev: the device structure
+ */
+static void mei_me_pg_enter(struct mei_device *dev)
+{
+ struct mei_me_hw *hw = to_me_hw(dev);
+ u32 reg = mei_me_reg_read(hw, H_HPG_CSR);
+ reg |= H_HPG_CSR_PGI;
+ mei_me_reg_write(hw, H_HPG_CSR, reg);
+}
+
+/**
+ * mei_me_pg_enter - write pg enter register to mei device.
+ *
+ * @dev: the device structure
+ */
+static void mei_me_pg_exit(struct mei_device *dev)
+{
+ struct mei_me_hw *hw = to_me_hw(dev);
+ u32 reg = mei_me_reg_read(hw, H_HPG_CSR);
+
+ WARN(!(reg & H_HPG_CSR_PGI), "PGI is not set\n");
+
+ reg |= H_HPG_CSR_PGIHEXR;
+ mei_me_reg_write(hw, H_HPG_CSR, reg);
+}
+
+/**
* mei_me_irq_quick_handler - The ISR of the MEI device
*
* @irq: The irq number
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 04/13] mei: condition PGI support on HW and HBM version
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (2 preceding siblings ...)
2014-03-18 20:51 ` [char-misc-next 03/13] mei: me: add power gating isolation register write wrappers Tomas Winkler
@ 2014-03-18 20:51 ` Tomas Winkler
2014-03-18 20:51 ` [char-misc-next 05/13] mei: expose hardware power gating state to mei layer Tomas Winkler
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:51 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
Enable power gating isolation only if hw and fw support it.
This is indicated by ME_PGIC_HRA bit in ME_CSR_HA register
and on HBM protocol version.
The information is exported to MEI layer through
new pg_is_enabled hw op.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
drivers/misc/mei/hw-me.c | 37 +++++++++++++++++++++++++++++++++++++
drivers/misc/mei/hw-txe.c | 14 ++++++++++++++
drivers/misc/mei/hw.h | 6 ++++++
drivers/misc/mei/mei_dev.h | 10 ++++++++++
4 files changed, 67 insertions(+)
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index fc5d001..7a7e662 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -460,6 +460,41 @@ static void mei_me_pg_exit(struct mei_device *dev)
}
/**
+ * mei_me_pg_is_enabled - detect if PG is supported by HW
+ *
+ * @dev: the device structure
+ *
+ * returns: true is pg supported, false otherwise
+ */
+static bool mei_me_pg_is_enabled(struct mei_device *dev)
+{
+ struct mei_me_hw *hw = to_me_hw(dev);
+ u32 reg = mei_me_reg_read(hw, ME_CSR_HA);
+
+ if ((reg & ME_PGIC_HRA) == 0)
+ goto notsupported;
+
+ if (dev->version.major_version < HBM_MAJOR_VERSION_PGI)
+ goto notsupported;
+
+ if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
+ dev->version.minor_version < HBM_MINOR_VERSION_PGI)
+ goto notsupported;
+
+ return true;
+
+notsupported:
+ dev_dbg(&dev->pdev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n",
+ !!(reg & ME_PGIC_HRA),
+ dev->version.major_version,
+ dev->version.minor_version,
+ HBM_MAJOR_VERSION_PGI,
+ HBM_MINOR_VERSION_PGI);
+
+ return false;
+}
+
+/**
* mei_me_irq_quick_handler - The ISR of the MEI device
*
* @irq: The irq number
@@ -573,6 +608,8 @@ static const struct mei_hw_ops mei_me_hw_ops = {
.hw_config = mei_me_hw_config,
.hw_start = mei_me_hw_start,
+ .pg_is_enabled = mei_me_pg_is_enabled,
+
.intr_clear = mei_me_intr_clear,
.intr_enable = mei_me_intr_enable,
.intr_disable = mei_me_intr_disable,
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index f60182a..49f197a 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -280,6 +280,18 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
}
/**
+ * mei_txe_pg_is_enabled - detect if PG is supported by HW
+ *
+ * @dev: the device structure
+ *
+ * returns: true is pg supported, false otherwise
+ */
+static bool mei_txe_pg_is_enabled(struct mei_device *dev)
+{
+ return true;
+}
+
+/**
* mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt
*
* @dev: the device structure
@@ -1017,6 +1029,8 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
.hw_config = mei_txe_hw_config,
.hw_start = mei_txe_hw_start,
+ .pg_is_enabled = mei_txe_pg_is_enabled,
+
.intr_clear = mei_txe_intr_clear,
.intr_enable = mei_txe_intr_enable,
.intr_disable = mei_txe_intr_disable,
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 1d70968..ec4a91a 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -39,6 +39,12 @@
#define HBM_MINOR_VERSION 0
#define HBM_MAJOR_VERSION 1
+/*
+ * MEI version with PGI support
+ */
+#define HBM_MINOR_VERSION_PGI 1
+#define HBM_MAJOR_VERSION_PGI 1
+
/* Host bus message command opcode */
#define MEI_HBM_CMD_OP_MSK 0x7f
/* Host bus message command RESPONSE */
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 4d4c041..ca7581c 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -220,6 +220,8 @@ struct mei_cl {
* @hw_start - start hw after reset
* @hw_config - configure hw
+ * @pg_is_enabled - is power gating enabled
+
* @intr_clear - clear pending interrupts
* @intr_enable - enable interrupts
* @intr_disable - disable interrupts
@@ -244,6 +246,8 @@ struct mei_hw_ops {
int (*hw_start)(struct mei_device *dev);
void (*hw_config)(struct mei_device *dev);
+ bool (*pg_is_enabled)(struct mei_device *dev);
+
void (*intr_clear)(struct mei_device *dev);
void (*intr_enable)(struct mei_device *dev);
void (*intr_disable)(struct mei_device *dev);
@@ -558,6 +562,12 @@ static inline void mei_hw_config(struct mei_device *dev)
{
dev->ops->hw_config(dev);
}
+
+static inline bool mei_pg_is_enabled(struct mei_device *dev)
+{
+ return dev->ops->pg_is_enabled(dev);
+}
+
static inline int mei_hw_reset(struct mei_device *dev, bool enable)
{
return dev->ops->hw_reset(dev, enable);
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 05/13] mei: expose hardware power gating state to mei layer
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (3 preceding siblings ...)
2014-03-18 20:51 ` [char-misc-next 04/13] mei: condition PGI support on HW and HBM version Tomas Winkler
@ 2014-03-18 20:51 ` Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 06/13] mei: me: add pg exit and entry flow commands Tomas Winkler
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:51 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
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>
---
drivers/misc/mei/hw-me.c | 15 +++++++++++++++
drivers/misc/mei/hw-txe.c | 43 ++++++++++++++++++++++++++++++++-----------
drivers/misc/mei/hw-txe.h | 14 ++++++--------
drivers/misc/mei/init.c | 2 ++
drivers/misc/mei/mei_dev.h | 38 ++++++++++++++++++++++++++++++++++++++
5 files changed, 93 insertions(+), 19 deletions(-)
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 7a7e662..02f3b0c 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 49f197a..4a6f567 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 0812d98..799f9f2 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 cc604e1..4b1eb37 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 ca7581c..bec6ade 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);
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 06/13] mei: me: add pg exit and entry flow commands
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (4 preceding siblings ...)
2014-03-18 20:51 ` [char-misc-next 05/13] mei: expose hardware power gating state to mei layer Tomas Winkler
@ 2014-03-18 20:52 ` Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 07/13] mei: add function to check write queues Tomas Winkler
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:52 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
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>
---
drivers/misc/mei/hbm.c | 2 ++
drivers/misc/mei/hw-me.c | 92 +++++++++++++++++++++++++++++++++++++++++++++---
drivers/misc/mei/hw-me.h | 4 +++
drivers/misc/mei/hw.h | 1 +
4 files changed, 95 insertions(+), 4 deletions(-)
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index a16b47c..a725365 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -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;
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 02f3b0c..4b048b6 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -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;
}
/**
@@ -473,6 +477,80 @@ static void mei_me_pg_exit(struct mei_device *dev)
}
/**
+ * 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
*
* @dev: the device structure
@@ -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);
diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h
index 80bd829..1d72b55 100644
--- a/drivers/misc/mei/hw-me.h
+++ b/drivers/misc/mei/hw-me.h
@@ -30,12 +30,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);
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index ec4a91a..9bad9cf 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -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 */
/*
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 07/13] mei: add function to check write queues
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (5 preceding siblings ...)
2014-03-18 20:52 ` [char-misc-next 06/13] mei: me: add pg exit and entry flow commands Tomas Winkler
@ 2014-03-18 20:52 ` Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 08/13] mei: me: add runtime pm framework Tomas Winkler
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:52 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
The driver needs to check whether the write
queue idle before entering power gating
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
drivers/misc/mei/init.c | 21 +++++++++++++++++++++
drivers/misc/mei/mei_dev.h | 2 ++
2 files changed, 23 insertions(+)
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index 4b1eb37..abc5ea0 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -303,7 +303,28 @@ void mei_stop(struct mei_device *dev)
}
EXPORT_SYMBOL_GPL(mei_stop);
+/**
+ * mei_write_is_idle - check if the write queues are idle
+ *
+ * @dev: the device structure
+ *
+ * returns true of there is no pending write
+ */
+bool mei_write_is_idle(struct mei_device *dev)
+{
+ bool idle = (dev->dev_state == MEI_DEV_ENABLED &&
+ list_empty(&dev->ctrl_wr_list.list) &&
+ list_empty(&dev->write_list.list));
+ dev_dbg(&dev->pdev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n",
+ idle,
+ mei_dev_state_str(dev->dev_state),
+ list_empty(&dev->ctrl_wr_list.list),
+ list_empty(&dev->write_list.list));
+
+ return idle;
+}
+EXPORT_SYMBOL_GPL(mei_write_is_idle);
void mei_device_init(struct mei_device *dev)
{
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index bec6ade..fe76e5b 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -680,6 +680,8 @@ static inline int mei_count_full_read_slots(struct mei_device *dev)
bool mei_hbuf_acquire(struct mei_device *dev);
+bool mei_write_is_idle(struct mei_device *dev);
+
#if IS_ENABLED(CONFIG_DEBUG_FS)
int mei_dbgfs_register(struct mei_device *dev, const char *name);
void mei_dbgfs_deregister(struct mei_device *dev);
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 08/13] mei: me: add runtime pm framework
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (6 preceding siblings ...)
2014-03-18 20:52 ` [char-misc-next 07/13] mei: add function to check write queues Tomas Winkler
@ 2014-03-18 20:52 ` Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 09/13] mei: txe: " Tomas Winkler
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:52 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
Add runtime pm framework for ME devices.
The runtime pm handlers are used to run
me power gating isolation protocol
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
drivers/misc/mei/hbm.c | 8 +++++
drivers/misc/mei/hw-me.h | 2 ++
drivers/misc/mei/pci-me.c | 92 +++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index a725365..b9a4bb5 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mei.h>
+#include <linux/pm_runtime.h>
#include "mei_dev.h"
#include "hbm.h"
@@ -742,6 +743,13 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
dev->pg_event = MEI_PG_EVENT_RECEIVED;
if (waitqueue_active(&dev->wait_pg))
wake_up(&dev->wait_pg);
+ else
+ /*
+ * If the driver is not waiting on this then
+ * this is HW initiated exit from PG.
+ * Start runtime pm resume sequence to exit from PG.
+ */
+ pm_request_resume(&dev->pdev->dev);
break;
case HOST_CLIENT_PROPERTIES_RES_CMD:
diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h
index 1d72b55..dbea454 100644
--- a/drivers/misc/mei/hw-me.h
+++ b/drivers/misc/mei/hw-me.h
@@ -23,6 +23,8 @@
#include "mei_dev.h"
#include "client.h"
+#define MEI_ME_RPM_TIMEOUT 500 /* ms */
+
struct mei_me_hw {
void __iomem *mem_addr;
/*
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 1c8fd3a..967f2f2 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -33,6 +33,8 @@
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
+#include <linux/pm_runtime.h>
+
#include <linux/mei.h>
#include "mei_dev.h"
@@ -196,6 +198,9 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto release_irq;
}
+ pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT);
+ pm_runtime_use_autosuspend(&pdev->dev);
+
err = mei_register(dev);
if (err)
goto release_irq;
@@ -204,6 +209,9 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
schedule_delayed_work(&dev->timer_work, HZ);
+ if (mei_pg_is_enabled(dev))
+ pm_runtime_put_noidle(&pdev->dev);
+
dev_dbg(&pdev->dev, "initialization successful.\n");
return 0;
@@ -243,6 +251,9 @@ static void mei_me_remove(struct pci_dev *pdev)
if (!dev)
return;
+ if (mei_pg_is_enabled(dev))
+ pm_runtime_get_noresume(&pdev->dev);
+
hw = to_me_hw(dev);
@@ -327,12 +338,89 @@ static int mei_me_pci_resume(struct device *device)
return 0;
}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int mei_me_pm_runtime_idle(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct mei_device *dev;
+
+ dev_dbg(&pdev->dev, "rpm: me: runtime_idle\n");
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return -ENODEV;
+ if (mei_write_is_idle(dev))
+ pm_schedule_suspend(device, MEI_ME_RPM_TIMEOUT * 2);
+
+ return -EBUSY;
+}
+
+static int mei_me_pm_runtime_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct mei_device *dev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "rpm: me: runtime suspend\n");
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->device_lock);
+
+ if (mei_write_is_idle(dev))
+ ret = mei_me_pg_set_sync(dev);
+ else
+ ret = -EAGAIN;
+
+ mutex_unlock(&dev->device_lock);
+
+ dev_dbg(&pdev->dev, "rpm: me: runtime suspend ret=%d\n", ret);
+
+ return ret;
+}
+
+static int mei_me_pm_runtime_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct mei_device *dev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "rpm: me: runtime resume\n");
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->device_lock);
+
+ ret = mei_me_pg_unset_sync(dev);
+
+ mutex_unlock(&dev->device_lock);
+
+ dev_dbg(&pdev->dev, "rpm: me: runtime resume ret = %d\n", ret);
+
+ return ret;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops mei_me_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mei_me_pci_suspend,
+ mei_me_pci_resume)
+ SET_RUNTIME_PM_OPS(
+ mei_me_pm_runtime_suspend,
+ mei_me_pm_runtime_resume,
+ mei_me_pm_runtime_idle)
+};
-static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
#define MEI_ME_PM_OPS (&mei_me_pm_ops)
#else
#define MEI_ME_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM */
/*
* PCI driver structure
*/
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 09/13] mei: txe: add runtime pm framework
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (7 preceding siblings ...)
2014-03-18 20:52 ` [char-misc-next 08/13] mei: me: add runtime pm framework Tomas Winkler
@ 2014-03-18 20:52 ` Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 10/13] mei: use runtime pm in write and read flow Tomas Winkler
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:52 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Alexander Usyskin, Tomas Winkler
From: Alexander Usyskin <alexander.usyskin@intel.com>
Add runtime pm framework for TXE devices.
The runtime pm handlers are used to run
txe power gating isolation protocol.
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
drivers/misc/mei/hw-txe.h | 2 +
drivers/misc/mei/pci-txe.c | 101 +++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 99 insertions(+), 4 deletions(-)
diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h
index 799f9f2..e8dd2d1 100644
--- a/drivers/misc/mei/hw-txe.h
+++ b/drivers/misc/mei/hw-txe.h
@@ -22,6 +22,8 @@
#include "hw.h"
#include "hw-txe-regs.h"
+#define MEI_TXI_RPM_TIMEOUT 500 /* ms */
+
/* Flatten Hierarchy interrupt cause */
#define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */
#define TXE_INTR_READINESS HISR_INT_0_STS
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index ad3adb0..31d86e7 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -27,6 +27,7 @@
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
#include <linux/mei.h>
@@ -137,12 +138,17 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto release_irq;
}
+ pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
+ pm_runtime_use_autosuspend(&pdev->dev);
+
err = mei_register(dev);
if (err)
goto release_irq;
pci_set_drvdata(pdev, dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
return 0;
release_irq:
@@ -187,6 +193,8 @@ static void mei_txe_remove(struct pci_dev *pdev)
return;
}
+ pm_runtime_get_noresume(&pdev->dev);
+
hw = to_txe_hw(dev);
mei_stop(dev);
@@ -265,15 +273,100 @@ static int mei_txe_pci_resume(struct device *device)
return err;
}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int mei_txe_pm_runtime_idle(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct mei_device *dev;
+
+ dev_dbg(&pdev->dev, "rpm: txe: runtime_idle\n");
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return -ENODEV;
+ if (mei_write_is_idle(dev))
+ pm_schedule_suspend(device, MEI_TXI_RPM_TIMEOUT * 2);
+
+ return -EBUSY;
+}
+static int mei_txe_pm_runtime_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct mei_device *dev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "rpm: txe: runtime suspend\n");
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return -ENODEV;
-static SIMPLE_DEV_PM_OPS(mei_txe_pm_ops,
- mei_txe_pci_suspend,
- mei_txe_pci_resume);
+ mutex_lock(&dev->device_lock);
+
+ if (mei_write_is_idle(dev))
+ ret = mei_txe_aliveness_set_sync(dev, 0);
+ else
+ ret = -EAGAIN;
+
+ /*
+ * If everything is okay we're about to enter PCI low
+ * power state (D3) therefor we need to disable the
+ * interrupts towards host.
+ * However if device is not wakeable we do not enter
+ * D-low state and we need to keep the interrupt kicking
+ */
+ if (!ret && pci_dev_run_wake(pdev))
+ mei_disable_interrupts(dev);
+
+ dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
+
+ mutex_unlock(&dev->device_lock);
+ return ret;
+}
+
+static int mei_txe_pm_runtime_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct mei_device *dev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "rpm: txe: runtime resume\n");
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->device_lock);
+
+ mei_enable_interrupts(dev);
+
+ ret = mei_txe_aliveness_set_sync(dev, 1);
+
+ mutex_unlock(&dev->device_lock);
+
+ dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret);
+
+ return ret;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops mei_txe_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mei_txe_pci_suspend,
+ mei_txe_pci_resume)
+ SET_RUNTIME_PM_OPS(
+ mei_txe_pm_runtime_suspend,
+ mei_txe_pm_runtime_resume,
+ mei_txe_pm_runtime_idle)
+};
#define MEI_TXE_PM_OPS (&mei_txe_pm_ops)
#else
#define MEI_TXE_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM */
+
/*
* PCI driver structure
*/
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 10/13] mei: use runtime pm in write and read flow
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (8 preceding siblings ...)
2014-03-18 20:52 ` [char-misc-next 09/13] mei: txe: " Tomas Winkler
@ 2014-03-18 20:52 ` Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 11/13] mei: me: use runtime PG pm domain for non wakeable devices Tomas Winkler
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:52 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
Take rpm token on operation start to initiate rpm resume if needed.
Mark last busy time, release token and advice rpm framework
to try to autosuspend on operation end.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
drivers/misc/mei/client.c | 84 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 71 insertions(+), 13 deletions(-)
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 36eca54..0c70459 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -18,6 +18,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
#include <linux/mei.h>
@@ -415,6 +416,10 @@ void mei_host_client_init(struct work_struct *work)
dev->reset_count = 0;
mutex_unlock(&dev->device_lock);
+
+ pm_runtime_mark_last_busy(&dev->pdev->dev);
+ dev_dbg(&dev->pdev->dev, "rpm: autosuspend\n");
+ pm_runtime_autosuspend(&dev->pdev->dev);
}
/**
@@ -425,6 +430,12 @@ void mei_host_client_init(struct work_struct *work)
*/
bool mei_hbuf_acquire(struct mei_device *dev)
{
+ if (mei_pg_state(dev) == MEI_PG_ON ||
+ dev->pg_event == MEI_PG_EVENT_WAIT) {
+ dev_dbg(&dev->pdev->dev, "device is in pg\n");
+ return false;
+ }
+
if (!dev->hbuf_is_ready) {
dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
return false;
@@ -460,9 +471,18 @@ int mei_cl_disconnect(struct mei_cl *cl)
if (cl->state != MEI_FILE_DISCONNECTING)
return 0;
+ rets = pm_runtime_get(&dev->pdev->dev);
+ if (rets < 0 && rets != -EINPROGRESS) {
+ pm_runtime_put_noidle(&dev->pdev->dev);
+ cl_err(dev, cl, "rpm: get failed %d\n", rets);
+ return rets;
+ }
+
cb = mei_io_cb_init(cl, NULL);
- if (!cb)
- return -ENOMEM;
+ if (!cb) {
+ rets = -ENOMEM;
+ goto free;
+ }
cb->fop_type = MEI_FOP_CLOSE;
if (mei_hbuf_acquire(dev)) {
@@ -494,8 +514,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
cl_err(dev, cl, "wrong status client disconnect.\n");
if (err)
- cl_dbg(dev, cl, "wait failed disconnect err=%08x\n",
- err);
+ cl_dbg(dev, cl, "wait failed disconnect err=%d\n", err);
cl_err(dev, cl, "failed to disconnect from FW client.\n");
}
@@ -503,6 +522,10 @@ int mei_cl_disconnect(struct mei_cl *cl)
mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl);
free:
+ cl_dbg(dev, cl, "rpm: autosuspend\n");
+ pm_runtime_mark_last_busy(&dev->pdev->dev);
+ pm_runtime_put_autosuspend(&dev->pdev->dev);
+
mei_io_cb_free(cb);
return rets;
}
@@ -557,6 +580,13 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
dev = cl->dev;
+ rets = pm_runtime_get(&dev->pdev->dev);
+ if (rets < 0 && rets != -EINPROGRESS) {
+ pm_runtime_put_noidle(&dev->pdev->dev);
+ cl_err(dev, cl, "rpm: get failed %d\n", rets);
+ return rets;
+ }
+
cb = mei_io_cb_init(cl, file);
if (!cb) {
rets = -ENOMEM;
@@ -596,6 +626,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
rets = cl->status;
out:
+ cl_dbg(dev, cl, "rpm: autosuspend\n");
+ pm_runtime_mark_last_busy(&dev->pdev->dev);
+ pm_runtime_put_autosuspend(&dev->pdev->dev);
+
mei_io_cb_free(cb);
return rets;
}
@@ -713,23 +747,32 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
return -ENOTTY;
}
+ rets = pm_runtime_get(&dev->pdev->dev);
+ if (rets < 0 && rets != -EINPROGRESS) {
+ pm_runtime_put_noidle(&dev->pdev->dev);
+ cl_err(dev, cl, "rpm: get failed %d\n", rets);
+ return rets;
+ }
+
cb = mei_io_cb_init(cl, NULL);
- if (!cb)
- return -ENOMEM;
+ if (!cb) {
+ rets = -ENOMEM;
+ goto out;
+ }
/* always allocate at least client max message */
length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length);
rets = mei_io_cb_alloc_resp_buf(cb, length);
if (rets)
- goto err;
+ goto out;
cb->fop_type = MEI_FOP_READ;
if (mei_hbuf_acquire(dev)) {
if (mei_hbm_cl_flow_control_req(dev, cl)) {
- cl_err(dev, cl, "flow control send failed\n");
rets = -ENODEV;
- goto err;
+ goto out;
}
+
list_add_tail(&cb->list, &dev->read_list.list);
} else {
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
@@ -737,9 +780,14 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
cl->read_cb = cb;
- return rets;
-err:
- mei_io_cb_free(cb);
+out:
+ cl_dbg(dev, cl, "rpm: autosuspend\n");
+ pm_runtime_mark_last_busy(&dev->pdev->dev);
+ pm_runtime_put_autosuspend(&dev->pdev->dev);
+
+ if (rets)
+ mei_io_cb_free(cb);
+
return rets;
}
@@ -776,7 +824,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
return rets;
if (rets == 0) {
- cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
+ cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
return 0;
}
@@ -856,6 +904,12 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size);
+ rets = pm_runtime_get(&dev->pdev->dev);
+ if (rets < 0 && rets != -EINPROGRESS) {
+ pm_runtime_put_noidle(&dev->pdev->dev);
+ cl_err(dev, cl, "rpm: get failed %d\n", rets);
+ return rets;
+ }
cb->fop_type = MEI_FOP_WRITE;
cb->buf_idx = 0;
@@ -926,6 +980,10 @@ out:
rets = buf->size;
err:
+ cl_dbg(dev, cl, "rpm: autosuspend\n");
+ pm_runtime_mark_last_busy(&dev->pdev->dev);
+ pm_runtime_put_autosuspend(&dev->pdev->dev);
+
return rets;
}
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 11/13] mei: me: use runtime PG pm domain for non wakeable devices
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (9 preceding siblings ...)
2014-03-18 20:52 ` [char-misc-next 10/13] mei: use runtime pm in write and read flow Tomas Winkler
@ 2014-03-18 20:52 ` Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 12/13] mei: txe: " Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 13/13] mei: me: bump hbm version to 1.1 to support power gating Tomas Winkler
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:52 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler, Alexander Usyskin
For non wakeable devices we can't use pci runtime framework
as we are not able to wakeup from D3 states.
Instead we create new pg runtime domain that only drives ME power
gating protocol to reduce the power consumption.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
drivers/misc/mei/mei_dev.h | 3 +++
drivers/misc/mei/pci-me.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index fe76e5b..beff9ef 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -419,6 +419,9 @@ struct mei_device {
* Power Gating support
*/
enum mei_pg_event pg_event;
+#ifdef CONFIG_PM_RUNTIME
+ struct dev_pm_domain pg_domain;
+#endif /* CONFIG_PM_RUNTIME */
unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */
u32 rd_msg_hdr;
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 967f2f2..bc3e043 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -87,6 +87,14 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);
+#ifdef CONFIG_PM_RUNTIME
+static inline void mei_me_set_pm_domain(struct mei_device *dev);
+static inline void mei_me_unset_pm_domain(struct mei_device *dev);
+#else
+static inline void mei_me_set_pm_domain(struct mei_device *dev) {}
+static inline void mei_me_unset_pm_domain(struct mei_device *dev) {}
+#endif /* CONFIG_PM_RUNTIME */
+
/**
* mei_quirk_probe - probe for devices that doesn't valid ME interface
*
@@ -209,6 +217,14 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
schedule_delayed_work(&dev->timer_work, HZ);
+ /*
+ * For not wake-able HW runtime pm framework
+ * can't be used on pci device level.
+ * Use domain runtime pm callbacks instead.
+ */
+ if (!pci_dev_run_wake(pdev))
+ mei_me_set_pm_domain(dev);
+
if (mei_pg_is_enabled(dev))
pm_runtime_put_noidle(&pdev->dev);
@@ -260,6 +276,9 @@ static void mei_me_remove(struct pci_dev *pdev)
dev_dbg(&pdev->dev, "stop\n");
mei_stop(dev);
+ if (!pci_dev_run_wake(pdev))
+ mei_me_unset_pm_domain(dev);
+
/* disable interrupts */
mei_disable_interrupts(dev);
@@ -405,6 +424,37 @@ static int mei_me_pm_runtime_resume(struct device *device)
return ret;
}
+
+/**
+ * mei_me_set_pm_domain - fill and set pm domian stucture for device
+ *
+ * @dev: mei_device
+ */
+static inline void mei_me_set_pm_domain(struct mei_device *dev)
+{
+ struct pci_dev *pdev = dev->pdev;
+
+ if (pdev->dev.bus && pdev->dev.bus->pm) {
+ dev->pg_domain.ops = *pdev->dev.bus->pm;
+
+ dev->pg_domain.ops.runtime_suspend = mei_me_pm_runtime_suspend;
+ dev->pg_domain.ops.runtime_resume = mei_me_pm_runtime_resume;
+ dev->pg_domain.ops.runtime_idle = mei_me_pm_runtime_idle;
+
+ pdev->dev.pm_domain = &dev->pg_domain;
+ }
+}
+
+/**
+ * mei_me_unset_pm_domain - clean pm domian stucture for device
+ *
+ * @dev: mei_device
+ */
+static inline void mei_me_unset_pm_domain(struct mei_device *dev)
+{
+ /* stop using pm callbacks if any */
+ dev->pdev->dev.pm_domain = NULL;
+}
#endif /* CONFIG_PM_RUNTIME */
#ifdef CONFIG_PM
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 12/13] mei: txe: use runtime PG pm domain for non wakeable devices
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (10 preceding siblings ...)
2014-03-18 20:52 ` [char-misc-next 11/13] mei: me: use runtime PG pm domain for non wakeable devices Tomas Winkler
@ 2014-03-18 20:52 ` Tomas Winkler
2014-03-18 20:52 ` [char-misc-next 13/13] mei: me: bump hbm version to 1.1 to support power gating Tomas Winkler
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:52 UTC (permalink / raw)
To: gregkh
Cc: arnd, rjw, linux-kernel, linux-pm, Alexander Usyskin, Tomas Winkler
From: Alexander Usyskin <alexander.usyskin@intel.com>
For non wakeable devices we can't use pci runtime framework
as we are not able to wakeup from D3 states.
Instead we create new pg runtime domain that only drives TXE power
gating protocol to reduce the power consumption.
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
drivers/misc/mei/pci-txe.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index 31d86e7..2c3f562 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -41,6 +41,13 @@ static const struct pci_device_id mei_txe_pci_tbl[] = {
};
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
+#ifdef CONFIG_PM_RUNTIME
+static inline void mei_txe_set_pm_domain(struct mei_device *dev);
+static inline void mei_txe_unset_pm_domain(struct mei_device *dev);
+#else
+static inline void mei_txe_set_pm_domain(struct mei_device *dev) {}
+static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {}
+#endif /* CONFIG_PM_RUNTIME */
static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
{
@@ -147,6 +154,14 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, dev);
+ /*
+ * For not wake-able HW runtime pm framework
+ * can't be used on pci device level.
+ * Use domain runtime pm callbacks instead.
+ */
+ if (!pci_dev_run_wake(pdev))
+ mei_txe_set_pm_domain(dev);
+
pm_runtime_put_noidle(&pdev->dev);
return 0;
@@ -199,6 +214,9 @@ static void mei_txe_remove(struct pci_dev *pdev)
mei_stop(dev);
+ if (!pci_dev_run_wake(pdev))
+ mei_txe_unset_pm_domain(dev);
+
/* disable interrupts */
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
@@ -350,6 +368,37 @@ static int mei_txe_pm_runtime_resume(struct device *device)
return ret;
}
+
+/**
+ * mei_txe_set_pm_domain - fill and set pm domian stucture for device
+ *
+ * @dev: mei_device
+ */
+static inline void mei_txe_set_pm_domain(struct mei_device *dev)
+{
+ struct pci_dev *pdev = dev->pdev;
+
+ if (pdev->dev.bus && pdev->dev.bus->pm) {
+ dev->pg_domain.ops = *pdev->dev.bus->pm;
+
+ dev->pg_domain.ops.runtime_suspend = mei_txe_pm_runtime_suspend;
+ dev->pg_domain.ops.runtime_resume = mei_txe_pm_runtime_resume;
+ dev->pg_domain.ops.runtime_idle = mei_txe_pm_runtime_idle;
+
+ pdev->dev.pm_domain = &dev->pg_domain;
+ }
+}
+
+/**
+ * mei_txe_unset_pm_domain - clean pm domian stucture for device
+ *
+ * @dev: mei_device
+ */
+static inline void mei_txe_unset_pm_domain(struct mei_device *dev)
+{
+ /* stop using pm callbacks if any */
+ dev->pdev->dev.pm_domain = NULL;
+}
#endif /* CONFIG_PM_RUNTIME */
#ifdef CONFIG_PM
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread* [char-misc-next 13/13] mei: me: bump hbm version to 1.1 to support power gating
2014-03-18 20:51 [char-misc-next 00/13] mei: add runtime pm Tomas Winkler
` (11 preceding siblings ...)
2014-03-18 20:52 ` [char-misc-next 12/13] mei: txe: " Tomas Winkler
@ 2014-03-18 20:52 ` Tomas Winkler
12 siblings, 0 replies; 14+ messages in thread
From: Tomas Winkler @ 2014-03-18 20:52 UTC (permalink / raw)
To: gregkh; +Cc: arnd, rjw, linux-kernel, linux-pm, Tomas Winkler
Communicate hbm version 1.1 to firmware to tell that we
now support power gating isolation protocol
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
drivers/misc/mei/hw.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 9bad9cf..dd448e5 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -37,7 +37,7 @@
/*
* MEI Version
*/
-#define HBM_MINOR_VERSION 0
+#define HBM_MINOR_VERSION 1
#define HBM_MAJOR_VERSION 1
/*
--
1.8.5.3
^ permalink raw reply [flat|nested] 14+ messages in thread