Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mei: me: wait for power gating exit confirmation

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: <stable@vger.kernel.org> #3.18+
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>

authored by

Alexander Usyskin and committed by
Greg Kroah-Hartman
3dc196ea c241e9b1

+80 -5
+1 -1
drivers/misc/mei/client.c
··· 714 714 bool mei_hbuf_acquire(struct mei_device *dev) 715 715 { 716 716 if (mei_pg_state(dev) == MEI_PG_ON || 717 - dev->pg_event == MEI_PG_EVENT_WAIT) { 717 + mei_pg_in_transition(dev)) { 718 718 dev_dbg(dev->dev, "device is in pg\n"); 719 719 return false; 720 720 }
+55 -4
drivers/misc/mei/hw-me.c
··· 663 663 mutex_lock(&dev->device_lock); 664 664 665 665 reply: 666 - if (dev->pg_event == MEI_PG_EVENT_RECEIVED) 667 - ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD); 666 + if (dev->pg_event != MEI_PG_EVENT_RECEIVED) { 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 683 else 669 684 ret = -ETIME; 670 685 686 + out: 671 687 dev->pg_event = MEI_PG_EVENT_IDLE; 672 688 hw->pg_state = MEI_PG_OFF; 673 689 674 690 return ret; 691 + } 692 + 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 + */ 700 + static 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; 675 704 } 676 705 677 706 /** ··· 731 702 HBM_MINOR_VERSION_PGI); 732 703 733 704 return false; 705 + } 706 + 707 + /** 708 + * mei_me_pg_intr - perform pg processing in interrupt thread handler 709 + * 710 + * @dev: the device structure 711 + */ 712 + static void mei_me_pg_intr(struct mei_device *dev) 713 + { 714 + struct mei_me_hw *hw = to_me_hw(dev); 715 + 716 + if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT) 717 + return; 718 + 719 + dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; 720 + hw->pg_state = MEI_PG_OFF; 721 + if (waitqueue_active(&dev->wait_pg)) 722 + wake_up(&dev->wait_pg); 734 723 } 735 724 736 725 /** ··· 808 761 goto end; 809 762 } 810 763 764 + mei_me_pg_intr(dev); 765 + 811 766 /* check if we need to start the dev */ 812 767 if (!mei_host_is_ready(dev)) { 813 768 if (mei_hw_is_ready(dev)) { ··· 846 797 /* 847 798 * During PG handshake only allowed write is the replay to the 848 799 * PG exit message, so block calling write function 849 - * if the pg state is not idle 800 + * if the pg event is in PG handshake 850 801 */ 851 - if (dev->pg_event == MEI_PG_EVENT_IDLE) { 802 + if (dev->pg_event != MEI_PG_EVENT_WAIT && 803 + dev->pg_event != MEI_PG_EVENT_RECEIVED) { 852 804 rets = mei_irq_write_handler(dev, &complete_list); 853 805 dev->hbuf_is_ready = mei_hbuf_is_ready(dev); 854 806 } ··· 874 824 .hw_config = mei_me_hw_config, 875 825 .hw_start = mei_me_hw_start, 876 826 827 + .pg_in_transition = mei_me_pg_in_transition, 877 828 .pg_is_enabled = mei_me_pg_is_enabled, 878 829 879 830 .intr_clear = mei_me_intr_clear,
+13
drivers/misc/mei/hw-txe.c
··· 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 + */ 311 + static bool mei_txe_pg_in_transition(struct mei_device *dev) 312 + { 313 + return dev->pg_event == MEI_PG_EVENT_WAIT; 314 + } 315 + 316 + /** 305 317 * mei_txe_pg_is_enabled - detect if PG is supported by HW 306 318 * 307 319 * @dev: the device structure ··· 1150 1138 .hw_config = mei_txe_hw_config, 1151 1139 .hw_start = mei_txe_hw_start, 1152 1140 1141 + .pg_in_transition = mei_txe_pg_in_transition, 1153 1142 .pg_is_enabled = mei_txe_pg_is_enabled, 1154 1143 1155 1144 .intr_clear = mei_txe_intr_clear,
+11
drivers/misc/mei/mei_dev.h
··· 276 276 277 277 * @fw_status : get fw status registers 278 278 * @pg_state : power gating state of the device 279 + * @pg_in_transition : is device now in pg transition 279 280 * @pg_is_enabled : is power gating enabled 280 281 281 282 * @intr_clear : clear pending interrupts ··· 306 305 307 306 int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts); 308 307 enum mei_pg_state (*pg_state)(struct mei_device *dev); 308 + bool (*pg_in_transition)(struct mei_device *dev); 309 309 bool (*pg_is_enabled)(struct mei_device *dev); 310 310 311 311 void (*intr_clear)(struct mei_device *dev); ··· 351 349 * @MEI_PG_EVENT_IDLE: the driver is not in power gating transition 352 350 * @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete 353 351 * @MEI_PG_EVENT_RECEIVED: the driver received pg event 352 + * @MEI_PG_EVENT_INTR_WAIT: the driver is waiting for a pg event interrupt 353 + * @MEI_PG_EVENT_INTR_RECEIVED: the driver received pg event interrupt 354 354 */ 355 355 enum mei_pg_event { 356 356 MEI_PG_EVENT_IDLE, 357 357 MEI_PG_EVENT_WAIT, 358 358 MEI_PG_EVENT_RECEIVED, 359 + MEI_PG_EVENT_INTR_WAIT, 360 + MEI_PG_EVENT_INTR_RECEIVED, 359 361 }; 360 362 361 363 /** ··· 674 668 static inline enum mei_pg_state mei_pg_state(struct mei_device *dev) 675 669 { 676 670 return dev->ops->pg_state(dev); 671 + } 672 + 673 + static inline bool mei_pg_in_transition(struct mei_device *dev) 674 + { 675 + return dev->ops->pg_in_transition(dev); 677 676 } 678 677 679 678 static inline bool mei_pg_is_enabled(struct mei_device *dev)