Commit 31d8df9f authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'mhi-for-v5.13' of...

Merge tag 'mhi-for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/mani/mhi into char-misc-next

Manivannan writes:

MHI changes for v5.13

core:

- Added support for Flash Programmer execution environment which allows the
  host machine (like x86) to flash the modem firmware to NAND or eMMC in the
  modem. The MHI bus will expose EDL channels (34, 35) and then the opensource
  QDL tool [1] can be used to flash the firmware from the host.
- Added an internal helper for polling the MHI registers with a retry interval.
  This helper is used now to poll for the MHI ready state in MHI STATUS
  register.
- Various fixes for issues found during the bringup of SDX24/SDX55 based Quectel
  and Telit modems.
- Updates to the Execution environment handling for proper downloading of the
  AMSS image from SBL (Secondary Bootloader) mode.
- Added support for sending STOP channel command to the MHI device and also made
  changes to the MHI core for proper handling of stop and restart.
- Fixed the runtime_pm handling in the core by forcing the device to be in wake
  mode until TX completion and allowing it to suspend for RX.
- Added sanity checks for values read from the device to avoid crash if those
  are corrupted somehow.
- Fixed warnings generated by sparse (W=2)
- Couple of kernel doc cleanups in mhi.h

pci_generic:

- Added support for runtime PM and generic PM
- Added Firehose channels for flashing the firmware
- Added support for modems such as Quectel EM1XXGR-L, SDX24, SDX65, Foxconn
  T99W175 exposing relevant channels.

[1] https://git.linaro.org/landing-teams/working/qualcomm/qdl.git

* tag 'mhi-for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/mani/mhi: (49 commits)
  bus: mhi: fix typo in comments for struct mhi_channel_config
  bus: mhi: core: Fix shadow declarations
  bus: mhi: pci_generic: Constify mhi_controller_config struct definitions
  bus: mhi: pci_generic: Introduce Foxconn T99W175 support
  bus: mhi: core: Sanity check values from remote device before use
  bus: mhi: pci_generic: Add FIREHOSE channels
  bus: mhi: pci_generic: Implement PCI shutdown callback
  bus: mhi: Improve documentation on channel transfer setup APIs
  bus: mhi: core: Remove __ prefix for MHI channel unprepare function
  bus: mhi: core: Check channel execution environment before issuing reset
  bus: mhi: core: Clear configuration from channel context during reset
  bus: mhi: core: Hold device wake for channel update commands
  bus: mhi: core: Update debug messages to use client device
  bus: mhi: core: Improvements to the channel handling state machine
  bus: mhi: core: Clear context for stopped channels from remove()
  bus: mhi: core: Allow sending the STOP channel command
  bus: mhi: pci_generic: Add SDX65 based modem support
  bus: mhi: core: Remove pre_init flag used for power purposes
  bus: mhi: pm: reduce PM state change verbosity
  bus: mhi: core: Fix MHI runtime_pm behavior
  ...
parents aa87e317 a503d162
Loading
Loading
Loading
Loading
+31 −33
Original line number Diff line number Diff line
@@ -389,7 +389,6 @@ static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
{
	const struct firmware *firmware = NULL;
	struct image_info *image_info;
	struct device *dev = &mhi_cntrl->mhi_dev->dev;
	const char *fw_name;
	void *buf;
@@ -417,9 +416,9 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
		}
	}

	/* If device is in pass through, do reset to ready state transition */
	if (mhi_cntrl->ee == MHI_EE_PTHRU)
		goto fw_load_ee_pthru;
	/* wait for ready on pass through or any other execution environment */
	if (mhi_cntrl->ee != MHI_EE_EDL && mhi_cntrl->ee != MHI_EE_PBL)
		goto fw_load_ready_state;

	fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
		mhi_cntrl->edl_image : mhi_cntrl->fw_image;
@@ -461,9 +460,10 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
		goto error_fw_load;
	}

	if (mhi_cntrl->ee == MHI_EE_EDL) {
	/* Wait for ready since EDL image was loaded */
	if (fw_name == mhi_cntrl->edl_image) {
		release_firmware(firmware);
		return;
		goto fw_load_ready_state;
	}

	write_lock_irq(&mhi_cntrl->pm_lock);
@@ -488,47 +488,45 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)

	release_firmware(firmware);

