1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2022 Linaro Ltd. 4 * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 5 */ 6 7 #include <linux/errno.h> 8 #include <linux/mhi_ep.h> 9 #include "internal.h" 10 11 bool __must_check mhi_ep_check_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, 12 enum mhi_state cur_mhi_state, 13 enum mhi_state mhi_state) 14 { 15 if (mhi_state == MHI_STATE_SYS_ERR) 16 return true; /* Allowed in any state */ 17 18 if (mhi_state == MHI_STATE_READY) 19 return cur_mhi_state == MHI_STATE_RESET; 20 21 if (mhi_state == MHI_STATE_M0) 22 return cur_mhi_state == MHI_STATE_M3 || cur_mhi_state == MHI_STATE_READY; 23 24 if (mhi_state == MHI_STATE_M3) 25 return cur_mhi_state == MHI_STATE_M0; 26 27 return false; 28 } 29 30 int mhi_ep_set_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state mhi_state) 31 { 32 struct device *dev = &mhi_cntrl->mhi_dev->dev; 33 34 if (!mhi_ep_check_mhi_state(mhi_cntrl, mhi_cntrl->mhi_state, mhi_state)) { 35 dev_err(dev, "MHI state change to %s from %s is not allowed!\n", 36 mhi_state_str(mhi_state), 37 mhi_state_str(mhi_cntrl->mhi_state)); 38 return -EACCES; 39 } 40 41 /* TODO: Add support for M1 and M2 states */ 42 if (mhi_state == MHI_STATE_M1 || mhi_state == MHI_STATE_M2) { 43 dev_err(dev, "MHI state (%s) not supported\n", mhi_state_str(mhi_state)); 44 return -EOPNOTSUPP; 45 } 46 47 mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK, mhi_state); 48 mhi_cntrl->mhi_state = mhi_state; 49 50 if (mhi_state == MHI_STATE_READY) 51 mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK, 1); 52 53 if (mhi_state == MHI_STATE_SYS_ERR) 54 mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_SYSERR_MASK, 1); 55 56 return 0; 57 } 58 59 int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl) 60 { 61 struct device *dev = &mhi_cntrl->mhi_dev->dev; 62 enum mhi_state old_state; 63 int ret; 64 65 /* If MHI is in M3, resume suspended channels */ 66 spin_lock_bh(&mhi_cntrl->state_lock); 67 old_state = mhi_cntrl->mhi_state; 68 if (old_state == MHI_STATE_M3) 69 mhi_ep_resume_channels(mhi_cntrl); 70 71 ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0); 72 spin_unlock_bh(&mhi_cntrl->state_lock); 73 74 if (ret) { 75 mhi_ep_handle_syserr(mhi_cntrl); 76 return ret; 77 } 78 79 /* Signal host that the device moved to M0 */ 80 ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M0); 81 if (ret) { 82 dev_err(dev, "Failed sending M0 state change event\n"); 83 return ret; 84 } 85 86 if (old_state == MHI_STATE_READY) { 87 /* Send AMSS EE event to host */ 88 ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EE_AMSS); 89 if (ret) { 90 dev_err(dev, "Failed sending AMSS EE event\n"); 91 return ret; 92 } 93 } 94 95 return 0; 96 } 97 98 int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl) 99 { 100 struct device *dev = &mhi_cntrl->mhi_dev->dev; 101 int ret; 102 103 spin_lock_bh(&mhi_cntrl->state_lock); 104 ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3); 105 spin_unlock_bh(&mhi_cntrl->state_lock); 106 107 if (ret) { 108 mhi_ep_handle_syserr(mhi_cntrl); 109 return ret; 110 } 111 112 mhi_ep_suspend_channels(mhi_cntrl); 113 114 /* Signal host that the device moved to M3 */ 115 ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M3); 116 if (ret) { 117 dev_err(dev, "Failed sending M3 state change event\n"); 118 return ret; 119 } 120 121 return 0; 122 } 123 124 int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl) 125 { 126 struct device *dev = &mhi_cntrl->mhi_dev->dev; 127 enum mhi_state mhi_state; 128 int ret, is_ready; 129 130 spin_lock_bh(&mhi_cntrl->state_lock); 131 /* Ensure that the MHISTATUS is set to RESET by host */ 132 mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK); 133 is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK); 134 135 if (mhi_state != MHI_STATE_RESET || is_ready) { 136 dev_err(dev, "READY state transition failed. MHI host not in RESET state\n"); 137 spin_unlock_bh(&mhi_cntrl->state_lock); 138 return -EIO; 139 } 140 141 ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY); 142 spin_unlock_bh(&mhi_cntrl->state_lock); 143 144 if (ret) 145 mhi_ep_handle_syserr(mhi_cntrl); 146 147 return ret; 148 } 149