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

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

Merge "mhi: cntrl: add support for advanced power management featues"

parents 2cea1f57 dabc6381
Loading
Loading
Loading
Loading
+330 −70
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/

#include <asm/dma-iommu.h>
#include <linux/async.h>
@@ -28,12 +28,12 @@ struct arch_info {
	struct pci_saved_state *pcie_state;
	struct pci_saved_state *ref_pcie_state;
	struct dma_iommu_mapping *mapping;
};

struct mhi_bl_info {
	struct mhi_device *mhi_device;
	async_cookie_t cookie;
	void *ipc_log;
	void *boot_ipc_log;
	struct mhi_device *boot_dev;
	struct mhi_link_info current_link_info;
	struct work_struct bw_scale_work;
	bool drv_connected;
};

/* ipc log markings */
@@ -73,8 +73,10 @@ static void mhi_arch_pci_link_state_cb(struct msm_pcie_notify *notify)
	struct mhi_controller *mhi_cntrl = notify->data;
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct pci_dev *pci_dev = mhi_dev->pci_dev;
	struct arch_info *arch_info = mhi_dev->arch_info;

	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 */
@@ -82,6 +84,42 @@ 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;
	case MSM_PCIE_EVENT_DRV_CONNECT:
		/* drv is connected we can suspend now */
		MHI_LOG("Received MSM_PCIE_EVENT_DRV_CONNECT signal\n");

		arch_info->drv_connected = true;

		pm_runtime_allow(&pci_dev->dev);

		mutex_lock(&mhi_cntrl->pm_mutex);

		/* if we're in amss attempt a suspend */
		if (mhi_dev->powered_on && mhi_cntrl->ee == MHI_EE_AMSS) {
			pm_runtime_mark_last_busy(&pci_dev->dev);
			pm_request_autosuspend(&pci_dev->dev);
		}
		mutex_unlock(&mhi_cntrl->pm_mutex);
		break;
	case MSM_PCIE_EVENT_DRV_DISCONNECT:
		MHI_LOG("Received MSM_PCIE_EVENT_DRV_DISCONNECT signal\n");

		/*
		 * if link suspended bring it out of suspend and disable runtime
		 * suspend
		 */
		arch_info->drv_connected = false;
		pm_runtime_forbid(&pci_dev->dev);
		break;
	default:
		MHI_ERR("Unhandled event 0x%x\n", notify->event);
	}
}

@@ -125,10 +163,27 @@ static int mhi_arch_esoc_ops_power_on(void *priv, bool mdm_state)
	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");
}

void mhi_arch_esoc_ops_power_off(void *priv, bool mdm_state)
{
	struct mhi_controller *mhi_cntrl = priv;
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;

	MHI_LOG("Enter: mdm_crashed:%d\n", mdm_state);
	if (!mhi_dev->powered_on) {
@@ -141,22 +196,28 @@ void mhi_arch_esoc_ops_power_off(void *priv, bool mdm_state)

	/* 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);

	mhi_arch_iommu_deinit(mhi_cntrl);
	mhi_arch_pcie_deinit(mhi_cntrl);
	mhi_dev->powered_on = false;
}

static void mhi_bl_dl_cb(struct mhi_device *mhi_dev,
static void mhi_bl_dl_cb(struct mhi_device *mhi_device,
			 struct mhi_result *mhi_result)
{
	struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev);
	struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl;
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;
	char *buf = mhi_result->buf_addr;

	/* force a null at last character */
	buf[mhi_result->bytes_xferd - 1] = 0;

	ipc_log_string(mhi_bl_info->ipc_log, "%s %s", DLOG, buf);
	ipc_log_string(arch_info->boot_ipc_log, "%s %s", DLOG, buf);
}

static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev,
@@ -164,21 +225,23 @@ static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev,
{
}

static void mhi_bl_remove(struct mhi_device *mhi_dev)
static void mhi_bl_remove(struct mhi_device *mhi_device)
{
	struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev);

	ipc_log_string(mhi_bl_info->ipc_log, HLOG "Received Remove notif.\n");
	struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl;
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;

	/* wait for boot monitor to exit */
	async_synchronize_cookie(mhi_bl_info->cookie + 1);
	arch_info->boot_dev = NULL;
	ipc_log_string(arch_info->boot_ipc_log,
		       HLOG "Received Remove notif.\n");
}