fw_load_ee_pthru:
fw_load_ready_state:
	/* Transitioning into MHI RESET->READY state */
	ret = mhi_ready_state_transition(mhi_cntrl);

	if (!mhi_cntrl->fbc_download)
		return;

	if (ret) {
		dev_err(dev, "MHI did not enter READY state\n");
		goto error_ready_state;
	}

	/* Wait for the SBL event */
	ret = wait_event_timeout(mhi_cntrl->state_event,
				 mhi_cntrl->ee == MHI_EE_SBL ||
				 MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
				 msecs_to_jiffies(mhi_cntrl->timeout_ms));

	if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
		dev_err(dev, "MHI did not enter SBL\n");
		goto error_ready_state;
	}

	/* Start full firmware image download */
	image_info = mhi_cntrl->fbc_image;
	ret = mhi_fw_load_bhie(mhi_cntrl,
			       /* Vector table is the last entry */
			       &image_info->mhi_buf[image_info->entries - 1]);
	if (ret) {
		dev_err(dev, "MHI did not load image over BHIe, ret: %d\n",
			ret);
		goto error_fw_load;
	}

	dev_info(dev, "Wait for device to enter SBL or Mission mode\n");
	return;

error_ready_state:
	if (mhi_cntrl->fbc_download) {
		mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
		mhi_cntrl->fbc_image = NULL;
	}

error_fw_load:
	mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR;
	wake_up_all(&mhi_cntrl->state_event);
}

