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

Commit 83e90498 authored by Sujeev Dias's avatar Sujeev Dias Committed by Gerrit - the friendly Code Review server
Browse files

mhi: cntrl: add support for advanced power management featues



Add support to allow system suspend while PCIe link is in
active state, allow host to initiate automatic suspend whenever
PCIe link is in idle state by taking advantage of L1ss inactivity
timer, and add support for drv handoff if soc supports it.

CRs-Fixed: 2418347
Change-Id: I02b66d790e7b8b625d186431d89f4d6bda5268d4
Signed-off-by: default avatarSujeev Dias <sdias@codeaurora.org>
parent 0163f65f
Loading
Loading
Loading
Loading
+107 −26
Original line number Diff line number Diff line
@@ -82,7 +82,8 @@ static void mhi_arch_pci_link_state_cb(struct msm_pcie_notify *notify)
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct pci_dev *pci_dev = mhi_dev->pci_dev;

	if (notify->event == MSM_PCIE_EVENT_WAKEUP) {
	switch (notify->event) {
	case MSM_PCIE_EVENT_WAKEUP:
		MHI_LOG("Received MSM_PCIE_EVENT_WAKE signal\n");

		/* bring link out of d3cold */
@@ -90,6 +91,15 @@ static void mhi_arch_pci_link_state_cb(struct msm_pcie_notify *notify)
			pm_runtime_get(&pci_dev->dev);
			pm_runtime_put_noidle(&pci_dev->dev);
		}
		break;
	case MSM_PCIE_EVENT_L1SS_TIMEOUT:
		MHI_VERB("Received MSM_PCIE_EVENT_L1SS_TIMEOUT signal\n");

		pm_runtime_mark_last_busy(&pci_dev->dev);
		pm_request_autosuspend(&pci_dev->dev);
		break;
	default:
		MHI_ERR("Unhandled event 0x%x\n", notify->event);
	}
}

@@ -133,6 +143,22 @@ static int mhi_arch_esoc_ops_power_on(void *priv, unsigned int flags)
	return mhi_pci_probe(pci_dev, NULL);
}

static void mhi_arch_link_off(struct mhi_controller *mhi_cntrl)
{
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct pci_dev *pci_dev = mhi_dev->pci_dev;

	MHI_LOG("Entered\n");

	pci_set_power_state(pci_dev, PCI_D3hot);

	/* release the resources */
	msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0);
	mhi_arch_set_bus_request(mhi_cntrl, 0);

	MHI_LOG("Exited\n");
}

static void mhi_arch_esoc_ops_power_off(void *priv, unsigned int flags)
{
	struct mhi_controller *mhi_cntrl = priv;
@@ -156,7 +182,7 @@ static void mhi_arch_esoc_ops_power_off(void *priv, unsigned int flags)

	/* turn the link off */
	mhi_deinit_pci_dev(mhi_cntrl);
	mhi_arch_link_off(mhi_cntrl, false);
	mhi_arch_link_off(mhi_cntrl);

	/* wait for boot monitor to exit */
	async_synchronize_cookie(arch_info->cookie + 1);
@@ -213,9 +239,16 @@ static void mhi_boot_monitor(void *data, async_cookie_t cookie)
		       TO_MHI_EXEC_STR(mhi_cntrl->ee));

	/* if we successfully booted to amss disable boot log channel */
	if (mhi_cntrl->ee == MHI_EE_AMSS) {
		boot_dev = arch_info->boot_dev;
	if (boot_dev && mhi_cntrl->ee == MHI_EE_AMSS)
		if (boot_dev)
			mhi_unprepare_from_transfer(boot_dev);

		/* enable link inactivity timer to start auto suspend */
		msm_pcie_l1ss_timeout_enable(mhi_dev->pci_dev);

		pm_runtime_allow(&mhi_dev->pci_dev->dev);
	}
}

