Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfcf7c9b authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mhi: cntrl: qcom: Add support for fatal error processing"

parents 2fc1d24f 5e803bd9
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -525,6 +525,22 @@ static int mhi_qcom_power_up(struct mhi_controller *mhi_cntrl)
	return ret;
}

static void mhi_qcom_fatal_worker(struct work_struct *work)
{
	struct mhi_dev *mhi_dev = container_of(work, struct mhi_dev,
					       fatal_worker);
	struct device *dev = &mhi_dev->pci_dev->dev;
	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
	int ret;

	mhi_power_down(mhi_cntrl, true);

	ret = mhi_qcom_power_up(mhi_cntrl);
	if (ret)
		MHI_ERR("Power up failure after SYS ERROR in PBL, ret:%d\n",
			ret);
}

static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv)
{
	struct mhi_dev *mhi_dev = priv;
@@ -569,6 +585,10 @@ static void mhi_status_cb(struct mhi_controller *mhi_cntrl,
		pm_runtime_put(dev);
		mhi_arch_mission_mode_enter(mhi_cntrl);
		break;
	case MHI_CB_FATAL_ERROR:
		MHI_CNTRL_ERR("Perform power cycle due to SYS ERROR in PBL\n");
		schedule_work(&mhi_dev->fatal_worker);
		break;
	default:
		MHI_CNTRL_LOG("Unhandled cb:0x%x\n", reason);
	}
@@ -782,6 +802,7 @@ static struct mhi_controller *mhi_register_controller(struct pci_dev *pci_dev)
		goto error_register;

	INIT_WORK(&mhi_cntrl->reg_write_work, mhi_reg_write_work);
	INIT_WORK(&mhi_dev->fatal_worker, mhi_qcom_fatal_worker);

	mhi_cntrl->reg_write_q = kcalloc(REG_WRITE_QUEUE_LEN,
					sizeof(*mhi_cntrl->reg_write_q),
+1 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ struct mhi_dev {
	dma_addr_t iova_start;
	dma_addr_t iova_stop;
	enum mhi_suspend_mode suspend_mode;
	struct work_struct fatal_worker;

	/* hardware info */
	u32 serial_num;
+39 −8
Original line number Diff line number Diff line
@@ -1967,7 +1967,8 @@ static int mhi_driver_remove(struct device *dev)
		MHI_CH_STATE_DISABLED,
		MHI_CH_STATE_DISABLED
	};
	int dir;
	int dir, ret;
	bool interrupted = false;

	/* control device has no work to do */
	if (mhi_dev->dev_type == MHI_CONTROLLER_TYPE)
@@ -1975,11 +1976,11 @@ static int mhi_driver_remove(struct device *dev)

	MHI_LOG("Removing device for chan:%s\n", mhi_dev->chan_name);

	/* reset both channels */
	/* move both channels to suspended state and disallow processing */
	for (dir = 0; dir < 2; dir++) {
		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;

		if (!mhi_chan)
		if (!mhi_chan || mhi_chan->offload_ch)
			continue;

		/* wake all threads waiting for completion */
@@ -1988,15 +1989,45 @@ static int mhi_driver_remove(struct device *dev)
		complete_all(&mhi_chan->completion);
		write_unlock_irq(&mhi_chan->lock);

		/* move channel state to disable, no more processing */
		mutex_lock(&mhi_chan->mutex);
		write_lock_irq(&mhi_chan->lock);
		if (mhi_chan->ch_state != MHI_CH_STATE_DISABLED) {
			ch_state[dir] = mhi_chan->ch_state;
			mhi_chan->ch_state = MHI_CH_STATE_SUSPENDED;
		}
		write_unlock_irq(&mhi_chan->lock);
		mutex_unlock(&mhi_chan->mutex);
	}

	/* wait for each channel to close and reset both channels */
	for (dir = 0; dir < 2; dir++) {
		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;

		if (!mhi_chan || mhi_chan->offload_ch)
			continue;

		/* unbind request from userspace, wait for channel reset */
		if (!(mhi_cntrl->power_down ||
		    MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) &&
		    ch_state[dir] != MHI_CH_STATE_DISABLED && !interrupted) {
			MHI_ERR("Channel %s busy, wait for it to be reset\n",
				mhi_dev->chan_name);
			ret = wait_event_interruptible(mhi_cntrl->state_event,
				mhi_chan->ch_state == MHI_CH_STATE_DISABLED ||
				MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state));
			if (unlikely(ret))
				interrupted = true;
		}

		/* update channel state as an error can exit above wait */
		mutex_lock(&mhi_chan->mutex);

		write_lock_irq(&mhi_chan->lock);
		ch_state[dir] = mhi_chan->ch_state;
		write_unlock_irq(&mhi_chan->lock);

		/* reset the channel */
		if (!mhi_chan->offload_ch)
		/* reset channel if it was left enabled */
		if (ch_state[dir] != MHI_CH_STATE_DISABLED)
			mhi_reset_chan(mhi_cntrl, mhi_chan);

		mutex_unlock(&mhi_chan->mutex);
@@ -2014,7 +2045,7 @@ static int mhi_driver_remove(struct device *dev)

		mutex_lock(&mhi_chan->mutex);

		if (ch_state[dir] == MHI_CH_STATE_ENABLED &&
		if (ch_state[dir] != MHI_CH_STATE_DISABLED &&
		    !mhi_chan->offload_ch)
			mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);

+27 −3
Original line number Diff line number Diff line
@@ -1259,6 +1259,22 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
				st = MHI_ST_TRANSITION_MISSION_MODE;
				break;
			case MHI_EE_RDDM:
				if (mhi_cntrl->ee == MHI_EE_RDDM ||
				    mhi_cntrl->power_down)
					break;

				MHI_ERR("RDDM event occurred!\n");
				write_lock_irq(&mhi_cntrl->pm_lock);
				mhi_cntrl->ee = MHI_EE_RDDM;
				write_unlock_irq(&mhi_cntrl->pm_lock);

				/* notify critical clients */
				mhi_control_error(mhi_cntrl);

				mhi_cntrl->status_cb(mhi_cntrl,
						     mhi_cntrl->priv_data,
						     MHI_CB_EE_RDDM);
				wake_up_all(&mhi_cntrl->state_event);
				break;
			default:
				MHI_ERR("Unhandled EE event:%s\n",
@@ -2038,19 +2054,22 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
{
	int ret;
	bool in_mission_mode = false;
	bool notify = false;

	MHI_LOG("Entered: unprepare channel:%d\n", mhi_chan->chan);

	/* no more processing events for this channel */
	mutex_lock(&mhi_chan->mutex);
	write_lock_irq(&mhi_chan->lock);
	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) {
	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED &&
	    mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) {
		MHI_LOG("chan:%d is already disabled\n", mhi_chan->chan);
		write_unlock_irq(&mhi_chan->lock);
		mutex_unlock(&mhi_chan->mutex);
		return;
	}

	if (mhi_chan->ch_state == MHI_CH_STATE_SUSPENDED)
		notify = true;
	mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
	write_unlock_irq(&mhi_chan->lock);

@@ -2091,6 +2110,10 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
	if (!mhi_chan->offload_ch) {
		mhi_reset_chan(mhi_cntrl, mhi_chan);
		mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);

		/* notify waiters to proceed with unbinding channel */
		if (notify)
			wake_up_all(&mhi_cntrl->state_event);
	}
	MHI_LOG("chan:%d successfully resetted\n", mhi_chan->chan);
	mutex_unlock(&mhi_chan->mutex);
@@ -2361,7 +2384,8 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
		 mhi_chan->chan, cmd == MHI_CMD_START_CHAN ? "START" : "STOP");

	/* if channel is not active state state do not allow to state change */
	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) {
	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED &&
	    mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) {
		ret = -EINVAL;
		MHI_LOG("channel:%d is not in active state, ch_state%d\n",
			mhi_chan->chan, mhi_chan->ch_state);
+21 −0
Original line number Diff line number Diff line
@@ -1487,6 +1487,26 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client)
		return -EIO;
	}

	if (mhi_cntrl->rddm_supported) {
		if (mhi_get_exec_env(mhi_cntrl) == MHI_EE_RDDM &&
		    !mhi_cntrl->power_down) {
			mhi_cntrl->ee = MHI_EE_RDDM;
			write_unlock_irq(&mhi_cntrl->pm_lock);

			MHI_ERR("RDDM event occurred!\n");

			/* notify critical clients with early notifications */
			mhi_control_error(mhi_cntrl);

			mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
					     MHI_CB_EE_RDDM);
			wake_up_all(&mhi_cntrl->state_event);

			tasklet_enable(&mhi_cntrl->mhi_event->task);
			goto exit_pm_fast_resume;
		}
	}

	/* restore the states */
	mhi_cntrl->pm_state = mhi_cntrl->saved_pm_state;
	mhi_cntrl->dev_state = mhi_cntrl->saved_dev_state;
@@ -1530,6 +1550,7 @@ int mhi_pm_fast_resume(struct mhi_controller *mhi_cntrl, bool notify_client)
	/* schedules worker if any special purpose events need to be handled */
	mhi_special_events_pending(mhi_cntrl);

exit_pm_fast_resume:
	MHI_LOG("Exit with pm_state:%s dev_state:%s\n",
		to_mhi_pm_state_str(mhi_cntrl->pm_state),
		TO_MHI_STATE_STR(mhi_cntrl->dev_state));