int mhi_download_amss_image(struct mhi_controller *mhi_cntrl)
{
	struct image_info *image_info = mhi_cntrl->fbc_image;
	struct device *dev = &mhi_cntrl->mhi_dev->dev;
	int ret;

	if (!image_info)
		return -EIO;

	ret = mhi_fw_load_bhie(mhi_cntrl,
			       /* Vector table is the last entry */
			       &image_info->mhi_buf[image_info->entries - 1]);
	if (ret) {
		dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret);
		mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR;
		wake_up_all(&mhi_cntrl->state_event);
	}

	return ret;
}
+1 −1
Original line number Diff line number Diff line
@@ -377,7 +377,7 @@ static struct dentry *mhi_debugfs_root;
void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
{
	mhi_cntrl->debugfs_dentry =
			debugfs_create_dir(dev_name(mhi_cntrl->cntrl_dev),
			debugfs_create_dir(dev_name(&mhi_cntrl->mhi_dev->dev),
					   mhi_debugfs_root);

	debugfs_create_file("states", 0444, mhi_cntrl->debugfs_dentry,
+43 −29
Original line number Diff line number Diff line
@@ -22,13 +22,14 @@
static DEFINE_IDA(mhi_controller_ida);

const char * const mhi_ee_str[MHI_EE_MAX] = {
	[MHI_EE_PBL] = "PBL",
	[MHI_EE_SBL] = "SBL",
	[MHI_EE_AMSS] = "AMSS",
	[MHI_EE_RDDM] = "RDDM",
	[MHI_EE_WFW] = "WFW",
	[MHI_EE_PTHRU] = "PASS THRU",
	[MHI_EE_EDL] = "EDL",
	[MHI_EE_PBL] = "PRIMARY BOOTLOADER",
	[MHI_EE_SBL] = "SECONDARY BOOTLOADER",
	[MHI_EE_AMSS] = "MISSION MODE",
	[MHI_EE_RDDM] = "RAMDUMP DOWNLOAD MODE",
	[MHI_EE_WFW] = "WLAN FIRMWARE",
	[MHI_EE_PTHRU] = "PASS THROUGH",
	[MHI_EE_EDL] = "EMERGENCY DOWNLOAD",
	[MHI_EE_FP] = "FLASH PROGRAMMER",
	[MHI_EE_DISABLE_TRANSITION] = "DISABLE",
	[MHI_EE_NOT_SUPPORTED] = "NOT SUPPORTED",
};
@@ -37,8 +38,9 @@ const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
	[DEV_ST_TRANSITION_PBL] = "PBL",
	[DEV_ST_TRANSITION_READY] = "READY",
	[DEV_ST_TRANSITION_SBL] = "SBL",
	[DEV_ST_TRANSITION_MISSION_MODE] = "MISSION_MODE",
	[DEV_ST_TRANSITION_SYS_ERR] = "SYS_ERR",
	[DEV_ST_TRANSITION_MISSION_MODE] = "MISSION MODE",
	[DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
	[DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
	[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
};

@@ -49,24 +51,30 @@ const char * const mhi_state_str[MHI_STATE_MAX] = {
	[MHI_STATE_M1] = "M1",
	[MHI_STATE_M2] = "M2",
	[MHI_STATE_M3] = "M3",
	[MHI_STATE_M3_FAST] = "M3_FAST",
	[MHI_STATE_M3_FAST] = "M3 FAST",
	[MHI_STATE_BHI] = "BHI",
	[MHI_STATE_SYS_ERR] = "SYS_ERR",
	[MHI_STATE_SYS_ERR] = "SYS ERROR",
};

const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
	[MHI_CH_STATE_TYPE_RESET] = "RESET",
	[MHI_CH_STATE_TYPE_STOP] = "STOP",
	[MHI_CH_STATE_TYPE_START] = "START",
};

static const char * const mhi_pm_state_str[] = {
	[MHI_PM_STATE_DISABLE] = "DISABLE",
	[MHI_PM_STATE_POR] = "POR",
	[MHI_PM_STATE_POR] = "POWER ON RESET",
	[MHI_PM_STATE_M0] = "M0",
	[MHI_PM_STATE_M2] = "M2",
	[MHI_PM_STATE_M3_ENTER] = "M?->M3",
	[MHI_PM_STATE_M3] = "M3",
	[MHI_PM_STATE_M3_EXIT] = "M3->M0",
	[MHI_PM_STATE_FW_DL_ERR] = "FW DL Error",
	[MHI_PM_STATE_SYS_ERR_DETECT] = "SYS_ERR Detect",
	[MHI_PM_STATE_SYS_ERR_PROCESS] = "SYS_ERR Process",
	[MHI_PM_STATE_FW_DL_ERR] = "Firmware Download Error",
	[MHI_PM_STATE_SYS_ERR_DETECT] = "SYS ERROR Detect",
	[MHI_PM_STATE_SYS_ERR_PROCESS] = "SYS ERROR Process",
	[MHI_PM_STATE_SHUTDOWN_PROCESS] = "SHUTDOWN Process",
	[MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "LD or Error Fatal Detect",
	[MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "Linkdown or Error Fatal Detect",
};

const char *to_mhi_pm_state_str(enum mhi_pm_state state)
@@ -508,8 +516,6 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)

	/* Setup wake db */
	mhi_cntrl->wake_db = base + val + (8 * MHI_DEV_WAKE_DB);
	mhi_write_reg(mhi_cntrl, mhi_cntrl->wake_db, 4, 0);
	mhi_write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0);
	mhi_cntrl->wake_set = false;

	/* Setup channel db address for each channel in tre_ring */
@@ -552,6 +558,7 @@ void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
	struct mhi_ring *buf_ring;
	struct mhi_ring *tre_ring;
	struct mhi_chan_ctxt *chan_ctxt;
	u32 tmp;

	buf_ring = &mhi_chan->buf_ring;
	tre_ring = &mhi_chan->tre_ring;
@@ -565,7 +572,19 @@ void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
	vfree(buf_ring->base);

	buf_ring->base = tre_ring->base = NULL;
	tre_ring->ctxt_wp = NULL;
	chan_ctxt->rbase = 0;
	chan_ctxt->rlen = 0;
	chan_ctxt->rp = 0;
	chan_ctxt->wp = 0;

	tmp = chan_ctxt->chcfg;
	tmp &= ~CHAN_CTX_CHSTATE_MASK;
	tmp |= (MHI_CH_STATE_DISABLED << CHAN_CTX_CHSTATE_SHIFT);
	chan_ctxt->chcfg = tmp;

	/* Update to all cores */
	smp_wmb();
}

int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
@@ -863,12 +882,10 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
	u32 soc_info;
	int ret, i;

	if (!mhi_cntrl)
		return -EINVAL;

	if (!mhi_cntrl->runtime_get || !mhi_cntrl->runtime_put ||
	if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->regs ||
	    !mhi_cntrl->runtime_get || !mhi_cntrl->runtime_put ||
	    !mhi_cntrl->status_cb || !mhi_cntrl->read_reg ||
	    !mhi_cntrl->write_reg || !mhi_cntrl->nr_irqs)
	    !mhi_cntrl->write_reg || !mhi_cntrl->nr_irqs || !mhi_cntrl->irq)
		return -EINVAL;

	ret = parse_config(mhi_cntrl, config);
@@ -890,8 +907,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
	INIT_WORK(&mhi_cntrl->st_worker, mhi_pm_st_worker);
	init_waitqueue_head(&mhi_cntrl->state_event);

	mhi_cntrl->hiprio_wq = alloc_ordered_workqueue
				("mhi_hiprio_wq", WQ_MEM_RECLAIM | WQ_HIGHPRI);
	mhi_cntrl->hiprio_wq = alloc_ordered_workqueue("mhi_hiprio_wq", WQ_HIGHPRI);
	if (!mhi_cntrl->hiprio_wq) {
		dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate workqueue\n");
		ret = -ENOMEM;
@@ -1083,8 +1099,6 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
			mhi_rddm_prepare(mhi_cntrl, mhi_cntrl->rddm_image);
	}

	mhi_cntrl->pre_init = true;

	mutex_unlock(&mhi_cntrl->pm_mutex);

	return 0;
@@ -1115,7 +1129,6 @@ void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl)
	}

	mhi_deinit_dev_ctxt(mhi_cntrl);
	mhi_cntrl->pre_init = false;
}
EXPORT_SYMBOL_GPL(mhi_unprepare_after_power_down);

