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

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

mhi: core: allow clients to independently disable bus or device lpm



Provide clients ability disable low power modes for link and/or
external modem low power modes.

CRs-Fixed: 2418347
Change-Id: I45ad7f43c6bea75ff65f883f9ccda9a0c11fa2aa
Signed-off-by: default avatarSujeev Dias <sdias@codeaurora.org>
parent 9df48dd6
Loading
Loading
Loading
Loading
+100 −8
Original line number Diff line number Diff line
@@ -70,6 +70,95 @@ const char *to_mhi_pm_state_str(enum MHI_PM_STATE state)
	return mhi_pm_state_str[index];
}

static ssize_t bus_vote_show(struct device *dev,
			     struct device_attribute *attr,
			     char *buf)
{
	struct mhi_device *mhi_dev = to_mhi_device(dev);

	return snprintf(buf, PAGE_SIZE, "%d\n",
			atomic_read(&mhi_dev->bus_vote));
}

static ssize_t bus_vote_store(struct device *dev,
			      struct device_attribute *attr,
			      const char *buf,
			      size_t count)
{
	struct mhi_device *mhi_dev = to_mhi_device(dev);
	int ret = -EINVAL;

	if (sysfs_streq(buf, "get")) {
		ret = mhi_device_get_sync(mhi_dev, MHI_VOTE_BUS);
	} else if (sysfs_streq(buf, "put")) {
		mhi_device_put(mhi_dev, MHI_VOTE_BUS);
		ret = 0;
	}

	return ret ? ret : count;
}
static DEVICE_ATTR_RW(bus_vote);

static ssize_t device_vote_show(struct device *dev,
				struct device_attribute *attr,
				char *buf)
{
	struct mhi_device *mhi_dev = to_mhi_device(dev);

	return snprintf(buf, PAGE_SIZE, "%d\n",
			atomic_read(&mhi_dev->dev_vote));
}

static ssize_t device_vote_store(struct device *dev,
				 struct device_attribute *attr,
				 const char *buf,
				 size_t count)
{
	struct mhi_device *mhi_dev = to_mhi_device(dev);
	int ret = -EINVAL;

	if (sysfs_streq(buf, "get")) {
		ret = mhi_device_get_sync(mhi_dev, MHI_VOTE_DEVICE);
	} else if (sysfs_streq(buf, "put")) {
		mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);
		ret = 0;
	}

	return ret ? ret : count;
}
static DEVICE_ATTR_RW(device_vote);

static struct attribute *mhi_vote_attrs[] = {
	&dev_attr_bus_vote.attr,
	&dev_attr_device_vote.attr,
	NULL,
};

static const struct attribute_group mhi_vote_group = {
	.attrs = mhi_vote_attrs,
};

int mhi_create_vote_sysfs(struct mhi_controller *mhi_cntrl)
{
	return sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj,
				  &mhi_vote_group);
}

void mhi_destroy_vote_sysfs(struct mhi_controller *mhi_cntrl)
{
	struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;

	sysfs_remove_group(&mhi_dev->dev.kobj, &mhi_vote_group);

	/* relinquish any pending votes for device */
	while (atomic_read(&mhi_dev->dev_vote))
		mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);

	/* remove pending votes for the bus */
	while (atomic_read(&mhi_dev->bus_vote))
		mhi_device_put(mhi_dev, MHI_VOTE_BUS);
}

/* MHI protocol require transfer ring to be aligned to ring length */
static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl,
				  struct mhi_ring *ring,
@@ -1377,7 +1466,7 @@ static int mhi_driver_probe(struct device *dev)
	int ret;

	/* bring device out of lpm */
	ret = mhi_device_get_sync(mhi_dev);
	ret = mhi_device_get_sync(mhi_dev, MHI_VOTE_DEVICE);
	if (ret)
		return ret;

@@ -1425,7 +1514,7 @@ static int mhi_driver_probe(struct device *dev)
		mhi_prepare_for_transfer(mhi_dev);

