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 mutex_lock(&mhi_cntrl->state_lock); 67 68 old_state = mhi_cntrl->mhi_state; 69 if (old_state == MHI_STATE_M3) 70 mhi_ep_resume_channels(mhi_cntrl); 71 72 ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0); 73 if (ret) { 74 mhi_ep_handle_syserr(mhi_cntrl); 75 goto err_unlock; 76 } 77 78 /* Signal host that the device moved to M0 */ 79 ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M0); 80 if (ret) { 81 dev_err(dev, "Failed sending M0 state change event\n"); 82 goto err_unlock; 83 } 84 85 if (old_state == MHI_STATE_READY) { 86 /* Send AMSS EE event to host */ 87 ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EE_AMSS); 88 if (ret) { 89 dev_err(dev, "Failed sending AMSS EE event\n"); 90 goto err_unlock; 91 } 92 } 93 94 err_unlock: 95 mutex_unlock(&mhi_cntrl->state_lock); 96 97 return ret; 98 } 99 100 int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl) 101 { 102 struct device *dev = &mhi_cntrl->mhi_dev->dev; 103 int ret; 104 105 mutex_lock(&mhi_cntrl->state_lock); 106 107 ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3); 108 if (ret) { 109 mhi_ep_handle_syserr(mhi_cntrl); 110 goto err_unlock; 111 } 112 113 mhi_ep_suspend_channels(mhi_cntrl); 114 115 /* Signal host that the device moved to M3 */ 116 ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M3); 117 if (ret) { 118 dev_err(dev, "Failed sending M3 state change event\n"); 119 goto err_unlock; 120 } 121 122 err_unlock: 123 mutex_unlock(&mhi_cntrl->state_lock); 124 125 return ret; 126 } 127 128 int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl) 129 { 130 struct device *dev = &mhi_cntrl->mhi_dev->dev; 131 enum mhi_state mhi_state; 132 int ret, is_ready; 133 134 mutex_lock(&mhi_cntrl->state_lock); 135 136 /* Ensure that the MHISTATUS is set to RESET by host */ 137 mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK); 138 is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK); 139 140 if (mhi_state != MHI_STATE_RESET || is_ready) { 141 dev_err(dev, "READY state transition failed. MHI host not in RESET state\n"); 142 ret = -EIO; 143 goto err_unlock; 144 } 145 146 ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY); 147 if (ret) 148 mhi_ep_handle_syserr(mhi_cntrl); 149 150 err_unlock: 151 mutex_unlock(&mhi_cntrl->state_lock); 152 153 return ret; 154 } 155