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

Commit c8a4959a authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mhi: core: Add support for host triggered device ram dump"

parents ce2bc3a0 00b9874b
Loading
Loading
Loading
Loading
+108 −9
Original line number Diff line number Diff line
@@ -249,6 +249,13 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic)
{
	struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
	struct bhie_vec_table *rddm_table = &bhi_ctxt->rddm_table;
	struct bhie_mem_info *bhie_mem_info;
	u32 rx_sequence, val, current_seq;
	u32 timeout = (bhi_ctxt->poll_timeout * 1000) / BHIE_RDDM_DELAY_TIME_US;
	int i;
	u32 cur_exec, prev_exec = 0;
	u32 state, prev_state = 0;
	u32 rx_status, prev_status = 0;

	if (!rddm_table->bhie_mem_info) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "RDDM table == NULL\n");
@@ -258,9 +265,93 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic)
	if (!in_panic)
		return bhi_rddm_graceful(mhi_dev_ctxt);

	/*
	 * Below code should only be executed during kernel panic,
	 * we expect other cores to be shutting down while we're
	 * executing rddm transfer. After returning from this function,
	 * we expect device to reset.
	 */

	/* Trigger device into RDDM */
	mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "pm_state:0x%x mhi_state:%s\n",
		mhi_dev_ctxt->mhi_pm_state,
		TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
	if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
		"RDDM collection in panic not yet supported\n");
	return -EINVAL;
			"Register access not allowed\n");
		return -EIO;
	}

	/*
	 * Normally we only set mhi_pm_state after grabbing pm_xfer_lock as a
	 * write, by function mhi_tryset_pm_state. Since we're in a kernel
	 * panic, we will set pm state w/o grabbing xfer lock. We're setting
	 * pm_state to LD as a safety precautions. If another core in middle
	 * of register access this should deter it. However, there is no
	 * no gurantee change will take effect.
	 */
	mhi_dev_ctxt->mhi_pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
	/* change should take effect immediately */
	smp_wmb();

	bhie_mem_info = &rddm_table->
		bhie_mem_info[rddm_table->segment_count - 1];
	rx_sequence = rddm_table->sequence++;

	/* program the vector table */
	mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Programming RXVEC table\n");
	val = HIGH_WORD(bhie_mem_info->phys_addr);
	mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base,
		      BHIE_RXVECADDR_HIGH_OFFS, val);
	val = LOW_WORD(bhie_mem_info->phys_addr);
	mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECADDR_LOW_OFFS,
		      val);
	val = (u32)bhie_mem_info->size;
	mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECSIZE_OFFS,
		      val);
	mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECDB_OFFS,
			    BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
			    rx_sequence);

	/* trigger device into rddm */
	mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
		"Triggering Device into RDDM mode\n");
	mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_SYS_ERR);
	i = 0;

	while (timeout--) {
		cur_exec = mhi_reg_read(bhi_ctxt->bhi_base, BHI_EXECENV);
		state = mhi_get_m_state(mhi_dev_ctxt);
		rx_status = mhi_reg_read(bhi_ctxt->bhi_base,
					 BHIE_RXVECSTATUS_OFFS);
		/* if reg. values changed or each sec (udelay(1000)) log it */
		if (cur_exec != prev_exec || state != prev_state ||
		    rx_status != prev_status || !(i & (SZ_1K - 1))) {
			mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
				"EXECENV:0x%x MHISTATE:0x%x RXSTATUS:0x%x\n",
				cur_exec, state, rx_status);
			prev_exec = cur_exec;
			prev_state = state;
			prev_status = rx_status;
		};
		current_seq = (rx_status & BHIE_TXVECSTATUS_SEQNUM_BMSK) >>
			BHIE_TXVECSTATUS_SEQNUM_SHFT;
		rx_status = (rx_status & BHIE_TXVECSTATUS_STATUS_BMSK) >>
			BHIE_TXVECSTATUS_STATUS_SHFT;

		if ((rx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) &&
		    (current_seq == rx_sequence)) {
			mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
				"rddm transfer completed\n");
			return 0;
		}
		udelay(BHIE_RDDM_DELAY_TIME_US);
		i++;
	}

	mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "rddm transfer timeout\n");

	return -EIO;
}

static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -439,7 +530,6 @@ void bhi_firmware_download(struct work_struct *work)
	struct bhi_ctxt_t *bhi_ctxt;
	struct bhie_mem_info mem_info;
	int ret;
	long timeout;

	mhi_dev_ctxt = container_of(work, struct mhi_device_ctxt,
				    bhi_ctxt.fw_load_work);
