2016-05-05 16:20:33 +05:30
/*
* Generic OPP OF helpers
*
* Copyright ( C ) 2009 - 2010 Texas Instruments Incorporated .
* Nishanth Menon
* Romit Dasgupta
* Kevin Hilman
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/cpu.h>
# include <linux/errno.h>
# include <linux/device.h>
2017-10-12 11:32:23 +01:00
# include <linux/of_device.h>
2016-12-01 16:28:19 +05:30
# include <linux/slab.h>
2016-05-05 16:20:33 +05:30
# include <linux/export.h>
# include "opp.h"
static struct opp_table * _managed_opp ( const struct device_node * np )
{
2017-01-23 10:11:45 +05:30
struct opp_table * opp_table , * managed_table = NULL ;
mutex_lock ( & opp_table_lock ) ;
2016-05-05 16:20:33 +05:30
2017-01-23 10:11:49 +05:30
list_for_each_entry ( opp_table , & opp_tables , node ) {
2016-05-05 16:20:33 +05:30
if ( opp_table - > np = = np ) {
/*
* Multiple devices can point to the same OPP table and
* so will have same node - pointer , np .
*
* But the OPPs will be considered as shared only if the
* OPP table contains a " opp-shared " property .
*/
2017-01-23 10:11:45 +05:30
if ( opp_table - > shared_opp = = OPP_TABLE_ACCESS_SHARED ) {
_get_opp_table_kref ( opp_table ) ;
managed_table = opp_table ;
}
2016-06-16 19:03:11 +05:30
2017-01-23 10:11:45 +05:30
break ;
2016-05-05 16:20:33 +05:30
}
}
2017-01-23 10:11:45 +05:30
mutex_unlock ( & opp_table_lock ) ;
return managed_table ;
2016-05-05 16:20:33 +05:30
}
void _of_init_opp_table ( struct opp_table * opp_table , struct device * dev )
{
struct device_node * np ;
/*
* Only required for backward compatibility with v1 bindings , but isn ' t
* harmful for other cases . And so we do it unconditionally .
*/
np = of_node_get ( dev - > of_node ) ;
if ( np ) {
u32 val ;
if ( ! of_property_read_u32 ( np , " clock-latency " , & val ) )
opp_table - > clock_latency_ns_max = val ;
of_property_read_u32 ( np , " voltage-tolerance " ,
& opp_table - > voltage_tolerance_v1 ) ;
of_node_put ( np ) ;
}
}
static bool _opp_is_supported ( struct device * dev , struct opp_table * opp_table ,
struct device_node * np )
{
unsigned int count = opp_table - > supported_hw_count ;
u32 version ;
int ret ;
2016-09-23 15:07:47 -05:00
if ( ! opp_table - > supported_hw ) {
/*
* In the case that no supported_hw has been set by the
* platform but there is an opp - supported - hw value set for
* an OPP then the OPP should not be enabled as there is
* no way to see if the hardware supports it .
*/
if ( of_find_property ( np , " opp-supported-hw " , NULL ) )
return false ;
else
return true ;
}
2016-05-05 16:20:33 +05:30
while ( count - - ) {
ret = of_property_read_u32_index ( np , " opp-supported-hw " , count ,
& version ) ;
if ( ret ) {
dev_warn ( dev , " %s: failed to read opp-supported-hw property at index %d: %d \n " ,
__func__ , count , ret ) ;
return false ;
}
/* Both of these are bitwise masks of the versions */
if ( ! ( version & opp_table - > supported_hw [ count ] ) )
return false ;
}
return true ;
}
static int opp_parse_supplies ( struct dev_pm_opp * opp , struct device * dev ,
struct opp_table * opp_table )
{
2016-12-01 16:28:19 +05:30
u32 * microvolt , * microamp = NULL ;
int supplies , vcount , icount , ret , i , j ;
2016-05-05 16:20:33 +05:30
struct property * prop = NULL ;
char name [ NAME_MAX ] ;
2016-12-01 16:28:19 +05:30
supplies = opp_table - > regulator_count ? opp_table - > regulator_count : 1 ;
2016-05-05 16:20:33 +05:30
/* Search for "opp-microvolt-<name>" */
if ( opp_table - > prop_name ) {
snprintf ( name , sizeof ( name ) , " opp-microvolt-%s " ,
opp_table - > prop_name ) ;
prop = of_find_property ( opp - > np , name , NULL ) ;
}
if ( ! prop ) {
/* Search for "opp-microvolt" */
sprintf ( name , " opp-microvolt " ) ;
prop = of_find_property ( opp - > np , name , NULL ) ;
/* Missing property isn't a problem, but an invalid entry is */
2017-05-23 09:32:12 +05:30
if ( ! prop ) {
if ( ! opp_table - > regulator_count )
return 0 ;
dev_err ( dev , " %s: opp-microvolt missing although OPP managing regulators \n " ,
__func__ ) ;
return - EINVAL ;
}
2016-05-05 16:20:33 +05:30
}
2016-12-01 16:28:19 +05:30
vcount = of_property_count_u32_elems ( opp - > np , name ) ;
if ( vcount < 0 ) {
2016-05-05 16:20:33 +05:30
dev_err ( dev , " %s: Invalid %s property (%d) \n " ,
2016-12-01 16:28:19 +05:30
__func__ , name , vcount ) ;
return vcount ;
2016-05-05 16:20:33 +05:30
}
2016-12-01 16:28:19 +05:30
/* There can be one or three elements per supply */
if ( vcount ! = supplies & & vcount ! = supplies * 3 ) {
dev_err ( dev , " %s: Invalid number of elements in %s property (%d) with supplies (%d) \n " ,
__func__ , name , vcount , supplies ) ;
2016-05-05 16:20:33 +05:30
return - EINVAL ;
}
2016-12-01 16:28:19 +05:30
microvolt = kmalloc_array ( vcount , sizeof ( * microvolt ) , GFP_KERNEL ) ;
if ( ! microvolt )
return - ENOMEM ;
ret = of_property_read_u32_array ( opp - > np , name , microvolt , vcount ) ;
2016-05-05 16:20:33 +05:30
if ( ret ) {
dev_err ( dev , " %s: error parsing %s: %d \n " , __func__ , name , ret ) ;
2016-12-01 16:28:19 +05:30
ret = - EINVAL ;
goto free_microvolt ;
2016-05-05 16:20:33 +05:30
}
/* Search for "opp-microamp-<name>" */
prop = NULL ;
if ( opp_table - > prop_name ) {
snprintf ( name , sizeof ( name ) , " opp-microamp-%s " ,
opp_table - > prop_name ) ;
prop = of_find_property ( opp - > np , name , NULL ) ;
}
if ( ! prop ) {
/* Search for "opp-microamp" */
sprintf ( name , " opp-microamp " ) ;
prop = of_find_property ( opp - > np , name , NULL ) ;
}
2016-12-01 16:28:19 +05:30
if ( prop ) {
icount = of_property_count_u32_elems ( opp - > np , name ) ;
if ( icount < 0 ) {
dev_err ( dev , " %s: Invalid %s property (%d) \n " , __func__ ,
name , icount ) ;
ret = icount ;
goto free_microvolt ;
}
2016-05-05 16:20:33 +05:30
2016-12-01 16:28:19 +05:30
if ( icount ! = supplies ) {
dev_err ( dev , " %s: Invalid number of elements in %s property (%d) with supplies (%d) \n " ,
__func__ , name , icount , supplies ) ;
ret = - EINVAL ;
goto free_microvolt ;
}
microamp = kmalloc_array ( icount , sizeof ( * microamp ) , GFP_KERNEL ) ;
if ( ! microamp ) {
ret = - EINVAL ;
goto free_microvolt ;
}
ret = of_property_read_u32_array ( opp - > np , name , microamp ,
icount ) ;
if ( ret ) {
dev_err ( dev , " %s: error parsing %s: %d \n " , __func__ ,
name , ret ) ;
ret = - EINVAL ;
goto free_microamp ;
}
}
for ( i = 0 , j = 0 ; i < supplies ; i + + ) {
opp - > supplies [ i ] . u_volt = microvolt [ j + + ] ;
if ( vcount = = supplies ) {
opp - > supplies [ i ] . u_volt_min = opp - > supplies [ i ] . u_volt ;
opp - > supplies [ i ] . u_volt_max = opp - > supplies [ i ] . u_volt ;
} else {
opp - > supplies [ i ] . u_volt_min = microvolt [ j + + ] ;
opp - > supplies [ i ] . u_volt_max = microvolt [ j + + ] ;
}
if ( microamp )
opp - > supplies [ i ] . u_amp = microamp [ i ] ;
}
free_microamp :
kfree ( microamp ) ;
free_microvolt :
kfree ( microvolt ) ;
return ret ;
2016-05-05 16:20:33 +05:30
}
/**
* dev_pm_opp_of_remove_table ( ) - Free OPP table entries created from static DT
* entries
* @ dev : device pointer used to lookup OPP table .
*
* Free OPPs created using static entries present in DT .
*/
void dev_pm_opp_of_remove_table ( struct device * dev )
{
2017-01-02 14:41:00 +05:30
_dev_pm_opp_find_and_remove_table ( dev , false ) ;
2016-05-05 16:20:33 +05:30
}
EXPORT_SYMBOL_GPL ( dev_pm_opp_of_remove_table ) ;
2017-07-27 12:01:17 +02:00
/* Returns opp descriptor node for a device node, caller must
* do of_node_put ( ) */
static struct device_node * _opp_of_get_opp_desc_node ( struct device_node * np )
2016-05-05 16:20:33 +05:30
{
/*
* There should be only ONE phandle present in " operating-points-v2 "
* property .
*/
2017-07-27 12:01:17 +02:00
return of_parse_phandle ( np , " operating-points-v2 " , 0 ) ;
}
/* Returns opp descriptor node for a device, caller must do of_node_put() */
struct device_node * dev_pm_opp_of_get_opp_desc_node ( struct device * dev )
{
return _opp_of_get_opp_desc_node ( dev - > of_node ) ;
2016-05-05 16:20:33 +05:30
}
2017-02-03 11:29:26 -06:00
EXPORT_SYMBOL_GPL ( dev_pm_opp_of_get_opp_desc_node ) ;
2016-05-05 16:20:33 +05:30
/**
* _opp_add_static_v2 ( ) - Allocate static OPPs ( As per ' v2 ' DT bindings )
2017-01-02 14:41:01 +05:30
* @ opp_table : OPP table
2016-05-05 16:20:33 +05:30
* @ dev : device for which we do this operation
* @ np : device node
*
* This function adds an opp definition to the opp table and returns status . The
* opp can be controlled using dev_pm_opp_enable / disable functions and may be
* removed by dev_pm_opp_remove .
*
* Return :
* 0 On success OR
* Duplicate OPPs ( both freq and volt are same ) and opp - > available
* - EEXIST Freq are same and volt are different OR
* Duplicate OPPs ( both freq and volt are same ) and ! opp - > available
* - ENOMEM Memory allocation failure
* - EINVAL Failed parsing the OPP node
*/
2017-01-02 14:41:01 +05:30
static int _opp_add_static_v2 ( struct opp_table * opp_table , struct device * dev ,
struct device_node * np )
2016-05-05 16:20:33 +05:30
{
struct dev_pm_opp * new_opp ;
u64 rate ;
u32 val ;
int ret ;
2017-01-02 14:41:01 +05:30
new_opp = _opp_allocate ( opp_table ) ;
if ( ! new_opp )
return - ENOMEM ;
2016-05-05 16:20:33 +05:30
ret = of_property_read_u64 ( np , " opp-hz " , & rate ) ;
if ( ret < 0 ) {
dev_err ( dev , " %s: opp-hz not found \n " , __func__ ) ;
goto free_opp ;
}
/* Check if the OPP supports hardware's hierarchy of versions or not */
if ( ! _opp_is_supported ( dev , opp_table , np ) ) {
dev_dbg ( dev , " OPP not supported by hardware: %llu \n " , rate ) ;
goto free_opp ;
}
/*
* Rate is defined as an unsigned long in clk API , and so casting
* explicitly to its type . Must be fixed once rate is 64 bit
* guaranteed in clk API .
*/
new_opp - > rate = ( unsigned long ) rate ;
new_opp - > turbo = of_property_read_bool ( np , " turbo-mode " ) ;
new_opp - > np = np ;
new_opp - > dynamic = false ;
new_opp - > available = true ;
if ( ! of_property_read_u32 ( np , " clock-latency-ns " , & val ) )
new_opp - > clock_latency_ns = val ;
ret = opp_parse_supplies ( new_opp , dev , opp_table ) ;
if ( ret )
goto free_opp ;
ret = _opp_add ( dev , new_opp , opp_table ) ;
2017-01-02 14:40:55 +05:30
if ( ret ) {
/* Don't return error for duplicate OPPs */
if ( ret = = - EBUSY )
ret = 0 ;
2016-05-05 16:20:33 +05:30
goto free_opp ;
2017-01-02 14:40:55 +05:30
}
2016-05-05 16:20:33 +05:30
/* OPP to select on device suspend */
if ( of_property_read_bool ( np , " opp-suspend " ) ) {
if ( opp_table - > suspend_opp ) {
dev_warn ( dev , " %s: Multiple suspend OPPs found (%lu %lu) \n " ,
__func__ , opp_table - > suspend_opp - > rate ,
new_opp - > rate ) ;
} else {
new_opp - > suspend = true ;
opp_table - > suspend_opp = new_opp ;
}
}
if ( new_opp - > clock_latency_ns > opp_table - > clock_latency_ns_max )
opp_table - > clock_latency_ns_max = new_opp - > clock_latency_ns ;
pr_debug ( " %s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu \n " ,
2016-12-01 16:28:17 +05:30
__func__ , new_opp - > turbo , new_opp - > rate ,
2016-12-01 16:28:19 +05:30
new_opp - > supplies [ 0 ] . u_volt , new_opp - > supplies [ 0 ] . u_volt_min ,
new_opp - > supplies [ 0 ] . u_volt_max , new_opp - > clock_latency_ns ) ;
2016-05-05 16:20:33 +05:30
/*
* Notify the changes in the availability of the operable
* frequency / voltage list .
*/
2017-01-23 10:11:49 +05:30
blocking_notifier_call_chain ( & opp_table - > head , OPP_EVENT_ADD , new_opp ) ;
2016-05-05 16:20:33 +05:30
return 0 ;
free_opp :
2017-01-02 14:41:01 +05:30
_opp_free ( new_opp ) ;
2016-05-05 16:20:33 +05:30
return ret ;
}
/* Initializes OPP tables based on new bindings */
static int _of_add_opp_table_v2 ( struct device * dev , struct device_node * opp_np )
{
struct device_node * np ;
struct opp_table * opp_table ;
int ret = 0 , count = 0 ;
opp_table = _managed_opp ( opp_np ) ;
if ( opp_table ) {
/* OPPs are already managed */
if ( ! _add_opp_dev ( dev , opp_table ) )
ret = - ENOMEM ;
2017-01-23 10:11:45 +05:30
goto put_opp_table ;
2017-01-02 14:41:01 +05:30
}
2017-01-23 10:11:45 +05:30
opp_table = dev_pm_opp_get_opp_table ( dev ) ;
if ( ! opp_table )
return - ENOMEM ;
2016-05-05 16:20:33 +05:30
/* We have opp-table node now, iterate over it and add OPPs */
for_each_available_child_of_node ( opp_np , np ) {
count + + ;
2017-01-02 14:41:01 +05:30
ret = _opp_add_static_v2 ( opp_table , dev , np ) ;
2016-05-05 16:20:33 +05:30
if ( ret ) {
dev_err ( dev , " %s: Failed to add OPP, %d \n " , __func__ ,
ret ) ;
2017-01-23 10:11:45 +05:30
_dev_pm_opp_remove_table ( opp_table , dev , false ) ;
2017-10-04 11:35:03 +05:30
of_node_put ( np ) ;
2017-01-23 10:11:45 +05:30
goto put_opp_table ;
2016-05-05 16:20:33 +05:30
}
}
/* There should be one of more OPP defined */
2017-01-02 14:41:01 +05:30
if ( WARN_ON ( ! count ) ) {
ret = - ENOENT ;
2017-01-23 10:11:45 +05:30
goto put_opp_table ;
2016-05-05 16:20:33 +05:30
}
opp_table - > np = opp_np ;
2016-06-16 19:03:11 +05:30
if ( of_property_read_bool ( opp_np , " opp-shared " ) )
opp_table - > shared_opp = OPP_TABLE_ACCESS_SHARED ;
else
opp_table - > shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE ;
2016-05-05 16:20:33 +05:30
2017-01-23 10:11:45 +05:30
put_opp_table :
dev_pm_opp_put_opp_table ( opp_table ) ;
2016-05-05 16:20:33 +05:30
return ret ;
}
/* Initializes OPP tables based on old-deprecated bindings */
static int _of_add_opp_table_v1 ( struct device * dev )
{
2017-01-02 14:41:01 +05:30
struct opp_table * opp_table ;
2016-05-05 16:20:33 +05:30
const struct property * prop ;
const __be32 * val ;
2017-01-02 14:41:01 +05:30
int nr , ret = 0 ;
2016-05-05 16:20:33 +05:30
prop = of_find_property ( dev - > of_node , " operating-points " , NULL ) ;
if ( ! prop )
return - ENODEV ;
if ( ! prop - > value )
return - ENODATA ;
/*
* Each OPP is a set of tuples consisting of frequency and
* voltage like < freq - kHz vol - uV > .
*/
nr = prop - > length / sizeof ( u32 ) ;
if ( nr % 2 ) {
dev_err ( dev , " %s: Invalid OPP table \n " , __func__ ) ;
return - EINVAL ;
}
2017-01-23 10:11:45 +05:30
opp_table = dev_pm_opp_get_opp_table ( dev ) ;
if ( ! opp_table )
return - ENOMEM ;
2017-01-02 14:41:01 +05:30
2016-05-05 16:20:33 +05:30
val = prop - > value ;
while ( nr ) {
unsigned long freq = be32_to_cpup ( val + + ) * 1000 ;
unsigned long volt = be32_to_cpup ( val + + ) ;
2017-01-02 14:41:01 +05:30
ret = _opp_add_v1 ( opp_table , dev , freq , volt , false ) ;
2017-01-02 14:40:58 +05:30
if ( ret ) {
dev_err ( dev , " %s: Failed to add OPP %ld (%d) \n " ,
__func__ , freq , ret ) ;
2017-01-02 14:41:01 +05:30
_dev_pm_opp_remove_table ( opp_table , dev , false ) ;
break ;
2017-01-02 14:40:58 +05:30
}
2016-05-05 16:20:33 +05:30
nr - = 2 ;
}
2017-01-23 10:11:45 +05:30
dev_pm_opp_put_opp_table ( opp_table ) ;
2017-01-02 14:41:01 +05:30
return ret ;
2016-05-05 16:20:33 +05:30
}
/**
* dev_pm_opp_of_add_table ( ) - Initialize opp table from device tree
* @ dev : device pointer used to lookup OPP table .
*
* Register the initial OPP table with the OPP library for given device .
*
* Return :
* 0 On success OR
* Duplicate OPPs ( both freq and volt are same ) and opp - > available
* - EEXIST Freq are same and volt are different OR
* Duplicate OPPs ( both freq and volt are same ) and ! opp - > available
* - ENOMEM Memory allocation failure
* - ENODEV when ' operating - points ' property is not found or is invalid data
* in device node .
* - ENODATA when empty ' operating - points ' property is found
* - EINVAL when invalid entries are found in opp - v2 table
*/
int dev_pm_opp_of_add_table ( struct device * dev )
{
struct device_node * opp_np ;
int ret ;
/*
* OPPs have two version of bindings now . The older one is deprecated ,
* try for the new binding first .
*/
2017-02-03 11:29:26 -06:00
opp_np = dev_pm_opp_of_get_opp_desc_node ( dev ) ;
2016-05-05 16:20:33 +05:30
if ( ! opp_np ) {
/*
* Try old - deprecated bindings for backward compatibility with
* older dtbs .
*/
return _of_add_opp_table_v1 ( dev ) ;
}
ret = _of_add_opp_table_v2 ( dev , opp_np ) ;
of_node_put ( opp_np ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dev_pm_opp_of_add_table ) ;
/* CPU device specific helpers */
/**
* dev_pm_opp_of_cpumask_remove_table ( ) - Removes OPP table for @ cpumask
* @ cpumask : cpumask for which OPP table needs to be removed
*
* This removes the OPP tables for CPUs present in the @ cpumask .
* This should be used only to remove static entries created from DT .
*/
void dev_pm_opp_of_cpumask_remove_table ( const struct cpumask * cpumask )
{
_dev_pm_opp_cpumask_remove_table ( cpumask , true ) ;
}
EXPORT_SYMBOL_GPL ( dev_pm_opp_of_cpumask_remove_table ) ;
/**
* dev_pm_opp_of_cpumask_add_table ( ) - Adds OPP table for @ cpumask
* @ cpumask : cpumask for which OPP table needs to be added .
*
* This adds the OPP tables for CPUs present in the @ cpumask .
*/
int dev_pm_opp_of_cpumask_add_table ( const struct cpumask * cpumask )
{
struct device * cpu_dev ;
int cpu , ret = 0 ;
WARN_ON ( cpumask_empty ( cpumask ) ) ;
for_each_cpu ( cpu , cpumask ) {
cpu_dev = get_cpu_device ( cpu ) ;
if ( ! cpu_dev ) {
pr_err ( " %s: failed to get cpu%d device \n " , __func__ ,
cpu ) ;
continue ;
}
ret = dev_pm_opp_of_add_table ( cpu_dev ) ;
if ( ret ) {
2017-07-12 09:21:49 +05:30
/*
* OPP may get registered dynamically , don ' t print error
* message here .
*/
pr_debug ( " %s: couldn't find opp table for cpu:%d, %d \n " ,
__func__ , cpu , ret ) ;
2016-05-05 16:20:33 +05:30
/* Free all other OPPs */
dev_pm_opp_of_cpumask_remove_table ( cpumask ) ;
break ;
}
}
return ret ;
}
EXPORT_SYMBOL_GPL ( dev_pm_opp_of_cpumask_add_table ) ;
/*
* Works only for OPP v2 bindings .
*
* Returns - ENOENT if operating - points - v2 bindings aren ' t supported .
*/
/**
* dev_pm_opp_of_get_sharing_cpus ( ) - Get cpumask of CPUs sharing OPPs with
* @ cpu_dev using operating - points - v2
* bindings .
*
* @ cpu_dev : CPU device for which we do this operation
* @ cpumask : cpumask to update with information of sharing CPUs
*
* This updates the @ cpumask with CPUs that are sharing OPPs with @ cpu_dev .
*
* Returns - ENOENT if operating - points - v2 isn ' t present for @ cpu_dev .
*/
int dev_pm_opp_of_get_sharing_cpus ( struct device * cpu_dev ,
struct cpumask * cpumask )
{
2017-07-27 12:01:17 +02:00
struct device_node * np , * tmp_np , * cpu_np ;
2016-05-05 16:20:33 +05:30
int cpu , ret = 0 ;
/* Get OPP descriptor node */
2017-02-03 11:29:26 -06:00
np = dev_pm_opp_of_get_opp_desc_node ( cpu_dev ) ;
2016-05-05 16:20:33 +05:30
if ( ! np ) {
2016-10-20 16:12:49 +09:00
dev_dbg ( cpu_dev , " %s: Couldn't find opp node. \n " , __func__ ) ;
2016-05-05 16:20:33 +05:30
return - ENOENT ;
}
cpumask_set_cpu ( cpu_dev - > id , cpumask ) ;
/* OPPs are shared ? */
if ( ! of_property_read_bool ( np , " opp-shared " ) )
goto put_cpu_node ;
for_each_possible_cpu ( cpu ) {
if ( cpu = = cpu_dev - > id )
continue ;
2017-10-12 11:32:23 +01:00
cpu_np = of_cpu_device_node_get ( cpu ) ;
2017-07-27 12:01:17 +02:00
if ( ! cpu_np ) {
dev_err ( cpu_dev , " %s: failed to get cpu%d node \n " ,
2016-05-05 16:20:33 +05:30
__func__ , cpu ) ;
2017-07-27 12:01:17 +02:00
ret = - ENOENT ;
2016-05-05 16:20:33 +05:30
goto put_cpu_node ;
}
/* Get OPP descriptor node */
2017-07-27 12:01:17 +02:00
tmp_np = _opp_of_get_opp_desc_node ( cpu_np ) ;
2017-10-12 11:32:23 +01:00
of_node_put ( cpu_np ) ;
2016-05-05 16:20:33 +05:30
if ( ! tmp_np ) {
2017-07-27 12:01:17 +02:00
pr_err ( " %pOF: Couldn't find opp node \n " , cpu_np ) ;
2016-05-05 16:20:33 +05:30
ret = - ENOENT ;
goto put_cpu_node ;
}
/* CPUs are sharing opp node */
if ( np = = tmp_np )
cpumask_set_cpu ( cpu , cpumask ) ;
of_node_put ( tmp_np ) ;
}
put_cpu_node :
of_node_put ( np ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dev_pm_opp_of_get_sharing_cpus ) ;