exit_probe:
	mhi_device_put(mhi_dev);
	mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);

	return ret;
}
@@ -1500,11 +1589,13 @@ static int mhi_driver_remove(struct device *dev)
	if (mhi_cntrl->tsync_dev == mhi_dev)
		mhi_cntrl->tsync_dev = NULL;

	/* relinquish any pending votes */
	read_lock_bh(&mhi_cntrl->pm_lock);
	while (atomic_read(&mhi_dev->dev_wake))
		mhi_device_put(mhi_dev);
	read_unlock_bh(&mhi_cntrl->pm_lock);
	/* relinquish any pending votes for device */
	while (atomic_read(&mhi_dev->dev_vote))
		mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);

	/* remove pending votes for the bus */
	while (atomic_read(&mhi_dev->bus_vote))
		mhi_device_put(mhi_dev, MHI_VOTE_BUS);

	return 0;
}
@@ -1548,7 +1639,8 @@ struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl)
	mhi_dev->bus = mhi_cntrl->bus;
	mhi_dev->slot = mhi_cntrl->slot;
	mhi_dev->mtu = MHI_MAX_MTU;
	atomic_set(&mhi_dev->dev_wake, 0);
	atomic_set(&mhi_dev->dev_vote, 0);
	atomic_set(&mhi_dev->bus_vote, 0);

	return mhi_dev;
}
+2 −0
Original line number Diff line number Diff line
@@ -739,6 +739,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,
+70 −18
Original line number Diff line number Diff line
@@ -483,6 +483,9 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
	/* add supported devices */
	mhi_create_devices(mhi_cntrl);

	/* setup sysfs nodes for userspace votes */
	mhi_create_vote_sysfs(mhi_cntrl);

	ret = 0;

	read_lock_bh(&mhi_cntrl->pm_lock);
@@ -581,6 +584,9 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,

	MHI_LOG("Finish resetting channels\n");

	/* remove support for userspace votes */
	mhi_destroy_vote_sysfs(mhi_cntrl);

	MHI_LOG("Waiting for all pending threads to complete\n");
	wake_up_all(&mhi_cntrl->state_event);
	flush_work(&mhi_cntrl->st_worker);
@@ -916,6 +922,7 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
	int ret;
	enum MHI_PM_STATE new_state;
	struct mhi_chan *itr, *tmp;
	struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;

	if (mhi_cntrl->pm_state == MHI_PM_DISABLE)
		return -EINVAL;
@@ -923,9 +930,10 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
		return -EIO;

	/* do a quick check to see if any pending data, then exit */
	/* do a quick check to see if any pending votes to keep us busy */
	if (atomic_read(&mhi_cntrl->dev_wake) ||
	    atomic_read(&mhi_cntrl->pending_pkts)) {
	    atomic_read(&mhi_cntrl->pending_pkts) ||
	    atomic_read(&mhi_dev->bus_vote)) {
		MHI_VERB("Busy, aborting M3\n");
		return -EBUSY;
	}
@@ -952,9 +960,13 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)

	write_lock_irq(&mhi_cntrl->pm_lock);

	/* we're asserting wake so count would be @ least 1 */
	/*
	 * Check the votes once more to see if we should abort
	 * suepend. We're asserting wake so count would be @ least 1
	 */
	if (atomic_read(&mhi_cntrl->dev_wake) > 1 ||
		atomic_read(&mhi_cntrl->pending_pkts)) {
	    atomic_read(&mhi_cntrl->pending_pkts) ||
	    atomic_read(&mhi_dev->bus_vote)) {
		MHI_VERB("Busy, aborting M3\n");
		write_unlock_irq(&mhi_cntrl->pm_lock);
		ret = -EBUSY;
@@ -1107,39 +1119,79 @@ int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl)
	return 0;
}

void mhi_device_get(struct mhi_device *mhi_dev)
void mhi_device_get(struct mhi_device *mhi_dev, int vote)
{
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;

	atomic_inc(&mhi_dev->dev_wake);
	if (vote & MHI_VOTE_DEVICE) {
		read_lock_bh(&mhi_cntrl->pm_lock);
		mhi_cntrl->wake_get(mhi_cntrl, true);
		read_unlock_bh(&mhi_cntrl->pm_lock);
		atomic_inc(&mhi_dev->dev_vote);
	}

	if (vote & MHI_VOTE_BUS) {
		mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data);
		atomic_inc(&mhi_dev->bus_vote);
	}
}
EXPORT_SYMBOL(mhi_device_get);