@@ -1296,7 +1309,8 @@ 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_ENABLED ||
		     ch_state[dir] == MHI_CH_STATE_STOP) &&
		    !mhi_chan->offload_ch)
			mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);

+19 −1
Original line number Diff line number Diff line
@@ -369,6 +369,18 @@ enum mhi_ch_state {
	MHI_CH_STATE_ERROR = 0x5,
};

enum mhi_ch_state_type {
	MHI_CH_STATE_TYPE_RESET,
	MHI_CH_STATE_TYPE_STOP,
	MHI_CH_STATE_TYPE_START,
	MHI_CH_STATE_TYPE_MAX,
};

extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \
				     "INVALID_STATE" : \
				     mhi_ch_state_type_str[(state)])

#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \
				    mode != MHI_DB_BRST_ENABLE)

@@ -379,13 +391,15 @@ extern const char * const mhi_ee_str[MHI_EE_MAX];
#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \
			ee == MHI_EE_EDL)

#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW)
#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW || \
				 ee == MHI_EE_FP)

enum dev_st_transition {
	DEV_ST_TRANSITION_PBL,
	DEV_ST_TRANSITION_READY,
	DEV_ST_TRANSITION_SBL,
	DEV_ST_TRANSITION_MISSION_MODE,
	DEV_ST_TRANSITION_FP,
	DEV_ST_TRANSITION_SYS_ERR,
	DEV_ST_TRANSITION_DISABLE,
	DEV_ST_TRANSITION_MAX,
@@ -619,6 +633,7 @@ int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl);
int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl);
int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
		 enum mhi_cmd_type cmd);
int mhi_download_amss_image(struct mhi_controller *mhi_cntrl);
static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl)
{
	return (mhi_cntrl->dev_state >= MHI_STATE_M0 &&
@@ -643,6 +658,9 @@ int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
				    void __iomem *base, u32 offset, u32 mask,
				    u32 shift, u32 *out);
int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
				    void __iomem *base, u32 offset, u32 mask,
				    u32 shift, u32 val, u32 delayus);
void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
		   u32 offset, u32 val);
void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
+288 −128

File changed.

Preview size limit exceeded, changes collapsed.

Loading