static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie)
static void mhi_boot_monitor(void *data, async_cookie_t cookie)
{
	struct mhi_bl_info *mhi_bl_info = data;
	struct mhi_device *mhi_device = mhi_bl_info->mhi_device;
	struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl;
	struct mhi_controller *mhi_cntrl = data;
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;
	struct mhi_device *boot_dev;
	/* 15 sec timeout for booting device */
	const u32 timeout = msecs_to_jiffies(15000);

@@ -187,46 +250,118 @@ static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie)
			   || mhi_cntrl->ee == MHI_EE_DISABLE_TRANSITION,
			   timeout);

	ipc_log_string(arch_info->boot_ipc_log, HLOG "Device current ee = %s\n",
		       TO_MHI_EXEC_STR(mhi_cntrl->ee));

	/* if we successfully booted to amss disable boot log channel */
	if (mhi_cntrl->ee == MHI_EE_AMSS) {
		ipc_log_string(mhi_bl_info->ipc_log, HLOG
			       "Device successfully booted to mission mode\n");
		boot_dev = arch_info->boot_dev;
		if (boot_dev)
			mhi_unprepare_from_transfer(boot_dev);

		mhi_unprepare_from_transfer(mhi_device);
	} else {
		ipc_log_string(mhi_bl_info->ipc_log, HLOG
			       "Device failed to boot to mission mode, ee = %s\n",
			       TO_MHI_EXEC_STR(mhi_cntrl->ee));
		/* enable link inactivity timer to start auto suspend */
		msm_pcie_l1ss_timeout_enable(mhi_dev->pci_dev);

		if (!mhi_dev->drv_supported || arch_info->drv_connected)
			pm_runtime_allow(&mhi_dev->pci_dev->dev);
	}
}

static int mhi_bl_probe(struct mhi_device *mhi_dev,
			const struct mhi_device_id *id)
int mhi_arch_power_up(struct mhi_controller *mhi_cntrl)
{
	char node_name[32];
	struct mhi_bl_info *mhi_bl_info;
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;

	mhi_bl_info = devm_kzalloc(&mhi_dev->dev, sizeof(*mhi_bl_info),
				   GFP_KERNEL);
	if (!mhi_bl_info)
		return -ENOMEM;
	/* start a boot monitor */
	arch_info->cookie = async_schedule(mhi_boot_monitor, mhi_cntrl);

	snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u",
		 mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot);
	return 0;
}

	mhi_bl_info->ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES,
						      node_name, 0);
	if (!mhi_bl_info->ipc_log)
		return -EINVAL;
static  int mhi_arch_pcie_scale_bw(struct mhi_controller *mhi_cntrl,
				   struct pci_dev *pci_dev,
				   struct mhi_link_info *link_info)
{
	int ret, scale;

	mhi_bl_info->mhi_device = mhi_dev;
	mhi_device_set_devdata(mhi_dev, mhi_bl_info);
	ret = msm_pcie_set_link_bandwidth(pci_dev, link_info->target_link_speed,
					  link_info->target_link_width);
	if (ret)
		return ret;

	ipc_log_string(mhi_bl_info->ipc_log, HLOG
		       "Entered SBL, Session ID:0x%x\n",
		       mhi_dev->mhi_cntrl->session_id);
	/* if we switch to low bw release bus scale voting */
	scale = !(link_info->target_link_speed == PCI_EXP_LNKSTA_CLS_2_5GB);
	mhi_arch_set_bus_request(mhi_cntrl, scale);

	/* start a thread to monitor entering mission mode */
	mhi_bl_info->cookie = async_schedule(mhi_bl_boot_monitor, mhi_bl_info);
	MHI_VERB("bw changed to speed:0x%x width:0x%x bus_scale:%d\n",
		 link_info->target_link_speed, link_info->target_link_width,
		 scale);