int mhi_arch_power_up(struct mhi_controller *mhi_cntrl)
@@ -265,8 +298,7 @@ static void mhi_arch_pcie_bw_scale_work(struct work_struct *work)
	int ret;

	mutex_lock(&mhi_cntrl->pm_mutex);
	if (!mhi_dev->powered_on ||
	    pm_runtime_status_suspended(dev) || dev->power.is_suspended)
	if (!mhi_dev->powered_on || MHI_IS_SUSPENDED(mhi_dev->suspend_mode))
		goto exit_work;

	/* copy the latest speed change */
@@ -353,6 +385,7 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
			return -ENOMEM;

		mhi_dev->arch_info = arch_info;
		arch_info->mhi_dev = mhi_dev;

		snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u",
			 mhi_cntrl->dev_id, mhi_cntrl->domain, mhi_cntrl->bus,
@@ -374,7 +407,9 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)

		/* register with pcie rc for WAKE# events */
		reg_event = &arch_info->pcie_reg_event;
		reg_event->events = MSM_PCIE_EVENT_WAKEUP;
		reg_event->events =
			MSM_PCIE_EVENT_WAKEUP | MSM_PCIE_EVENT_L1SS_TIMEOUT;

		reg_event->user = mhi_dev->pci_dev;
		reg_event->callback = mhi_arch_pci_link_state_cb;
		reg_event->notify.data = mhi_cntrl;
@@ -407,6 +442,13 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
		/* save reference state for pcie config space */
		arch_info->ref_pcie_state = pci_store_saved_state(
							mhi_dev->pci_dev);
		/*
		 * MHI host driver has full autonomy to manage power state.
		 * Disable all automatic power collapse features
		 */
		msm_pcie_pm_control(MSM_PCIE_DISABLE_PC, mhi_cntrl->bus,
				    mhi_dev->pci_dev, NULL, 0);
		mhi_dev->pci_dev->no_d3hot = true;

		INIT_WORK(&arch_info->bw_scale_work,
			  mhi_arch_pcie_bw_scale_work);
@@ -566,50 +608,58 @@ void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl)
	mhi_cntrl->dev = NULL;
}

int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful)
int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl)
{
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;
	struct pci_dev *pci_dev = mhi_dev->pci_dev;
	int ret;
	int ret = 0;

	MHI_LOG("Entered\n");

	if (graceful) {
	/* disable inactivity timer */
	msm_pcie_l1ss_timeout_disable(pci_dev);

	switch (mhi_dev->suspend_mode) {
	case MHI_DEFAULT_SUSPEND:
		pci_clear_master(pci_dev);
		ret = pci_save_state(mhi_dev->pci_dev);
		if (ret) {
			MHI_ERR("Failed with pci_save_state, ret:%d\n", ret);
			return ret;
			goto exit_suspend;
		}

		arch_info->pcie_state = pci_store_saved_state(pci_dev);
		pci_disable_device(pci_dev);
	}

	/*
	 * We will always attempt to put link into D3hot, however
	 * link down may have happened due to error fatal, so
	 * ignoring the return code
	 */
		pci_set_power_state(pci_dev, PCI_D3hot);

		/* release the resources */
	msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0);
		msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev,
				    NULL, 0);
		mhi_arch_set_bus_request(mhi_cntrl, 0);
		break;
	case MHI_FAST_LINK_OFF:
	case MHI_ACTIVE_STATE:
	case MHI_FAST_LINK_ON:/* keeping link on do nothing */
		break;
	}

	MHI_LOG("Exited\n");
exit_suspend:
	if (ret)
		msm_pcie_l1ss_timeout_enable(pci_dev);

	return 0;
	MHI_LOG("Exited with ret:%d\n", ret);

	return ret;
}

int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
static int __mhi_arch_link_resume(struct mhi_controller *mhi_cntrl)
{
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;
	struct pci_dev *pci_dev = mhi_dev->pci_dev;
	struct mhi_link_info *cur_info = &arch_info->current_link_info;
	struct mhi_link_info *updated_info = &mhi_cntrl->mhi_link_info;
	int ret;

	MHI_LOG("Entered\n");
@@ -647,6 +697,35 @@ int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
	pci_restore_state(pci_dev);
	pci_set_master(pci_dev);

	return 0;
}

