Patchwork [v3,2/5] devfreq: add support for suspend/resume of a devfreq device

login
register
mail settings
Submitter Lukasz Luba
Date Dec. 5, 2018, 11:05 a.m.
Message ID <1544007956-28889-3-git-send-email-l.luba@partner.samsung.com>
Download mbox | patch
Permalink /patch/672971/
State New
Headers show

Comments

Lukasz Luba - Dec. 5, 2018, 11:05 a.m.
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: Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
Suggested-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
---
 drivers/devfreq/devfreq.c | 47 +++++++++++++++++++++++++++++++++++++++++------
 include/linux/devfreq.h   |  7 +++++++
 2 files changed, 48 insertions(+), 6 deletions(-)
Chanwoo Choi - Dec. 6, 2018, 1:17 a.m.
Hi Lukasz,

On 2018년 12월 05일 20:05, Lukasz Luba wrote:
> 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: Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
> Suggested-by: Chanwoo Choi <cw00.choi@samsung.com>
> Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
> ---
>  drivers/devfreq/devfreq.c | 47 +++++++++++++++++++++++++++++++++++++++++------
>  include/linux/devfreq.h   |  7 +++++++
>  2 files changed, 48 insertions(+), 6 deletions(-)

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>

> 
> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
> index a9fd61b..46517b6 100644
> --- a/drivers/devfreq/devfreq.c
> +++ b/drivers/devfreq/devfreq.c
> @@ -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,
> -				DEVFREQ_GOV_SUSPEND, NULL);
> +	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,
> -				DEVFREQ_GOV_RESUME, NULL);
> +	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);
>  
> diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
> index e4963b0..d985199 100644
> --- a/include/linux/devfreq.h
> +++ b/include/linux/devfreq.h
> @@ -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;
>
Lukasz Luba - Dec. 6, 2018, 10:13 a.m.
Hi Chanwoo,

On 12/6/18 2:17 AM, Chanwoo Choi wrote:
> Hi Lukasz,
> 
> On 2018년 12월 05일 20:05, Lukasz Luba wrote:
>> 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: Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
>> Suggested-by: Chanwoo Choi <cw00.choi@samsung.com>
>> Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
>> ---
>>   drivers/devfreq/devfreq.c | 47 +++++++++++++++++++++++++++++++++++++++++------
>>   include/linux/devfreq.h   |  7 +++++++
>>   2 files changed, 48 insertions(+), 6 deletions(-)
> 
> Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Thank you for the review and comments for the whole patch series.

Regards,
Lukasz

> 
>>
>> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
>> index a9fd61b..46517b6 100644
>> --- a/drivers/devfreq/devfreq.c
>> +++ b/drivers/devfreq/devfreq.c
>> @@ -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,
>> -				DEVFREQ_GOV_SUSPEND, NULL);
>> +	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,
>> -				DEVFREQ_GOV_RESUME, NULL);
>> +	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);
>>   
>> diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
>> index e4963b0..d985199 100644
>> --- a/include/linux/devfreq.h
>> +++ b/include/linux/devfreq.h
>> @@ -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;
>>
> 
>
MyungJoo Ham - Dec. 11, 2018, 1:43 a.m.
> Hi Lukasz,
> 
> On 2018년 12월 05일 20:05, Lukasz Luba wrote:

> > 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: Tobias Jakobi <tjakobi@math.uni-bielefeld.de>

> > Suggested-by: Chanwoo Choi <cw00.choi@samsung.com>

> > Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>

> > ---

> >  drivers/devfreq/devfreq.c | 47 +++++++++++++++++++++++++++++++++++++++++------

> >  include/linux/devfreq.h   |  7 +++++++

> >  2 files changed, 48 insertions(+), 6 deletions(-)

> 

> Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>

> 


Looks goot do me as well.

Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>



Anyway, for the sake of curiosity...

Having suspend-frequency is usually required when
the frequency configuration is reset with suspend-resume
as older Exynos's CPU did (I don't know whether it still does).