	return 0;
}

static void mhi_arch_pcie_bw_scale_work(struct work_struct *work)
{
	struct arch_info *arch_info = container_of(work,
						   struct arch_info,
						   bw_scale_work);
	struct mhi_dev *mhi_dev = arch_info->mhi_dev;
	struct pci_dev *pci_dev = mhi_dev->pci_dev;
	struct device *dev = &pci_dev->dev;
	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
	struct mhi_link_info mhi_link_info;
	struct mhi_link_info *cur_info = &arch_info->current_link_info;
	int ret;

	mutex_lock(&mhi_cntrl->pm_mutex);
	if (!mhi_dev->powered_on || MHI_IS_SUSPENDED(mhi_dev->suspend_mode))
		goto exit_work;

	/* copy the latest speed change */
	write_lock_irq(&mhi_cntrl->pm_lock);
	mhi_link_info = mhi_cntrl->mhi_link_info;
	write_unlock_irq(&mhi_cntrl->pm_lock);

	/* link is already set to current settings */
	if (cur_info->target_link_speed == mhi_link_info.target_link_speed &&
	    cur_info->target_link_width == mhi_link_info.target_link_width)
		goto exit_work;

	ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, &mhi_link_info);
	if (ret)
		goto exit_work;

	*cur_info = mhi_link_info;

exit_work:
	mutex_unlock(&mhi_cntrl->pm_mutex);
}

static void mhi_arch_pcie_bw_scale_cb(struct mhi_controller *mhi_cntrl,
				      struct mhi_dev *mhi_dev)
{
	struct arch_info *arch_info = mhi_dev->arch_info;

	schedule_work(&arch_info->bw_scale_work);
}

static int mhi_bl_probe(struct mhi_device *mhi_device,
			const struct mhi_device_id *id)
{
	char node_name[32];
	struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl;
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;

	snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u",
		 mhi_device->dev_id, mhi_device->domain, mhi_device->bus,
		 mhi_device->slot);

	arch_info->boot_dev = mhi_device;
	arch_info->boot_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES,
							 node_name, 0);
	ipc_log_string(arch_info->boot_ipc_log, HLOG
		       "Entered SBL, Session ID:0x%x\n", mhi_cntrl->session_id);

	return 0;
}
@@ -257,6 +392,8 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)

	if (!arch_info) {
		struct msm_pcie_register_event *reg_event;
		struct pci_dev *root_port;
		struct device_node *root_ofnode;

		arch_info = devm_kzalloc(&mhi_dev->pci_dev->dev,
					 sizeof(*arch_info), GFP_KERNEL);
@@ -264,6 +401,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,
@@ -283,9 +421,23 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
				return -EINVAL;
		}

		/* check if root-complex support DRV */
		root_port = pci_find_pcie_root_port(mhi_dev->pci_dev);
		root_ofnode = root_port->dev.of_node;
		if (root_ofnode->parent)
			mhi_dev->drv_supported =
				of_property_read_bool(root_ofnode->parent,
						      "qcom,drv-supported");

		/* 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;

		/* if drv supported register for drv connection events */
		if (mhi_dev->drv_supported)
			reg_event->events |= MSM_PCIE_EVENT_DRV_CONNECT |
				MSM_PCIE_EVENT_DRV_DISCONNECT;
		reg_event->user = mhi_dev->pci_dev;
		reg_event->callback = mhi_arch_pci_link_state_cb;
		reg_event->notify.data = mhi_cntrl;
@@ -318,6 +470,17 @@ 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);
		mhi_dev->bw_scale = mhi_arch_pcie_bw_scale_cb;

		mhi_driver_register(&mhi_bl_driver);
	}
