123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2022 Linaro Ltd.
- * Author: Manivannan Sadhasivam <[email protected]>
- */
- #include <linux/errno.h>
- #include <linux/mhi_ep.h>
- #include "internal.h"
- bool __must_check mhi_ep_check_mhi_state(struct mhi_ep_cntrl *mhi_cntrl,
- enum mhi_state cur_mhi_state,
- enum mhi_state mhi_state)
- {
- if (mhi_state == MHI_STATE_SYS_ERR)
- return true; /* Allowed in any state */
- if (mhi_state == MHI_STATE_READY)
- return cur_mhi_state == MHI_STATE_RESET;
- if (mhi_state == MHI_STATE_M0)
- return cur_mhi_state == MHI_STATE_M3 || cur_mhi_state == MHI_STATE_READY;
- if (mhi_state == MHI_STATE_M3)
- return cur_mhi_state == MHI_STATE_M0;
- return false;
- }
- int mhi_ep_set_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state mhi_state)
- {
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
- if (!mhi_ep_check_mhi_state(mhi_cntrl, mhi_cntrl->mhi_state, mhi_state)) {
- dev_err(dev, "MHI state change to %s from %s is not allowed!\n",
- mhi_state_str(mhi_state),
- mhi_state_str(mhi_cntrl->mhi_state));
- return -EACCES;
- }
- /* TODO: Add support for M1 and M2 states */
- if (mhi_state == MHI_STATE_M1 || mhi_state == MHI_STATE_M2) {
- dev_err(dev, "MHI state (%s) not supported\n", mhi_state_str(mhi_state));
- return -EOPNOTSUPP;
- }
- mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK, mhi_state);
- mhi_cntrl->mhi_state = mhi_state;
- if (mhi_state == MHI_STATE_READY)
- mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK, 1);
- if (mhi_state == MHI_STATE_SYS_ERR)
- mhi_ep_mmio_masked_write(mhi_cntrl, EP_MHISTATUS, MHISTATUS_SYSERR_MASK, 1);
- return 0;
- }
- int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl)
- {
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
- enum mhi_state old_state;
- int ret;
- /* If MHI is in M3, resume suspended channels */
- mutex_lock(&mhi_cntrl->state_lock);
- old_state = mhi_cntrl->mhi_state;
- if (old_state == MHI_STATE_M3)
- mhi_ep_resume_channels(mhi_cntrl);
- ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0);
- if (ret) {
- mhi_ep_handle_syserr(mhi_cntrl);
- goto err_unlock;
- }
- /* Signal host that the device moved to M0 */
- ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M0);
- if (ret) {
- dev_err(dev, "Failed sending M0 state change event\n");
- goto err_unlock;
- }
- if (old_state == MHI_STATE_READY) {
- /* Send AMSS EE event to host */
- ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EE_AMSS);
- if (ret) {
- dev_err(dev, "Failed sending AMSS EE event\n");
- goto err_unlock;
- }
- }
- err_unlock:
- mutex_unlock(&mhi_cntrl->state_lock);
- return ret;
- }
- int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl)
- {
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
- int ret;
- mutex_lock(&mhi_cntrl->state_lock);
- ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3);
- if (ret) {
- mhi_ep_handle_syserr(mhi_cntrl);
- goto err_unlock;
- }
- mhi_ep_suspend_channels(mhi_cntrl);
- /* Signal host that the device moved to M3 */
- ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M3);
- if (ret) {
- dev_err(dev, "Failed sending M3 state change event\n");
- goto err_unlock;
- }
- err_unlock:
- mutex_unlock(&mhi_cntrl->state_lock);
- return ret;
- }
- int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl)
- {
- struct device *dev = &mhi_cntrl->mhi_dev->dev;
- enum mhi_state mhi_state;
- int ret, is_ready;
- mutex_lock(&mhi_cntrl->state_lock);
- /* Ensure that the MHISTATUS is set to RESET by host */
- mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK);
- is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK);
- if (mhi_state != MHI_STATE_RESET || is_ready) {
- dev_err(dev, "READY state transition failed. MHI host not in RESET state\n");
- ret = -EIO;
- goto err_unlock;
- }
- ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY);
- if (ret)
- mhi_ep_handle_syserr(mhi_cntrl);
- err_unlock:
- mutex_unlock(&mhi_cntrl->state_lock);
- return ret;
- }
|