aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei
diff options
context:
space:
mode:
authorAlexander Usyskin <alexander.usyskin@intel.com>2015-06-13 01:51:17 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-07-21 13:10:03 -0400
commit5acb6674291b15ea45d5bd65baff96a896f11ec6 (patch)
treeb1f209e4ee1c0374d0ef0b02dd19ec97a8881521 /drivers/misc/mei
parentf6795f11a4dfb7fbbd4b34668271a553141c0aa7 (diff)
mei: me: wait for power gating exit confirmation
commit 3dc196eae1db548f05e53e5875ff87b8ff79f249 upstream. Fix the hbm power gating state machine so it will wait till it receives confirmation interrupt for the PG_ISOLATION_EXIT message. In process of the suspend flow the devices first have to exit from the power gating state (runtime pm resume). If we do not handle the confirmation interrupt after sending PG_ISOLATION_EXIT message, we may receive it already after the suspend flow has changed the device state and interrupt will be interpreted as a spurious event, consequently link reset will be invoked which will prevent the device from completing the suspend flow kernel: [6603] mei_reset:136: mei_me 0000:00:16.0: powering down: end of reset kernel: [476] mei_me_irq_thread_handler:643: mei_me 0000:00:16.0: function called after ISR to handle the interrupt processing. kernel: mei_me 0000:00:16.0: FW not ready: resetting Cc: Gabriele Mazzotta <gabriele.mzt@gmail.com> Link: https://bugzilla.kernel.org/show_bug.cgi?id=86241 Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=770397 Tested-by: Gabriele Mazzotta <gabriele.mzt@gmail.com> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mei')
-rw-r--r--drivers/misc/mei/client.c2
-rw-r--r--drivers/misc/mei/hw-me.c59
-rw-r--r--drivers/misc/mei/hw-txe.c13
-rw-r--r--drivers/misc/mei/mei_dev.h11
4 files changed, 80 insertions, 5 deletions
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 1e99ef6a54a2..b2b9f4382d77 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -699,7 +699,7 @@ void mei_host_client_init(struct work_struct *work)
699bool mei_hbuf_acquire(struct mei_device *dev) 699bool mei_hbuf_acquire(struct mei_device *dev)
700{ 700{
701 if (mei_pg_state(dev) == MEI_PG_ON || 701 if (mei_pg_state(dev) == MEI_PG_ON ||
702 dev->pg_event == MEI_PG_EVENT_WAIT) { 702 mei_pg_in_transition(dev)) {
703 dev_dbg(dev->dev, "device is in pg\n"); 703 dev_dbg(dev->dev, "device is in pg\n");
704 return false; 704 return false;
705 } 705 }
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 6fb75e62a764..43d7101ff993 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -663,11 +663,27 @@ int mei_me_pg_exit_sync(struct mei_device *dev)
663 mutex_lock(&dev->device_lock); 663 mutex_lock(&dev->device_lock);
664 664
665reply: 665reply:
666 if (dev->pg_event == MEI_PG_EVENT_RECEIVED) 666 if (dev->pg_event != MEI_PG_EVENT_RECEIVED) {
667 ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD); 667 ret = -ETIME;
668 goto out;
669 }
670
671 dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
672 ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
673 if (ret)
674 return ret;
675
676 mutex_unlock(&dev->device_lock);
677 wait_event_timeout(dev->wait_pg,
678 dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, timeout);
679 mutex_lock(&dev->device_lock);
680
681 if (dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED)
682 ret = 0;
668 else 683 else
669 ret = -ETIME; 684 ret = -ETIME;
670 685
686out:
671 dev->pg_event = MEI_PG_EVENT_IDLE; 687 dev->pg_event = MEI_PG_EVENT_IDLE;
672 hw->pg_state = MEI_PG_OFF; 688 hw->pg_state = MEI_PG_OFF;
673 689
@@ -675,6 +691,19 @@ reply:
675} 691}
676 692
677/** 693/**
694 * mei_me_pg_in_transition - is device now in pg transition
695 *
696 * @dev: the device structure
697 *
698 * Return: true if in pg transition, false otherwise
699 */
700static bool mei_me_pg_in_transition(struct mei_device *dev)
701{
702 return dev->pg_event >= MEI_PG_EVENT_WAIT &&
703 dev->pg_event <= MEI_PG_EVENT_INTR_WAIT;
704}
705
706/**
678 * mei_me_pg_is_enabled - detect if PG is supported by HW 707 * mei_me_pg_is_enabled - detect if PG is supported by HW
679 * 708 *
680 * @dev: the device structure 709 * @dev: the device structure
@@ -705,6 +734,24 @@ notsupported:
705} 734}
706 735
707/** 736/**
737 * mei_me_pg_intr - perform pg processing in interrupt thread handler
738 *
739 * @dev: the device structure
740 */
741static void mei_me_pg_intr(struct mei_device *dev)
742{
743 struct mei_me_hw *hw = to_me_hw(dev);
744
745 if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT)
746 return;
747
748 dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED;
749 hw->pg_state = MEI_PG_OFF;
750 if (waitqueue_active(&dev->wait_pg))
751 wake_up(&dev->wait_pg);
752}
753
754/**
708 * mei_me_irq_quick_handler - The ISR of the MEI device 755 * mei_me_irq_quick_handler - The ISR of the MEI device
709 * 756 *
710 * @irq: The irq number 757 * @irq: The irq number
@@ -761,6 +808,8 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
761 goto end; 808 goto end;
762 } 809 }
763 810
811 mei_me_pg_intr(dev);
812
764 /* check if we need to start the dev */ 813 /* check if we need to start the dev */
765 if (!mei_host_is_ready(dev)) { 814 if (!mei_host_is_ready(dev)) {
766 if (mei_hw_is_ready(dev)) { 815 if (mei_hw_is_ready(dev)) {
@@ -797,9 +846,10 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
797 /* 846 /*
798 * During PG handshake only allowed write is the replay to the 847 * During PG handshake only allowed write is the replay to the
799 * PG exit message, so block calling write function 848 * PG exit message, so block calling write function
800 * if the pg state is not idle 849 * if the pg event is in PG handshake
801 */ 850 */
802 if (dev->pg_event == MEI_PG_EVENT_IDLE) { 851 if (dev->pg_event != MEI_PG_EVENT_WAIT &&
852 dev->pg_event != MEI_PG_EVENT_RECEIVED) {
803 rets = mei_irq_write_handler(dev, &complete_list); 853 rets = mei_irq_write_handler(dev, &complete_list);
804 dev->hbuf_is_ready = mei_hbuf_is_ready(dev); 854 dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
805 } 855 }
@@ -824,6 +874,7 @@ static const struct mei_hw_ops mei_me_hw_ops = {
824 .hw_config = mei_me_hw_config, 874 .hw_config = mei_me_hw_config,
825 .hw_start = mei_me_hw_start, 875 .hw_start = mei_me_hw_start,
826 876
877 .pg_in_transition = mei_me_pg_in_transition,
827 .pg_is_enabled = mei_me_pg_is_enabled, 878 .pg_is_enabled = mei_me_pg_is_enabled,
828 879
829 .intr_clear = mei_me_intr_clear, 880 .intr_clear = mei_me_intr_clear,
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index 7abafe7d120d..964136b35733 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -302,6 +302,18 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
302} 302}
303 303
304/** 304/**
305 * mei_txe_pg_in_transition - is device now in pg transition
306 *
307 * @dev: the device structure
308 *
309 * Return: true if in pg transition, false otherwise
310 */
311static bool mei_txe_pg_in_transition(struct mei_device *dev)
312{
313 return dev->pg_event == MEI_PG_EVENT_WAIT;
314}
315
316/**
305 * mei_txe_pg_is_enabled - detect if PG is supported by HW 317 * mei_txe_pg_is_enabled - detect if PG is supported by HW
306 * 318 *
307 * @dev: the device structure 319 * @dev: the device structure
@@ -1138,6 +1150,7 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
1138 .hw_config = mei_txe_hw_config, 1150 .hw_config = mei_txe_hw_config,
1139 .hw_start = mei_txe_hw_start, 1151 .hw_start = mei_txe_hw_start,
1140 1152
1153 .pg_in_transition = mei_txe_pg_in_transition,
1141 .pg_is_enabled = mei_txe_pg_is_enabled, 1154 .pg_is_enabled = mei_txe_pg_is_enabled,
1142 1155
1143 .intr_clear = mei_txe_intr_clear, 1156 .intr_clear = mei_txe_intr_clear,
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index f066ecd71939..f84c39ee28a8 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -271,6 +271,7 @@ struct mei_cl {
271 271
272 * @fw_status : get fw status registers 272 * @fw_status : get fw status registers
273 * @pg_state : power gating state of the device 273 * @pg_state : power gating state of the device
274 * @pg_in_transition : is device now in pg transition
274 * @pg_is_enabled : is power gating enabled 275 * @pg_is_enabled : is power gating enabled
275 276
276 * @intr_clear : clear pending interrupts 277 * @intr_clear : clear pending interrupts
@@ -300,6 +301,7 @@ struct mei_hw_ops {
300 301
301 int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts); 302 int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts);
302 enum mei_pg_state (*pg_state)(struct mei_device *dev); 303 enum mei_pg_state (*pg_state)(struct mei_device *dev);
304 bool (*pg_in_transition)(struct mei_device *dev);
303 bool (*pg_is_enabled)(struct mei_device *dev); 305 bool (*pg_is_enabled)(struct mei_device *dev);
304 306
305 void (*intr_clear)(struct mei_device *dev); 307 void (*intr_clear)(struct mei_device *dev);
@@ -398,11 +400,15 @@ struct mei_cl_device {
398 * @MEI_PG_EVENT_IDLE: the driver is not in power gating transition 400 * @MEI_PG_EVENT_IDLE: the driver is not in power gating transition
399 * @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete 401 * @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete
400 * @MEI_PG_EVENT_RECEIVED: the driver received pg event 402 * @MEI_PG_EVENT_RECEIVED: the driver received pg event
403 * @MEI_PG_EVENT_INTR_WAIT: the driver is waiting for a pg event interrupt
404 * @MEI_PG_EVENT_INTR_RECEIVED: the driver received pg event interrupt
401 */ 405 */
402enum mei_pg_event { 406enum mei_pg_event {
403 MEI_PG_EVENT_IDLE, 407 MEI_PG_EVENT_IDLE,
404 MEI_PG_EVENT_WAIT, 408 MEI_PG_EVENT_WAIT,
405 MEI_PG_EVENT_RECEIVED, 409 MEI_PG_EVENT_RECEIVED,
410 MEI_PG_EVENT_INTR_WAIT,
411 MEI_PG_EVENT_INTR_RECEIVED,
406}; 412};
407 413
408/** 414/**
@@ -717,6 +723,11 @@ static inline enum mei_pg_state mei_pg_state(struct mei_device *dev)
717 return dev->ops->pg_state(dev); 723 return dev->ops->pg_state(dev);
718} 724}
719 725
726static inline bool mei_pg_in_transition(struct mei_device *dev)
727{
728 return dev->ops->pg_in_transition(dev);
729}
730
720static inline bool mei_pg_is_enabled(struct mei_device *dev) 731static inline bool mei_pg_is_enabled(struct mei_device *dev)
721{ 732{
722 return dev->ops->pg_is_enabled(dev); 733 return dev->ops->pg_is_enabled(dev);