@@ -448,7 +538,14 @@ void bhi_firmware_download(struct work_struct *work)
	mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");

	wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
			mhi_dev_ctxt->mhi_state == MHI_STATE_BHI);
		mhi_dev_ctxt->mhi_state == MHI_STATE_BHI ||
		mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT);
	if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT ||
	    mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
			"MHI is not in valid state for firmware download\n");
		return;
	}

	/* PBL image is the first segment in firmware vector table */
	mem_info = *bhi_ctxt->fw_table.bhie_mem_info;
@@ -462,10 +559,12 @@ void bhi_firmware_download(struct work_struct *work)
	mhi_init_state_transition(mhi_dev_ctxt,
				  STATE_TRANSITION_RESET);

	timeout = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
				mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE,
	wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
		mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE ||
		mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
		msecs_to_jiffies(bhi_ctxt->poll_timeout));
	if (!timeout) {
	if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT ||
	    mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_BHIE) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
			"Failed to Enter EXEC_ENV_BHIE\n");
		return;
+1 −0
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@

#define BHI_POLL_SLEEP_TIME_MS 100
#define BHI_POLL_TIMEOUT_MS 2000
#define BHIE_RDDM_DELAY_TIME_US (1000)

int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt);
void bhi_firmware_download(struct work_struct *work);
+84 −21
Original line number Diff line number Diff line
@@ -22,6 +22,22 @@
#include "mhi_hwio.h"
#include "mhi_bhi.h"

static const char *const mhi_dev_ctrl_str[MHI_DEV_CTRL_MAXCMD] = {
	[MHI_DEV_CTRL_INIT] = "INIT",
	[MHI_DEV_CTRL_DE_INIT] = "DE-INIT",
	[MHI_DEV_CTRL_SUSPEND] = "SUSPEND",
	[MHI_DEV_CTRL_RESUME] = "RESUME",
	[MHI_DEV_CTRL_POWER_OFF] = "OFF",
	[MHI_DEV_CTRL_POWER_ON] = "ON",
	[MHI_DEV_CTRL_TRIGGER_RDDM] = "TRIGGER RDDM",
	[MHI_DEV_CTRL_RDDM] = "RDDM",
	[MHI_DEV_CTRL_RDDM_KERNEL_PANIC] = "RDDM IN PANIC",
	[MHI_DEV_CTRL_NOTIFY_LINK_ERROR] = "LD",
};

#define TO_MHI_DEV_CTRL_STR(cmd) ((cmd >= MHI_DEV_CTRL_MAXCMD) ? "INVALID" : \
				  mhi_dev_ctrl_str[cmd])

/* Write only sysfs attributes */
static DEVICE_ATTR(MHI_M0, S_IWUSR, NULL, sysfs_init_m0);
static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3);
@@ -98,11 +114,13 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
	read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
	r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
		mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
			       mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
		mhi_dev_ctxt->mhi_state == MHI_STATE_M1 ||
		mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
		msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
	if (!r) {
	if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
			"Failed to get M0||M1 event, timeout, current state:%s\n",
			"Failed to get M0||M1 event or LD pm_state:0x%x state:%s\n",
			mhi_dev_ctxt->mhi_pm_state,
			TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
		return -EIO;
	}
@@ -122,9 +140,10 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
	write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
	mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Waiting for M3 completion.\n");
	r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event,
			       mhi_dev_ctxt->mhi_state == MHI_STATE_M3,
		mhi_dev_ctxt->mhi_state == MHI_STATE_M3 ||
		mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
		msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT));
	if (!r) {
	if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
			"Failed to get M3 event, timeout, current state:%s\n",
			TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
@@ -159,11 +178,12 @@ static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt)
	write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
	r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
		mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
			       mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
		mhi_dev_ctxt->mhi_state == MHI_STATE_M1 ||
		mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
		msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
	if (!r) {
	if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
			"Failed to get M0 event, timeout\n");
			"Failed to get M0 event, timeout or LD\n");
		r = -EIO;
	} else
		r = 0;
@@ -295,13 +315,16 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
	mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
	read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);

	ret_val = wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete,
	wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete,
				    msecs_to_jiffies(timeout));
	if (!ret_val || mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS)
	if (mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS)
		ret_val = -EIO;
	else
		ret_val = 0;

	/* wait for firmware download to complete */
	flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work);

	if (ret_val) {
		read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
		mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
@@ -537,16 +560,16 @@ void mhi_link_state_cb(struct msm_pcie_notify *notify)
	}
}