Does GPU do this as well?
(memory-bus won't do this because they are kept turned on during suspend)


Cheers,
MyungJoo
Lukasz Luba - Dec. 11, 2018, 8:26 a.m.
Hi MyungJoo,

Thank you for taking the patch set.
Please check my response bellow.

On 12/11/18 2:43 AM, MyungJoo Ham wrote:
>> Hi Lukasz,
>>
>> On 2018년 12월 05일 20:05, Lukasz Luba wrote:
>>> 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: Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
>>> Suggested-by: Chanwoo Choi <cw00.choi@samsung.com>
>>> Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
>>> ---
>>>   drivers/devfreq/devfreq.c | 47 +++++++++++++++++++++++++++++++++++++++++------
>>>   include/linux/devfreq.h   |  7 +++++++
>>>   2 files changed, 48 insertions(+), 6 deletions(-)
>>
>> Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
>>
> 
> Looks goot do me as well.
> 
> Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> 
> 
> Anyway, for the sake of curiosity...
> 
> Having suspend-frequency is usually required when
> the frequency configuration is reset with suspend-resume
> as older Exynos's CPU did (I don't know whether it still does).
You are right, some Exynos SoCs like 4412 needs the proper configuration.
> 
> Does GPU do this as well?
> (memory-bus won't do this because they are kept turned on during suspend)
I haven't seen GPU failures.
The CPU resume process can fail when some buses ('leftbus' (data 
bus/peripheral bus), 'rightbus', 'memory-bus')
are not operating with needed frequency.

Here is the log showing CPU1,2,3 booting failure:
-------------------------------------------------------------------
root@target:~# echo mem > /sys/power/state
[   43.432066] PM: suspend entry (deep)
[   43.481742] PM: Syncing filesystems ... done.
[   43.553433] Freezing user space processes ... (elapsed 0.002 seconds) 
done.
[   43.556687] OOM killer disabled.
[   43.556694] Freezing remaining freezable tasks ... (elapsed 0.001 
seconds) done.
[   43.558305] printk: Suspending console(s) (use no_console_suspend to 
debug)

[   43.718007] dwc2 12480000.hsotg: suspending usb gadget g_ether
[   43.718229] dwc2 12480000.hsotg: dwc2_hsotg_ep_disable: called for ep0
[   43.718260] dwc2 12480000.hsotg: dwc2_hsotg_ep_disable: called for ep0
[   43.722021] dwc2 12480000.hsotg: dwc2_hsotg_ep_disable: called for ep0
[   43.722038] dwc2 12480000.hsotg: dwc2_hsotg_ep_disable: called for ep0
[   43.781714] wake enabled for irq 134
[   43.781822] CAM_ISP_CORE_1.2V: No configuration
[   43.781918] VMEM_VDDF_3.0V: No configuration
[   43.781952] VCC_SUB_2.0V: No configuration
[   43.781986] VCC_SUB_1.35V: No configuration
[   43.782018] VMEM_1.2V_AP: No configuration
[   43.787139] MOTOR_VCC_3.0V: No configuration
[   43.787176] LCD_VCC_3.3V: No configuration
[   43.787209] TSP_VDD_1.8V: No configuration
[   43.787242] TSP_AVDD_3.3V: No configuration
[   43.787273] VMEM_VDD_2.8V: No configuration
[   43.787305] VTF_2.8V: No configuration
[   43.787422] VDDQ_PRE_1.8V: No configuration
[   43.787456] VT_CAM_1.8V: No configuration
[   43.787488] CAM_ISP_SEN_IO_1.8V: No configuration
[   43.787521] CAM_SENSOR_CORE_1.2V: No configuration
[   43.789160] NFC_AVDD_1.8V: No configuration
[   43.792571] CAM_ISP_MIPI_1.2V: No configuration
[   43.794209] VCC_1.8V_IO: No configuration
[   43.794244] VCC_2.8V_AP: No configuration
[   43.794279] VCC_1.8V_AP: No configuration
[   43.794331] VALIVE_1.0V_AP: No configuration
[   43.805821] wake enabled for irq 138
[   43.806368] wake enabled for irq 160
[   43.806382] wake enabled for irq 161
[   43.824045] samsung-pinctrl 11000000.pinctrl: Setting external wakeup 
interrupt mask: 0xff77ff7d
[   43.841162] Disabling non-boot CPUs ...
[   43.863016] Enabling non-boot CPUs ...
[   43.864749] ------------[ cut here ]------------
[   43.864779] WARNING: CPU: 1 PID: 0 at 
./arch/arm/include/asm/proc-fns.h:124 secondary_start_kernel+0x20c/0x268
[   43.864788] Modules linked in:
[   43.864805] CPU: 1 PID: 0 Comm: swapper/1 Not tainted 
4.20.0-rc2-next-20181114-00007-g5506a9cd6bd7 #1042
[   43.864813] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[   43.864835] [<c011261c>] (unwind_backtrace) from [<c010e150>] 
(show_stack+0x10/0x14)
[   43.864852] [<c010e150>] (show_stack) from [<c0a4b0a0>] 
(dump_stack+0x98/0xc4)
[   43.864870] [<c0a4b0a0>] (dump_stack) from [<c01270a4>] 
(__warn+0x10c/0x124)
[   43.864884] [<c01270a4>] (__warn) from [<c01271d0>] 
(warn_slowpath_null+0x40/0x48)
[   43.864899] [<c01271d0>] (warn_slowpath_null) from [<c0110ee0>] 
(secondary_start_kernel+0x20c/0x268)
[   43.864913] [<c0110ee0>] (secondary_start_kernel) from [<401027ac>] 
(0x401027ac)
[   43.864923] irq event stamp: 982984
[   43.864942] hardirqs last  enabled at (982983): [<c0a6cc6c>] 
_raw_spin_unlock_irqrestore+0x6c/0x74
[   43.864954] hardirqs last disabled at (982984): [<c0110c68>] 
arch_cpu_idle_dead+0x18/0x84
[   43.864969] softirqs last  enabled at (982964): [<c012f0e0>] 
irq_enter+0x78/0x80
[   43.864980] softirqs last disabled at (982963): [<c012f0cc>] 
irq_enter+0x64/0x80
[   43.864989] ---[ end trace 6401b9331547e26f ]---
[   43.864996] ------------[ cut here ]------------
[   43.865009] WARNING: CPU: 1 PID: 0 at 
./arch/arm/include/asm/proc-fns.h:126 secondary_start_kernel+0x244/0x268
[   43.865016] Modules linked in:
[   43.865030] CPU: 1 PID: 0 Comm: swapper/1 Tainted: G        W 
4.20.0-rc2-next-20181114-00007-g5506a9cd6bd7 #1042
[   43.865039] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[   43.865054] [<c011261c>] (unwind_backtrace) from [<c010e150>] 
(show_stack+0x10/0x14)
[   43.865068] [<c010e150>] (show_stack) from [<c0a4b0a0>] 
(dump_stack+0x98/0xc4)
[   43.865081] [<c0a4b0a0>] (dump_stack) from [<c01270a4>] 
(__warn+0x10c/0x124)
[   43.865095] [<c01270a4>] (__warn) from [<c01271d0>] 
(warn_slowpath_null+0x40/0x48)
[   43.865109] [<c01271d0>] (warn_slowpath_null) from [<c0110f18>] 
(secondary_start_kernel+0x244/0x268)
[   43.865122] [<c0110f18>] (secondary_start_kernel) from [<401027ac>] 
(0x401027ac)
[   43.865131] irq event stamp: 982984
[   43.865144] hardirqs last  enabled at (982983): [<c0a6cc6c>] 
_raw_spin_unlock_irqrestore+0x6c/0x74
[   43.865156] hardirqs last disabled at (982984): [<c0110c68>] 
arch_cpu_idle_dead+0x18/0x84
[   43.865168] softirqs last  enabled at (982964): [<c012f0e0>] 
irq_enter+0x78/0x80
[   43.865179] softirqs last disabled at (982963): [<c012f0cc>] 
irq_enter+0x64/0x80
[   43.865187] ---[ end trace 6401b9331547e270 ]---
[   44.856140] CPU1: failed to boot: -110
[   44.856956] Error taking CPU1 up: -110
[   45.856655] CPU2: failed to boot: -110
[   45.857770] Error taking CPU2 up: -110
[   46.856059] CPU3: failed to boot: -110
[   46.856721] Error taking CPU3 up: -110
-------------------------------------------------------------------

With the patch set, the CPUs are up and running.
-------------------------------------------------------------------
==========================================================
root@target:~# echo mem > /sys/power/state
[ 1135.879168] PM: suspend entry (deep)
[ 1135.906378] PM: Syncing filesystems ...
[ 1135.919299] mmc_host mmc2: Bus speed (slot 0) = 50000000Hz (slot req 
400000Hz, actual 396825HZ div = 63)
[ 1136.137522] mmc_host mmc2: Bus speed (slot 0) = 50000000Hz (slot req 
52000000Hz, actual 50000000HZ div = 0)
[ 1136.184722] done.
[ 1136.196373] Freezing user space processes ... (elapsed 0.001 seconds) 
done.
[ 1136.197689] OOM killer disabled.
[ 1136.197692] Freezing remaining freezable tasks ... (elapsed 0.001 
seconds) done.
[ 1136.198923] printk: Suspending console(s) (use no_console_suspend to 
debug)
[ 1136.364056] devfreq8, suspend_freq=200000000
[ 1136.364114] devfreq4, suspend_freq=200000000
[ 1136.364174] devfreq3, suspend_freq=200000000
[ 1136.364217] devfreq2, suspend_freq=400000000
[ 1136.364311] devfreq0, suspend_freq=400000000
[ 1136.425416] wake enabled for irq 134
[ 1136.425439] CAM_ISP_CORE_1.2V: No configuration
[ 1136.425457] VMEM_VDDF_3.0V: No configuration
[ 1136.425476] VCC_SUB_2.0V: No configuration
[ 1136.425493] VCC_SUB_1.35V: No configuration
[ 1136.425510] VMEM_1.2V_AP: No configuration
[ 1136.426939] MOTOR_VCC_3.0V: No configuration
[ 1136.426958] LCD_VCC_3.3V: No configuration
[ 1136.426976] TSP_VDD_1.8V: No configuration
[ 1136.426994] TSP_AVDD_3.3V: No configuration
[ 1136.427012] VMEM_VDD_2.8V: No configuration
[ 1136.427029] VTF_2.8V: No configuration
[ 1136.427047] VDDQ_PRE_1.8V: No configuration
[ 1136.427064] VT_CAM_1.8V: No configuration
[ 1136.427082] CAM_ISP_SEN_IO_1.8V: No configuration
[ 1136.427100] CAM_SENSOR_CORE_1.2V: No configuration
[ 1136.427605] NFC_AVDD_1.8V: No configuration
[ 1136.429020] CAM_ISP_MIPI_1.2V: No configuration
[ 1136.429859] VCC_1.8V_IO: No configuration
[ 1136.429879] VCC_2.8V_AP: No configuration
[ 1136.429898] VCC_1.8V_AP: No configuration
[ 1136.429926] VALIVE_1.0V_AP: No configuration
[ 1136.435110] wake enabled for irq 138
[ 1136.435381] wake enabled for irq 160
[ 1136.435391] wake enabled for irq 161
[ 1136.443372] samsung-pinctrl 11000000.pinctrl: Setting external wakeup 
interrupt mask: 0xff77ff7d
[ 1136.450942] Disabling non-boot CPUs ...
[ 1136.575256] Enabling non-boot CPUs ...
[ 1136.577278] CPU1 is up
[ 1136.579226] CPU2 is up
[ 1136.581283] CPU3 is up
[ 1136.581773] s3c-i2c 13860000.i2c: slave address 0x10
[ 1136.581788] s3c-i2c 13860000.i2c: bus frequency set to 390 KHz
[ 1136.581809] s3c-i2c 13890000.i2c: slave address 0x10
[ 1136.581821] s3c-i2c 13890000.i2c: bus frequency set to 390 KHz
[ 1136.581841] s3c-i2c 138a0000.i2c: slave address 0x10
[ 1136.581866] s3c-i2c 138a0000.i2c: bus frequency set to 97 KHz
[ 1136.581887] s3c-i2c 138b0000.i2c: slave address 0x00
[ 1136.581899] s3c-i2c 138b0000.i2c: bus frequency set to 97 KHz
[ 1136.581919] s3c-i2c 138d0000.i2c: slave address 0x10
[ 1136.581930] s3c-i2c 138d0000.i2c: bus frequency set to 97 KHz
[ 1136.581949] s3c-i2c 138e0000.i2c: slave address 0x00
[ 1136.581961] s3c-i2c 138e0000.i2c: bus frequency set to 97 KHz
[ 1136.590858] s3c-rtc 10070000.rtc: rtc disabled, re-enabling
[ 1136.592539] s3c2410-wdt 10060000.watchdog: watchdog disabled
[ 1136.592636] wake disabled for irq 160
[ 1136.592646] wake disabled for irq 161
[ 1136.592768] wake disabled for irq 138
[ 1136.596830] wake disabled for irq 134
[ 1136.648824] mmc1: queuing unknown CIS tuple 0x80 (7 bytes)
[ 1136.651430] mmc1: queuing unknown CIS tuple 0x80 (6 bytes)
[ 1136.721638] devfreq8 resume_freq=200000000
[ 1136.721664] devfreq4 resume_freq=200000000
[ 1136.721681] devfreq3 resume_freq=200000000
[ 1136.721704] devfreq2 resume_freq=267000000
[ 1136.721738] devfreq0 resume_freq=267000000
[ 1136.944032] panel-samsung-s6e8aa0 11c80000.dsi.0: ID: 0xa2, 0x20, 0x8c
[ 1137.754939] OOM killer enabled.
[ 1137.760515] Restarting tasks ... done.
root@target:~# [ 1137.768132] PM: suspend exit
-------------------------------------------------------------------

Regards,
Lukasz
> 
> 
> Cheers,
> MyungJoo
> 
> 
>

Patch

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index a9fd61b..46517b6 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -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,
-				DEVFREQ_GOV_SUSPEND, NULL);
+	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,
-				DEVFREQ_GOV_RESUME, NULL);
+	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);
 
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index e4963b0..d985199 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -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;