int mhi_device_get_sync(struct mhi_device *mhi_dev)
int mhi_device_get_sync(struct mhi_device *mhi_dev, int vote)
{
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
	int ret;

	/*
	 * regardless of any vote we will bring device out lpm and assert
	 * device wake
	 */
	ret = __mhi_device_get_sync(mhi_cntrl);
	if (!ret)
		atomic_inc(&mhi_dev->dev_wake);

	if (ret)
		return ret;

	if (vote & MHI_VOTE_DEVICE) {
		atomic_inc(&mhi_dev->dev_vote);
	} else {
		/* client did not requested device vote so de-assert dev_wake */
		read_lock_bh(&mhi_cntrl->pm_lock);
		mhi_cntrl->wake_put(mhi_cntrl, false);
		read_unlock_bh(&mhi_cntrl->pm_lock);
	}

	if (vote & MHI_VOTE_BUS) {
		mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data);
		atomic_inc(&mhi_dev->bus_vote);
	}

	return 0;
}
EXPORT_SYMBOL(mhi_device_get_sync);

void mhi_device_put(struct mhi_device *mhi_dev)
void mhi_device_put(struct mhi_device *mhi_dev, int vote)
{
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;

	atomic_dec(&mhi_dev->dev_wake);
	if (vote & MHI_VOTE_DEVICE) {
		atomic_dec(&mhi_dev->dev_vote);
		read_lock_bh(&mhi_cntrl->pm_lock);
		mhi_cntrl->wake_put(mhi_cntrl, false);
		read_unlock_bh(&mhi_cntrl->pm_lock);
	}

	if (vote & MHI_VOTE_BUS) {
		atomic_dec(&mhi_dev->bus_vote);
		mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data);

		/*
		 * if counts reach 0, clients release all votes
		 * send idle cb to to attempt suspend
		 */
		if (!atomic_read(&mhi_dev->bus_vote))
			mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
					     MHI_CB_IDLE);
	}
}
EXPORT_SYMBOL(mhi_device_put);

int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl)
+2 −2
Original line number Diff line number Diff line
@@ -456,12 +456,12 @@ static int mhi_netdev_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
			/* Request to enable LPM */
			MSG_VERB("Enable MHI LPM");
			mhi_netdev->wake--;
			mhi_device_put(mhi_dev);
			mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);
		} else if (!ext_cmd.u.data && !mhi_netdev->wake) {
			/* Request to disable LPM */
			MSG_VERB("Disable MHI LPM");
			mhi_netdev->wake++;
			mhi_device_get(mhi_dev);
			mhi_device_get(mhi_dev, MHI_VOTE_DEVICE);
		}
		break;
	default:
+3 −3
Original line number Diff line number Diff line
@@ -636,7 +636,7 @@ struct ipa_mhi_clk_vote_resp_msg_v01
	 * executed from mhi context.
	 */
	if (vote) {
		ret = mhi_device_get_sync(imp_ctx->md.mhi_dev);
		ret = mhi_device_get_sync(imp_ctx->md.mhi_dev, MHI_VOTE_BUS);
		if (ret) {
			IMP_ERR("mhi_sync_get failed %d\n", ret);
			resp->resp.result = IPA_QMI_RESULT_FAILURE_V01;
@@ -649,7 +649,7 @@ struct ipa_mhi_clk_vote_resp_msg_v01
			return resp;
		}
	} else {
		mhi_device_put(imp_ctx->md.mhi_dev);
		mhi_device_put(imp_ctx->md.mhi_dev, MHI_VOTE_BUS);
	}

	mutex_lock(&imp_ctx->mutex);
@@ -709,7 +709,7 @@ static void imp_mhi_shutdown(void)
				false);
		}
		if (imp_ctx->lpm_disabled) {
			mhi_device_put(imp_ctx->md.mhi_dev);
			mhi_device_put(imp_ctx->md.mhi_dev, MHI_VOTE_BUS);
			imp_ctx->lpm_disabled = false;
		}

Loading