int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl)
{
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;
	struct pci_dev *pci_dev = mhi_dev->pci_dev;
	struct mhi_link_info *cur_info = &arch_info->current_link_info;
	struct mhi_link_info *updated_info = &mhi_cntrl->mhi_link_info;
	int ret = 0;

	MHI_LOG("Entered\n");

	switch (mhi_dev->suspend_mode) {
	case MHI_DEFAULT_SUSPEND:
		ret = __mhi_arch_link_resume(mhi_cntrl);
		break;
	case MHI_FAST_LINK_OFF:
	case MHI_ACTIVE_STATE:
	case MHI_FAST_LINK_ON:
		break;
	}

	if (ret) {
		MHI_ERR("Link training failed, ret:%d\n", ret);
		return ret;
	}

	/* BW request from device doesn't match current link speed */
	if (cur_info->target_link_speed != updated_info->target_link_speed ||
	    cur_info->target_link_width != updated_info->target_link_width) {
@@ -655,6 +734,8 @@ int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
			*cur_info = *updated_info;
	}

	msm_pcie_l1ss_timeout_enable(pci_dev);

	MHI_LOG("Exited\n");

	return 0;
+108 −27
Original line number Diff line number Diff line
@@ -195,7 +195,43 @@ static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl)

static int mhi_runtime_suspend(struct device *dev)
{
	return -EBUSY;
	int ret = 0;
	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);

	MHI_LOG("Enter\n");

	mutex_lock(&mhi_cntrl->pm_mutex);

	if (!mhi_dev->powered_on) {
		MHI_LOG("Not fully powered, return success\n");
		mutex_unlock(&mhi_cntrl->pm_mutex);
		return 0;
	}

	ret = mhi_pm_suspend(mhi_cntrl);

	if (ret) {
		MHI_LOG("Abort due to ret:%d\n", ret);
		goto exit_runtime_suspend;
	}

	mhi_dev->suspend_mode = MHI_DEFAULT_SUSPEND;

	ret = mhi_arch_link_suspend(mhi_cntrl);

	/* failed suspending link abort mhi suspend */
	if (ret) {
		MHI_LOG("Failed to suspend link, abort suspend\n");
		mhi_pm_resume(mhi_cntrl);
		mhi_dev->suspend_mode = MHI_ACTIVE_STATE;
	}

exit_runtime_suspend:
	mutex_unlock(&mhi_cntrl->pm_mutex);
	MHI_LOG("Exited with ret:%d\n", ret);

	return ret;
}

static int mhi_runtime_idle(struct device *dev)
@@ -236,12 +272,18 @@ static int mhi_runtime_resume(struct device *dev)
	}

	/* turn on link */
	ret = mhi_arch_link_on(mhi_cntrl);
	ret = mhi_arch_link_resume(mhi_cntrl);
	if (ret)
		goto rpm_resume_exit;

	/* enter M0 state */

	/* transition to M0 state */
	if (mhi_dev->suspend_mode == MHI_DEFAULT_SUSPEND)
		ret = mhi_pm_resume(mhi_cntrl);
	else
		ret = mhi_pm_fast_resume(mhi_cntrl, MHI_FAST_LINK_ON);

	mhi_dev->suspend_mode = MHI_ACTIVE_STATE;

rpm_resume_exit:
	mutex_unlock(&mhi_cntrl->pm_mutex);
@@ -252,41 +294,81 @@ static int mhi_runtime_resume(struct device *dev)

static int mhi_system_resume(struct device *dev)
{
	int ret = 0;
	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);

	ret = mhi_runtime_resume(dev);
	if (ret) {
		MHI_ERR("Failed to resume link\n");
	} else {
		pm_runtime_set_active(dev);
		pm_runtime_enable(dev);
	}

	return ret;
	return mhi_runtime_resume(dev);
}