int mhi_pm_control_device(struct mhi_device *mhi_device,
		       enum mhi_dev_ctrl ctrl)
int mhi_pm_control_device(struct mhi_device *mhi_device, enum mhi_dev_ctrl ctrl)
{
	struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->mhi_dev_ctxt;
	unsigned long flags;

	if (!mhi_dev_ctxt)
		return -EINVAL;

	mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
		"Entered with cmd:%d\n", ctrl);
	mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered with cmd:%s\n",
		TO_MHI_DEV_CTRL_STR(ctrl));

	switch (ctrl) {
	case MHI_DEV_CTRL_INIT:
@@ -560,12 +583,46 @@ int mhi_pm_control_device(struct mhi_device *mhi_device,
	case MHI_DEV_CTRL_POWER_OFF:
		mhi_pm_slave_mode_power_off(mhi_dev_ctxt);
		break;
	case MHI_DEV_CTRL_TRIGGER_RDDM:
		write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
		if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
			write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock,
						flags);
			mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
				"failed to trigger rddm, no register access in state:0x%x\n",
				mhi_dev_ctxt->mhi_pm_state);
			return -EIO;
		}
		mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_SYS_ERR);
		write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
		break;
	case MHI_DEV_CTRL_RDDM:
		return bhi_rddm(mhi_dev_ctxt, false);
	case MHI_DEV_CTRL_RDDM_KERNEL_PANIC:
		return bhi_rddm(mhi_dev_ctxt, true);
	case MHI_DEV_CTRL_DE_INIT:
		if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE)
		if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE) {
			enum MHI_PM_STATE cur_state;
			/*
			 * If bus master calls DE_INIT before calling POWER_OFF
			 * means a critical failure occurred during POWER_ON
			 * state transition and external PCIe device may not
			 * respond to host.  Force PM state to PCIe linkdown
			 * state prior to starting shutdown process to avoid
			 * accessing PCIe link.
			 */
			write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
			cur_state = mhi_tryset_pm_state(mhi_dev_ctxt,
						MHI_PM_LD_ERR_FATAL_DETECT);
			write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
			if (unlikely(cur_state != MHI_PM_LD_ERR_FATAL_DETECT)) {
				mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
					"Failed to transition to state 0x%x from 0x%x\n",
					MHI_PM_LD_ERR_FATAL_DETECT, cur_state);
			}
			process_disable_transition(MHI_PM_SHUTDOWN_PROCESS,
						   mhi_dev_ctxt);
		}
		bhi_exit(mhi_dev_ctxt);
		break;
	case MHI_DEV_CTRL_NOTIFY_LINK_ERROR:
@@ -580,6 +637,12 @@ int mhi_pm_control_device(struct mhi_device *mhi_device,
			mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
				"Failed to transition to state 0x%x from 0x%x\n",
				MHI_PM_LD_ERR_FATAL_DETECT, cur_state);

		/* wake up all threads that's waiting for state change events */
		complete(&mhi_dev_ctxt->cmd_complete);
		wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
		wake_up(mhi_dev_ctxt->mhi_ev_wq.m0_event);
		wake_up(mhi_dev_ctxt->mhi_ev_wq.m3_event);
		break;
	}
	default:
+3 −2
Original line number Diff line number Diff line
@@ -147,7 +147,8 @@ void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt,
 *     M1 -> M3_ENTER --> M3
 * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR
 * L2: SHUTDOWN_PROCESS -> DISABLE -> SSR_PENDING (via SSR Notification only)
 * L3: LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS
 * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT
 *     LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS
 */
static const struct mhi_pm_transitions const mhi_state_transitions[] = {
	/* L0 States */
@@ -216,7 +217,7 @@ static const struct mhi_pm_transitions const mhi_state_transitions[] = {
	/* L3 States */
	{
		MHI_PM_LD_ERR_FATAL_DETECT,
		MHI_PM_SHUTDOWN_PROCESS
		MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_PROCESS
	},
	/* From SSR notification only */
	{
+2 −0
Original line number Diff line number Diff line
@@ -160,9 +160,11 @@ enum mhi_dev_ctrl {
	MHI_DEV_CTRL_RESUME,
	MHI_DEV_CTRL_POWER_OFF,
	MHI_DEV_CTRL_POWER_ON,
	MHI_DEV_CTRL_TRIGGER_RDDM,
	MHI_DEV_CTRL_RDDM,
	MHI_DEV_CTRL_RDDM_KERNEL_PANIC,
	MHI_DEV_CTRL_NOTIFY_LINK_ERROR,
	MHI_DEV_CTRL_MAXCMD,
};

enum mhi_rddm_segment {