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

Commit 83f8ca45 authored by Lukasz Luba's avatar Lukasz Luba Committed by MyungJoo Ham
Browse files

PM / devfreq: add support for suspend/resume of a devfreq device



The patch prepares devfreq device for handling suspend/resume
functionality. The new fields will store needed information during this
process. Devfreq framework handles opp-suspend DT entry and there is no
need of modyfications in the drivers code. It uses atomic variables to
make sure no race condition affects the process.

Suggested-by: default avatarTobias Jakobi <tjakobi@math.uni-bielefeld.de>
Suggested-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: default avatarLukasz Luba <l.luba@partner.samsung.com>
Reviewed-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: default avatarMyungJoo Ham <myungjoo.ham@samsung.com>
parent 63314172
Loading
Loading
Loading
Loading
+41 −6
Original line number Diff line number Diff line
@@ -316,6 +316,10 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
			"Couldn't update frequency transition information.\n");

	devfreq->previous_freq = new_freq;

	if (devfreq->suspend_freq)
		devfreq->resume_freq = cur_freq;

	return err;
}

@@ -667,6 +671,9 @@ struct devfreq *devfreq_add_device(struct device *dev,
	}
	devfreq->max_freq = devfreq->scaling_max_freq;

	devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
	atomic_set(&devfreq->suspend_count, 0);

	dev_set_name(&devfreq->dev, "devfreq%d",
				atomic_inc_return(&devfreq_no));
	err = device_register(&devfreq->dev);
@@ -867,14 +874,28 @@ EXPORT_SYMBOL(devm_devfreq_remove_device);
 */
int devfreq_suspend_device(struct devfreq *devfreq)
{
	int ret;

	if (!devfreq)
		return -EINVAL;

	if (!devfreq->governor)
	if (atomic_inc_return(&devfreq->suspend_count) > 1)
		return 0;

	return devfreq->governor->event_handler(devfreq,
	if (devfreq->governor) {
		ret = devfreq->governor->event_handler(devfreq,
					DEVFREQ_GOV_SUSPEND, NULL);
		if (ret)
			return ret;
	}

	if (devfreq->suspend_freq) {
		ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0);
		if (ret)
			return ret;
	}

	return 0;
}
EXPORT_SYMBOL(devfreq_suspend_device);

@@ -888,14 +909,28 @@ EXPORT_SYMBOL(devfreq_suspend_device);
 */
int devfreq_resume_device(struct devfreq *devfreq)
{
	int ret;

	if (!devfreq)
		return -EINVAL;

	if (!devfreq->governor)
	if (atomic_dec_return(&devfreq->suspend_count) >= 1)
		return 0;

	return devfreq->governor->event_handler(devfreq,
	if (devfreq->resume_freq) {
		ret = devfreq_set_target(devfreq, devfreq->resume_freq, 0);
		if (ret)
			return ret;
	}

	if (devfreq->governor) {
		ret = devfreq->governor->event_handler(devfreq,
					DEVFREQ_GOV_RESUME, NULL);
		if (ret)
			return ret;
	}

	return 0;
}
EXPORT_SYMBOL(devfreq_resume_device);

+7 −0
Original line number Diff line number Diff line
@@ -131,6 +131,9 @@ struct devfreq_dev_profile {
 * @scaling_min_freq:	Limit minimum frequency requested by OPP interface
 * @scaling_max_freq:	Limit maximum frequency requested by OPP interface
 * @stop_polling:	 devfreq polling status of a device.
 * @suspend_freq:	 frequency of a device set during suspend phase.
 * @resume_freq:	 frequency of a device set in resume phase.
 * @suspend_count:	 suspend requests counter for a device.
 * @total_trans:	Number of devfreq transitions
 * @trans_table:	Statistics of devfreq transitions
 * @time_in_state:	Statistics of devfreq states
@@ -167,6 +170,10 @@ struct devfreq {
	unsigned long scaling_max_freq;
	bool stop_polling;

	unsigned long suspend_freq;
	unsigned long resume_freq;
	atomic_t suspend_count;

	/* information for device frequency transition */
	unsigned int total_trans;
	unsigned int *trans_table;