int mhi_system_suspend(struct device *dev)
{
	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	int ret;

	MHI_LOG("Entered\n");

	/* if rpm status still active then force suspend */
	if (!pm_runtime_status_suspended(dev)) {
		ret = mhi_runtime_suspend(dev);
	mutex_lock(&mhi_cntrl->pm_mutex);

	if (!mhi_dev->powered_on) {
		MHI_LOG("Not fully powered, return success\n");
		mutex_unlock(&mhi_cntrl->pm_mutex);
		return 0;
	}

	/*
	 * pci framework always makes a dummy vote to rpm
	 * framework to resume before calling system suspend
	 * hence usage count is minimum one
	 */
	if (atomic_read(&dev->power.usage_count) > 1) {
		/*
		 * clients have requested to keep link on, try
		 * fast suspend. No need to notify clients since
		 * we will not be turning off the pcie link
		 */
		ret = mhi_pm_fast_suspend(mhi_cntrl, false);
		mhi_dev->suspend_mode = MHI_FAST_LINK_ON;
	} else {
		/* try normal suspend */
		mhi_dev->suspend_mode = MHI_DEFAULT_SUSPEND;
		ret = mhi_pm_suspend(mhi_cntrl);

		/*
		 * normal suspend failed because we're busy, try
		 * fast suspend before aborting system suspend.
		 * this could happens if client has disabled
		 * device lpm but no active vote for PCIe from
		 * apps processor
		 */
		if (ret == -EBUSY) {
			ret = mhi_pm_fast_suspend(mhi_cntrl, true);
			mhi_dev->suspend_mode = MHI_FAST_LINK_ON;
		}
	}

	if (ret) {
			MHI_LOG("suspend failed ret:%d\n", ret);
			return ret;
		MHI_LOG("Abort due to ret:%d\n", ret);
		mhi_dev->suspend_mode = MHI_ACTIVE_STATE;
		goto exit_system_suspend;
	}

	ret = mhi_arch_link_suspend(mhi_cntrl);

	/* failed suspending link abort mhi suspend */
	if (ret) {
		MHI_LOG("Failed to suspend link, abort suspend\n");
		if (mhi_dev->suspend_mode == MHI_DEFAULT_SUSPEND)
			mhi_pm_resume(mhi_cntrl);
		else
			mhi_pm_fast_resume(mhi_cntrl, MHI_FAST_LINK_OFF);

		mhi_dev->suspend_mode = MHI_ACTIVE_STATE;
	}

	pm_runtime_set_suspended(dev);
	pm_runtime_disable(dev);
exit_system_suspend:
	mutex_unlock(&mhi_cntrl->pm_mutex);

	MHI_LOG("Exit\n");
	return 0;
	MHI_LOG("Exit with ret:%d\n", ret);

	return ret;
}

/* checks if link is down */
@@ -658,7 +740,6 @@ int mhi_pci_probe(struct pci_dev *pci_dev,
	}

	pm_runtime_mark_last_busy(&pci_dev->dev);
	pm_runtime_allow(&pci_dev->dev);

	MHI_LOG("Return successful\n");

+15 −5
Original line number Diff line number Diff line
@@ -29,8 +29,18 @@
extern const char * const mhi_ee_str[MHI_EE_MAX];
#define TO_MHI_EXEC_STR(ee) (ee >= MHI_EE_MAX ? "INVALID_EE" : mhi_ee_str[ee])

enum mhi_suspend_mode {
	MHI_ACTIVE_STATE,
	MHI_DEFAULT_SUSPEND,
	MHI_FAST_LINK_OFF,
	MHI_FAST_LINK_ON,
};

#define MHI_IS_SUSPENDED(mode) (mode)

struct mhi_dev {
	struct pci_dev *pci_dev;
	bool drv_supported;
	u32 smmu_cfg;
	int resn;
	void *arch_info;
@@ -38,6 +48,7 @@ struct mhi_dev {
	dma_addr_t iova_start;
	dma_addr_t iova_stop;
	bool lpm_disabled;
	enum mhi_suspend_mode suspend_mode;

	/* if set, soc support dynamic bw scaling */
	void (*bw_scale)(struct mhi_controller *mhi_cntrl,
@@ -55,8 +66,8 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl);
void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl);
int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl);
void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl);
int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful);
int mhi_arch_link_on(struct mhi_controller *mhi_cntrl);
int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl);
int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl);

#else

@@ -82,13 +93,12 @@ static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
{
}

static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl,
				    bool graceful)
static inline int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl)
{
	return 0;
}

static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
static inline int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl)
{
	return 0;
}