@@ -459,56 +622,111 @@ 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)
static int mhi_arch_drv_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;
	struct mhi_link_info link_info, *cur_link_info;
	bool bw_switched = false;
	int ret;

	cur_link_info = &arch_info->current_link_info;
	/* if link is not in gen 1 we need to switch to gen 1 */
	if (cur_link_info->target_link_speed != PCI_EXP_LNKSTA_CLS_2_5GB) {
		link_info.target_link_speed = PCI_EXP_LNKSTA_CLS_2_5GB;
		link_info.target_link_width = cur_link_info->target_link_width;
		ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, &link_info);
		if (ret) {
			MHI_ERR("Failed to switch Gen1 speed\n");
			return -EBUSY;
		}

		bw_switched = true;
	}

	/* do a drv hand off */
	ret = msm_pcie_pm_control(MSM_PCIE_DRV_SUSPEND, mhi_cntrl->bus,
				  pci_dev, NULL, 0);

	/*
	 * we failed to suspend and scaled down pcie bw.. need to scale up again
	 */
	if (ret && bw_switched) {
		mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, cur_link_info);
		return ret;
	}

	if (bw_switched)
		*cur_link_info = link_info;

	return ret;
}

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 = 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:
		ret = mhi_arch_drv_suspend(mhi_cntrl);
		break;
	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;
	int ret;

	MHI_LOG("Entered\n");

	/* request resources and establish link trainning */
	/* request bus scale voting if we're on Gen 2 or higher speed */
	if (cur_info->target_link_speed != PCI_EXP_LNKSTA_CLS_2_5GB) {
		ret = mhi_arch_set_bus_request(mhi_cntrl, 1);
		if (ret)
			MHI_LOG("Could not set bus frequency, ret:%d\n", ret);
	}

	ret = msm_pcie_pm_control(MSM_PCIE_RESUME, mhi_cntrl->bus, pci_dev,
				  NULL, 0);
@@ -536,6 +754,48 @@ 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:
		ret = msm_pcie_pm_control(MSM_PCIE_RESUME, mhi_cntrl->bus,
					  pci_dev, NULL, 0);
		break;
	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) {
		ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, updated_info);
		if (!ret)
			*cur_info = *updated_info;
	}

	msm_pcie_l1ss_timeout_enable(pci_dev);

	MHI_LOG("Exited\n");

	return 0;
+123 −32
Original line number Diff line number Diff line
@@ -200,15 +200,39 @@ static int mhi_runtime_suspend(struct device *dev)
		return 0;
	}

	/* if drv is supported we will always go into drv */
	if (mhi_dev->drv_supported) {
		ret = mhi_pm_fast_suspend(mhi_cntrl, true);
		mhi_dev->suspend_mode = MHI_FAST_LINK_OFF;
	} else {
		ret = mhi_pm_suspend(mhi_cntrl);
		mhi_dev->suspend_mode = MHI_DEFAULT_SUSPEND;

		/* regular suspend failed, probably a client has a vote */
		if (ret == -EBUSY) {
			ret = mhi_pm_fast_suspend(mhi_cntrl, false);
			mhi_dev->suspend_mode = MHI_FAST_LINK_ON;
		}
	}

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

	ret = mhi_arch_link_off(mhi_cntrl, true);
	if (ret)
		MHI_ERR("Failed to Turn off link ret:%d\n", ret);
	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_dev->suspend_mode == MHI_FAST_LINK_OFF);

		mhi_dev->suspend_mode = MHI_ACTIVE_STATE;
	}

exit_runtime_suspend:
	mutex_unlock(&mhi_cntrl->pm_mutex);
@@ -255,12 +279,19 @@ 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_dev->suspend_mode == MHI_FAST_LINK_OFF);

	mhi_dev->suspend_mode = MHI_ACTIVE_STATE;

rpm_resume_exit:
	mutex_unlock(&mhi_cntrl->pm_mutex);
@@ -271,41 +302,87 @@ 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 {
		/* if drv enable always do fast suspend */
		if (mhi_dev->drv_supported) {
			ret = mhi_pm_fast_suspend(mhi_cntrl, true);
			mhi_dev->suspend_mode = MHI_FAST_LINK_OFF;
		} 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);
		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_dev->suspend_mode == 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 with ret:%d\n", ret);

	MHI_LOG("Exit\n");
	return 0;
	return ret;
}

