2323#include <linux/nvmem-consumer.h>
2424#include <linux/of.h>
2525#include <linux/platform_device.h>
26+ #include <linux/pm.h>
2627#include <linux/pm_domain.h>
2728#include <linux/pm_opp.h>
29+ #include <linux/pm_runtime.h>
2830#include <linux/slab.h>
2931#include <linux/soc/qcom/smem.h>
3032
@@ -55,6 +57,7 @@ struct qcom_cpufreq_match_data {
5557
5658struct qcom_cpufreq_drv_cpu {
5759 int opp_token ;
60+ struct device * * virt_devs ;
5861};
5962
6063struct qcom_cpufreq_drv {
@@ -424,6 +427,30 @@ static const struct qcom_cpufreq_match_data match_data_ipq8074 = {
424427 .get_version = qcom_cpufreq_ipq8074_name_version ,
425428};
426429
430+ static void qcom_cpufreq_suspend_virt_devs (struct qcom_cpufreq_drv * drv , unsigned int cpu )
431+ {
432+ const char * const * name = drv -> data -> genpd_names ;
433+ int i ;
434+
435+ if (!drv -> cpus [cpu ].virt_devs )
436+ return ;
437+
438+ for (i = 0 ; * name ; i ++ , name ++ )
439+ device_set_awake_path (drv -> cpus [cpu ].virt_devs [i ]);
440+ }
441+
442+ static void qcom_cpufreq_put_virt_devs (struct qcom_cpufreq_drv * drv , unsigned int cpu )
443+ {
444+ const char * const * name = drv -> data -> genpd_names ;
445+ int i ;
446+
447+ if (!drv -> cpus [cpu ].virt_devs )
448+ return ;
449+
450+ for (i = 0 ; * name ; i ++ , name ++ )
451+ pm_runtime_put (drv -> cpus [cpu ].virt_devs [i ]);
452+ }
453+
427454static int qcom_cpufreq_probe (struct platform_device * pdev )
428455{
429456 struct qcom_cpufreq_drv * drv ;
@@ -478,6 +505,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
478505 of_node_put (np );
479506
480507 for_each_possible_cpu (cpu ) {
508+ struct device * * virt_devs = NULL ;
481509 struct dev_pm_opp_config config = {
482510 .supported_hw = NULL ,
483511 };
@@ -498,7 +526,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
498526
499527 if (drv -> data -> genpd_names ) {
500528 config .genpd_names = drv -> data -> genpd_names ;
501- config .virt_devs = NULL ;
529+ config .virt_devs = & virt_devs ;
502530 }
503531
504532 if (config .supported_hw || config .genpd_names ) {
@@ -509,6 +537,27 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
509537 goto free_opp ;
510538 }
511539 }
540+
541+ if (virt_devs ) {
542+ const char * const * name = config .genpd_names ;
543+ int i , j ;
544+
545+ for (i = 0 ; * name ; i ++ , name ++ ) {
546+ ret = pm_runtime_resume_and_get (virt_devs [i ]);
547+ if (ret ) {
548+ dev_err (cpu_dev , "failed to resume %s: %d\n" ,
549+ * name , ret );
550+
551+ /* Rollback previous PM runtime calls */
552+ name = config .genpd_names ;
553+ for (j = 0 ; * name && j < i ; j ++ , name ++ )
554+ pm_runtime_put (virt_devs [j ]);
555+
556+ goto free_opp ;
557+ }
558+ }
559+ drv -> cpus [cpu ].virt_devs = virt_devs ;
560+ }
512561 }
513562
514563 cpufreq_dt_pdev = platform_device_register_simple ("cpufreq-dt" , -1 ,
@@ -522,8 +571,10 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
522571 dev_err (cpu_dev , "Failed to register platform device\n" );
523572
524573free_opp :
525- for_each_possible_cpu (cpu )
574+ for_each_possible_cpu (cpu ) {
575+ qcom_cpufreq_put_virt_devs (drv , cpu );
526576 dev_pm_opp_clear_config (drv -> cpus [cpu ].opp_token );
577+ }
527578 return ret ;
528579}
529580
@@ -534,15 +585,31 @@ static void qcom_cpufreq_remove(struct platform_device *pdev)
534585
535586 platform_device_unregister (cpufreq_dt_pdev );
536587
537- for_each_possible_cpu (cpu )
588+ for_each_possible_cpu (cpu ) {
589+ qcom_cpufreq_put_virt_devs (drv , cpu );
538590 dev_pm_opp_clear_config (drv -> cpus [cpu ].opp_token );
591+ }
539592}
540593
594+ static int qcom_cpufreq_suspend (struct device * dev )
595+ {
596+ struct qcom_cpufreq_drv * drv = dev_get_drvdata (dev );
597+ unsigned int cpu ;
598+
599+ for_each_possible_cpu (cpu )
600+ qcom_cpufreq_suspend_virt_devs (drv , cpu );
601+
602+ return 0 ;
603+ }
604+
605+ static DEFINE_SIMPLE_DEV_PM_OPS (qcom_cpufreq_pm_ops , qcom_cpufreq_suspend , NULL) ;
606+
541607static struct platform_driver qcom_cpufreq_driver = {
542608 .probe = qcom_cpufreq_probe ,
543609 .remove_new = qcom_cpufreq_remove ,
544610 .driver = {
545611 .name = "qcom-cpufreq-nvmem" ,
612+ .pm = pm_sleep_ptr (& qcom_cpufreq_pm_ops ),
546613 },
547614};
548615
0 commit comments