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

mei: me: d0i3: add d0i3 enter/exit state machine

Rework mei power gating state machine to support entry and exit to and
from D0i3 power state.
The choice between legacy and D0i3 routines is conditioned on
d0i3_supported flag.

The patch introduces warning:
drivers/misc/mei/hw-me.c:901:12: warning: ‘mei_me_d0i3_enter’ defined but not used [-Wunused-function]
it will go away in consequent patch

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
859ef2ff 63f75232

+350 -14
+12 -1
drivers/misc/mei/hbm.c
··· 902 902 } 903 903 904 904 /** 905 + * mei_hbm_pg_resume - process with PG resume 906 + * 907 + * @dev: the device structure. 908 + */ 909 + void mei_hbm_pg_resume(struct mei_device *dev) 910 + { 911 + pm_request_resume(dev->dev); 912 + } 913 + EXPORT_SYMBOL_GPL(mei_hbm_pg_resume); 914 + 915 + /** 905 916 * mei_hbm_pg_exit_res - PG exit response received 906 917 * 907 918 * @dev: the device structure. ··· 941 930 * Start runtime pm resume sequence to exit from PG. 942 931 */ 943 932 dev->pg_event = MEI_PG_EVENT_RECEIVED; 944 - pm_request_resume(dev->dev); 933 + mei_hbm_pg_resume(dev); 945 934 break; 946 935 default: 947 936 WARN(1, "hbm: pg exit response: unexpected pg event = %d\n",
+1
drivers/misc/mei/hbm.h
··· 54 54 int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl); 55 55 bool mei_hbm_version_is_supported(struct mei_device *dev); 56 56 int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd); 57 + void mei_hbm_pg_resume(struct mei_device *dev); 57 58 int mei_hbm_cl_notify_req(struct mei_device *dev, 58 59 struct mei_cl *cl, u8 request); 59 60
+334 -11
drivers/misc/mei/hw-me.c
··· 139 139 } 140 140 141 141 /** 142 + * mei_me_d0i3c_read - Reads 32bit data from the D0I3C register 143 + * 144 + * @dev: the device structure 145 + * 146 + * Return: H_D0I3C register value (u32) 147 + */ 148 + static inline u32 mei_me_d0i3c_read(const struct mei_device *dev) 149 + { 150 + u32 reg; 151 + 152 + reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C); 153 + trace_mei_reg_read(dev->dev, "H_D0I3C", H_CSR, reg); 154 + 155 + return reg; 156 + } 157 + 158 + /** 159 + * mei_me_d0i3c_write - writes H_D0I3C register to device 160 + * 161 + * @dev: the device structure 162 + * @reg: new register value 163 + */ 164 + static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg) 165 + { 166 + trace_mei_reg_write(dev->dev, "H_D0I3C", H_CSR, reg); 167 + mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg); 168 + } 169 + 170 + /** 142 171 * mei_me_fw_status - read fw status register from pci config space 143 172 * 144 173 * @dev: mei device ··· 638 609 } 639 610 640 611 /** 641 - * mei_me_pg_enter_sync - perform pg entry procedure 612 + * mei_me_pg_legacy_enter_sync - perform legacy pg entry procedure 642 613 * 643 614 * @dev: the device structure 644 615 * 645 616 * Return: 0 on success an error code otherwise 646 617 */ 647 - int mei_me_pg_enter_sync(struct mei_device *dev) 618 + static int mei_me_pg_legacy_enter_sync(struct mei_device *dev) 648 619 { 649 620 struct mei_me_hw *hw = to_me_hw(dev); 650 621 unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); ··· 675 646 } 676 647 677 648 /** 678 - * mei_me_pg_exit_sync - perform pg exit procedure 649 + * mei_me_pg_legacy_exit_sync - perform legacy pg exit procedure 679 650 * 680 651 * @dev: the device structure 681 652 * 682 653 * Return: 0 on success an error code otherwise 683 654 */ 684 - int mei_me_pg_exit_sync(struct mei_device *dev) 655 + static int mei_me_pg_legacy_exit_sync(struct mei_device *dev) 685 656 { 686 657 struct mei_me_hw *hw = to_me_hw(dev); 687 658 unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); ··· 749 720 */ 750 721 static bool mei_me_pg_is_enabled(struct mei_device *dev) 751 722 { 723 + struct mei_me_hw *hw = to_me_hw(dev); 752 724 u32 reg = mei_me_mecsr_read(dev); 725 + 726 + if (hw->d0i3_supported) 727 + return true; 753 728 754 729 if ((reg & ME_PGIC_HRA) == 0) 755 730 goto notsupported; ··· 764 731 return true; 765 732 766 733 notsupported: 767 - dev_dbg(dev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n", 734 + dev_dbg(dev->dev, "pg: not supported: d0i3 = %d HGP = %d hbm version %d.%d ?= %d.%d\n", 735 + hw->d0i3_supported, 768 736 !!(reg & ME_PGIC_HRA), 769 737 dev->version.major_version, 770 738 dev->version.minor_version, ··· 773 739 HBM_MINOR_VERSION_PGI); 774 740 775 741 return false; 742 + } 743 + 744 + /** 745 + * mei_me_d0i3_set - write d0i3 register bit on mei device. 746 + * 747 + * @dev: the device structure 748 + * @intr: ask for interrupt 749 + * 750 + * Return: D0I3C register value 751 + */ 752 + static u32 mei_me_d0i3_set(struct mei_device *dev, bool intr) 753 + { 754 + u32 reg = mei_me_d0i3c_read(dev); 755 + 756 + reg |= H_D0I3C_I3; 757 + if (intr) 758 + reg |= H_D0I3C_IR; 759 + else 760 + reg &= ~H_D0I3C_IR; 761 + mei_me_d0i3c_write(dev, reg); 762 + /* read it to ensure HW consistency */ 763 + reg = mei_me_d0i3c_read(dev); 764 + return reg; 765 + } 766 + 767 + /** 768 + * mei_me_d0i3_unset - clean d0i3 register bit on mei device. 769 + * 770 + * @dev: the device structure 771 + * 772 + * Return: D0I3C register value 773 + */ 774 + static u32 mei_me_d0i3_unset(struct mei_device *dev) 775 + { 776 + u32 reg = mei_me_d0i3c_read(dev); 777 + 778 + reg &= ~H_D0I3C_I3; 779 + reg |= H_D0I3C_IR; 780 + mei_me_d0i3c_write(dev, reg); 781 + /* read it to ensure HW consistency */ 782 + reg = mei_me_d0i3c_read(dev); 783 + return reg; 784 + } 785 + 786 + /** 787 + * mei_me_d0i3_enter_sync - perform d0i3 entry procedure 788 + * 789 + * @dev: the device structure 790 + * 791 + * Return: 0 on success an error code otherwise 792 + */ 793 + static int mei_me_d0i3_enter_sync(struct mei_device *dev) 794 + { 795 + struct mei_me_hw *hw = to_me_hw(dev); 796 + unsigned long d0i3_timeout = mei_secs_to_jiffies(MEI_D0I3_TIMEOUT); 797 + unsigned long pgi_timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); 798 + int ret; 799 + u32 reg; 800 + 801 + reg = mei_me_d0i3c_read(dev); 802 + if (reg & H_D0I3C_I3) { 803 + /* we are in d0i3, nothing to do */ 804 + dev_dbg(dev->dev, "d0i3 set not needed\n"); 805 + ret = 0; 806 + goto on; 807 + } 808 + 809 + /* PGI entry procedure */ 810 + dev->pg_event = MEI_PG_EVENT_WAIT; 811 + 812 + ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD); 813 + if (ret) 814 + /* FIXME: should we reset here? */ 815 + goto out; 816 + 817 + mutex_unlock(&dev->device_lock); 818 + wait_event_timeout(dev->wait_pg, 819 + dev->pg_event == MEI_PG_EVENT_RECEIVED, pgi_timeout); 820 + mutex_lock(&dev->device_lock); 821 + 822 + if (dev->pg_event != MEI_PG_EVENT_RECEIVED) { 823 + ret = -ETIME; 824 + goto out; 825 + } 826 + /* end PGI entry procedure */ 827 + 828 + dev->pg_event = MEI_PG_EVENT_INTR_WAIT; 829 + 830 + reg = mei_me_d0i3_set(dev, true); 831 + if (!(reg & H_D0I3C_CIP)) { 832 + dev_dbg(dev->dev, "d0i3 enter wait not needed\n"); 833 + ret = 0; 834 + goto on; 835 + } 836 + 837 + mutex_unlock(&dev->device_lock); 838 + wait_event_timeout(dev->wait_pg, 839 + dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, d0i3_timeout); 840 + mutex_lock(&dev->device_lock); 841 + 842 + if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) { 843 + reg = mei_me_d0i3c_read(dev); 844 + if (!(reg & H_D0I3C_I3)) { 845 + ret = -ETIME; 846 + goto out; 847 + } 848 + } 849 + 850 + ret = 0; 851 + on: 852 + hw->pg_state = MEI_PG_ON; 853 + out: 854 + dev->pg_event = MEI_PG_EVENT_IDLE; 855 + dev_dbg(dev->dev, "d0i3 enter ret = %d\n", ret); 856 + return ret; 857 + } 858 + 859 + /** 860 + * mei_me_d0i3_enter - perform d0i3 entry procedure 861 + * no hbm PG handshake 862 + * no waiting for confirmation; runs with interrupts 863 + * disabled 864 + * 865 + * @dev: the device structure 866 + * 867 + * Return: 0 on success an error code otherwise 868 + */ 869 + static int mei_me_d0i3_enter(struct mei_device *dev) 870 + { 871 + struct mei_me_hw *hw = to_me_hw(dev); 872 + u32 reg; 873 + 874 + reg = mei_me_d0i3c_read(dev); 875 + if (reg & H_D0I3C_I3) { 876 + /* we are in d0i3, nothing to do */ 877 + dev_dbg(dev->dev, "already d0i3 : set not needed\n"); 878 + goto on; 879 + } 880 + 881 + mei_me_d0i3_set(dev, false); 882 + on: 883 + hw->pg_state = MEI_PG_ON; 884 + dev->pg_event = MEI_PG_EVENT_IDLE; 885 + dev_dbg(dev->dev, "d0i3 enter\n"); 886 + return 0; 887 + } 888 + 889 + /** 890 + * mei_me_d0i3_exit_sync - perform d0i3 exit procedure 891 + * 892 + * @dev: the device structure 893 + * 894 + * Return: 0 on success an error code otherwise 895 + */ 896 + static int mei_me_d0i3_exit_sync(struct mei_device *dev) 897 + { 898 + struct mei_me_hw *hw = to_me_hw(dev); 899 + unsigned long timeout = mei_secs_to_jiffies(MEI_D0I3_TIMEOUT); 900 + int ret; 901 + u32 reg; 902 + 903 + dev->pg_event = MEI_PG_EVENT_INTR_WAIT; 904 + 905 + reg = mei_me_d0i3c_read(dev); 906 + if (!(reg & H_D0I3C_I3)) { 907 + /* we are not in d0i3, nothing to do */ 908 + dev_dbg(dev->dev, "d0i3 exit not needed\n"); 909 + ret = 0; 910 + goto off; 911 + } 912 + 913 + reg = mei_me_d0i3_unset(dev); 914 + if (!(reg & H_D0I3C_CIP)) { 915 + dev_dbg(dev->dev, "d0i3 exit wait not needed\n"); 916 + ret = 0; 917 + goto off; 918 + } 919 + 920 + mutex_unlock(&dev->device_lock); 921 + wait_event_timeout(dev->wait_pg, 922 + dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, timeout); 923 + mutex_lock(&dev->device_lock); 924 + 925 + if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) { 926 + reg = mei_me_d0i3c_read(dev); 927 + if (reg & H_D0I3C_I3) { 928 + ret = -ETIME; 929 + goto out; 930 + } 931 + } 932 + 933 + ret = 0; 934 + off: 935 + hw->pg_state = MEI_PG_OFF; 936 + out: 937 + dev->pg_event = MEI_PG_EVENT_IDLE; 938 + 939 + dev_dbg(dev->dev, "d0i3 exit ret = %d\n", ret); 940 + return ret; 941 + } 942 + 943 + /** 944 + * mei_me_pg_legacy_intr - perform legacy pg processing 945 + * in interrupt thread handler 946 + * 947 + * @dev: the device structure 948 + */ 949 + static void mei_me_pg_legacy_intr(struct mei_device *dev) 950 + { 951 + struct mei_me_hw *hw = to_me_hw(dev); 952 + 953 + if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT) 954 + return; 955 + 956 + dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; 957 + hw->pg_state = MEI_PG_OFF; 958 + if (waitqueue_active(&dev->wait_pg)) 959 + wake_up(&dev->wait_pg); 960 + } 961 + 962 + /** 963 + * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler 964 + * 965 + * @dev: the device structure 966 + */ 967 + static void mei_me_d0i3_intr(struct mei_device *dev) 968 + { 969 + struct mei_me_hw *hw = to_me_hw(dev); 970 + 971 + if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && 972 + (hw->intr_source & H_D0I3C_IS)) { 973 + dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; 974 + if (hw->pg_state == MEI_PG_ON) { 975 + hw->pg_state = MEI_PG_OFF; 976 + if (dev->hbm_state != MEI_HBM_IDLE) { 977 + /* 978 + * force H_RDY because it could be 979 + * wiped off during PG 980 + */ 981 + dev_dbg(dev->dev, "d0i3 set host ready\n"); 982 + mei_me_host_set_ready(dev); 983 + } 984 + } else { 985 + hw->pg_state = MEI_PG_ON; 986 + } 987 + 988 + wake_up(&dev->wait_pg); 989 + } 990 + 991 + if (hw->pg_state == MEI_PG_ON && (hw->intr_source & H_IS)) { 992 + /* 993 + * HW sent some data and we are in D0i3, so 994 + * we got here because of HW initiated exit from D0i3. 995 + * Start runtime pm resume sequence to exit low power state. 996 + */ 997 + dev_dbg(dev->dev, "d0i3 want resume\n"); 998 + mei_hbm_pg_resume(dev); 999 + } 776 1000 } 777 1001 778 1002 /** ··· 1042 750 { 1043 751 struct mei_me_hw *hw = to_me_hw(dev); 1044 752 1045 - if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT) 1046 - return; 753 + if (hw->d0i3_supported) 754 + mei_me_d0i3_intr(dev); 755 + else 756 + mei_me_pg_legacy_intr(dev); 757 + } 1047 758 1048 - dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; 1049 - hw->pg_state = MEI_PG_OFF; 1050 - if (waitqueue_active(&dev->wait_pg)) 1051 - wake_up(&dev->wait_pg); 759 + /** 760 + * mei_me_pg_enter_sync - perform runtime pm entry procedure 761 + * 762 + * @dev: the device structure 763 + * 764 + * Return: 0 on success an error code otherwise 765 + */ 766 + int mei_me_pg_enter_sync(struct mei_device *dev) 767 + { 768 + struct mei_me_hw *hw = to_me_hw(dev); 769 + 770 + if (hw->d0i3_supported) 771 + return mei_me_d0i3_enter_sync(dev); 772 + else 773 + return mei_me_pg_legacy_enter_sync(dev); 774 + } 775 + 776 + /** 777 + * mei_me_pg_exit_sync - perform runtime pm exit procedure 778 + * 779 + * @dev: the device structure 780 + * 781 + * Return: 0 on success an error code otherwise 782 + */ 783 + int mei_me_pg_exit_sync(struct mei_device *dev) 784 + { 785 + struct mei_me_hw *hw = to_me_hw(dev); 786 + 787 + if (hw->d0i3_supported) 788 + return mei_me_d0i3_exit_sync(dev); 789 + else 790 + return mei_me_pg_legacy_exit_sync(dev); 1052 791 } 1053 792 1054 793 /**
+3 -2
drivers/misc/mei/hw.h
··· 31 31 #define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */ 32 32 #define MEI_IAMTHIF_READ_TIMER 10 /* HPS */ 33 33 34 - #define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */ 35 - #define MEI_HBM_TIMEOUT 1 /* 1 second */ 34 + #define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */ 35 + #define MEI_D0I3_TIMEOUT 5 /* D0i3 set/unset max response time */ 36 + #define MEI_HBM_TIMEOUT 1 /* 1 second */ 36 37 37 38 /* 38 39 * MEI Version