/* checks if link is down */
@@ -409,6 +486,13 @@ static int mhi_qcom_power_up(struct mhi_controller *mhi_cntrl)
			return -EIO;
	}

	/* when coming out of SSR, initial ee state is not valid */
	mhi_cntrl->ee = 0;

	ret = mhi_arch_power_up(mhi_cntrl);
	if (ret)
		return ret;

	ret = mhi_async_power_up(mhi_cntrl);

	/* power up create the dentry */
@@ -445,10 +529,18 @@ static void mhi_status_cb(struct mhi_controller *mhi_cntrl,
	struct mhi_dev *mhi_dev = priv;
	struct device *dev = &mhi_dev->pci_dev->dev;

	if (reason == MHI_CB_IDLE) {
		MHI_LOG("Schedule runtime suspend 1\n");
	switch (reason) {
	case MHI_CB_IDLE:
		MHI_LOG("Schedule runtime suspend\n");
		pm_runtime_mark_last_busy(dev);
		pm_request_autosuspend(dev);
		break;
	case MHI_CB_BW_REQ:
		if (mhi_dev->bw_scale)
			mhi_dev->bw_scale(mhi_cntrl, mhi_dev);
		break;
	default:
		MHI_ERR("Unhandled cb:0x%x\n", reason);
	}
}

@@ -660,7 +752,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");

+26 −6
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/

#ifndef _MHI_QCOM_
#define _MHI_QCOM_
@@ -21,8 +21,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;
@@ -30,6 +40,11 @@ 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,
			 struct mhi_dev *mhi_dev);
};

void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
@@ -38,12 +53,13 @@ int mhi_pci_probe(struct pci_dev *pci_dev,

#ifdef CONFIG_ARCH_QCOM

int mhi_arch_power_up(struct mhi_controller *mhi_cntrl);
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

@@ -69,13 +85,17 @@ 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_resume(struct mhi_controller *mhi_cntrl)
{
	return 0;
}

static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
static inline int mhi_arch_power_up(struct mhi_controller *mhi_cntrl)
{
	return 0;
}
+107 −8

File changed.

Preview size limit exceeded, changes collapsed.

+7 −1
Original line number Diff line number Diff line
@@ -320,6 +320,8 @@ enum mhi_cmd_type {
#define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr)
#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr)
#define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF)
#define MHI_TRE_GET_EV_LINKSPEED(tre) (((tre)->dword[1] >> 24) & 0xFF)
#define MHI_TRE_GET_EV_LINKWIDTH(tre) ((tre)->dword[0] & 0xFF)

/* transfer descriptor macros */
#define MHI_TRE_DATA_PTR(ptr) (ptr)
@@ -352,6 +354,7 @@ enum MHI_PKT_TYPE {
	MHI_PKT_TYPE_RSC_TX_EVENT = 0x28,
	MHI_PKT_TYPE_EE_EVENT = 0x40,
	MHI_PKT_TYPE_TSYNC_EVENT = 0x48,
	MHI_PKT_TYPE_BW_REQ_EVENT = 0x50,
	MHI_PKT_TYPE_STALE_EVENT, /* internal event */
};

@@ -451,7 +454,8 @@ enum MHI_PM_STATE {
		MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR)))
#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT)
#define MHI_DB_ACCESS_VALID(pm_state) (pm_state & MHI_PM_M0)
#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & \
					mhi_cntrl->db_access)
#define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \
						MHI_PM_M2 | MHI_PM_M3_EXIT))
#define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2)
@@ -738,6 +742,8 @@ int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, u32 capability,
int mhi_init_timesync(struct mhi_controller *mhi_cntrl);
int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl);
int mhi_create_vote_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_vote_sysfs(struct mhi_controller *mhi_cntrl);

/* memory allocation methods */
static inline void *mhi_alloc_coherent(struct mhi_controller *mhi_cntrl,
Loading