1111 * published by the Free Software Foundation.
1212 */
1313
14+ #include <linux/cpu.h>
1415#include <linux/kernel.h>
1516#include <linux/errno.h>
1617#include <linux/err.h>
@@ -1195,6 +1196,26 @@ void of_free_opp_table(struct device *dev)
11951196}
11961197EXPORT_SYMBOL_GPL (of_free_opp_table );
11971198
1199+ void of_cpumask_free_opp_table (cpumask_var_t cpumask )
1200+ {
1201+ struct device * cpu_dev ;
1202+ int cpu ;
1203+
1204+ WARN_ON (cpumask_empty (cpumask ));
1205+
1206+ for_each_cpu (cpu , cpumask ) {
1207+ cpu_dev = get_cpu_device (cpu );
1208+ if (!cpu_dev ) {
1209+ pr_err ("%s: failed to get cpu%d device\n" , __func__ ,
1210+ cpu );
1211+ continue ;
1212+ }
1213+
1214+ of_free_opp_table (cpu_dev );
1215+ }
1216+ }
1217+ EXPORT_SYMBOL_GPL (of_cpumask_free_opp_table );
1218+
11981219/* Returns opp descriptor node from its phandle. Caller must do of_node_put() */
11991220static struct device_node *
12001221_of_get_opp_desc_node_from_prop (struct device * dev , const struct property * prop )
@@ -1211,6 +1232,31 @@ _of_get_opp_desc_node_from_prop(struct device *dev, const struct property *prop)
12111232 return opp_np ;
12121233}
12131234
1235+ /* Returns opp descriptor node for a device. Caller must do of_node_put() */
1236+ static struct device_node * _of_get_opp_desc_node (struct device * dev )
1237+ {
1238+ const struct property * prop ;
1239+
1240+ prop = of_find_property (dev -> of_node , "operating-points-v2" , NULL );
1241+ if (!prop )
1242+ return ERR_PTR (- ENODEV );
1243+ if (!prop -> value )
1244+ return ERR_PTR (- ENODATA );
1245+
1246+ /*
1247+ * TODO: Support for multiple OPP tables.
1248+ *
1249+ * There should be only ONE phandle present in "operating-points-v2"
1250+ * property.
1251+ */
1252+ if (prop -> length != sizeof (__be32 )) {
1253+ dev_err (dev , "%s: Invalid opp desc phandle\n" , __func__ );
1254+ return ERR_PTR (- EINVAL );
1255+ }
1256+
1257+ return _of_get_opp_desc_node_from_prop (dev , prop );
1258+ }
1259+
12141260/* Initializes OPP tables based on new bindings */
12151261static int _of_init_opp_table_v2 (struct device * dev ,
12161262 const struct property * prop )
@@ -1351,4 +1397,133 @@ int of_init_opp_table(struct device *dev)
13511397 return _of_init_opp_table_v2 (dev , prop );
13521398}
13531399EXPORT_SYMBOL_GPL (of_init_opp_table );
1400+
1401+ int of_cpumask_init_opp_table (cpumask_var_t cpumask )
1402+ {
1403+ struct device * cpu_dev ;
1404+ int cpu , ret = 0 ;
1405+
1406+ WARN_ON (cpumask_empty (cpumask ));
1407+
1408+ for_each_cpu (cpu , cpumask ) {
1409+ cpu_dev = get_cpu_device (cpu );
1410+ if (!cpu_dev ) {
1411+ pr_err ("%s: failed to get cpu%d device\n" , __func__ ,
1412+ cpu );
1413+ continue ;
1414+ }
1415+
1416+ ret = of_init_opp_table (cpu_dev );
1417+ if (ret ) {
1418+ pr_err ("%s: couldn't find opp table for cpu:%d, %d\n" ,
1419+ __func__ , cpu , ret );
1420+
1421+ /* Free all other OPPs */
1422+ of_cpumask_free_opp_table (cpumask );
1423+ break ;
1424+ }
1425+ }
1426+
1427+ return ret ;
1428+ }
1429+ EXPORT_SYMBOL_GPL (of_cpumask_init_opp_table );
1430+
1431+ /* Required only for V1 bindings, as v2 can manage it from DT itself */
1432+ int set_cpus_sharing_opps (struct device * cpu_dev , cpumask_var_t cpumask )
1433+ {
1434+ struct device_list_opp * list_dev ;
1435+ struct device_opp * dev_opp ;
1436+ struct device * dev ;
1437+ int cpu , ret = 0 ;
1438+
1439+ rcu_read_lock ();
1440+
1441+ dev_opp = _find_device_opp (cpu_dev );
1442+ if (IS_ERR (dev_opp )) {
1443+ ret = - EINVAL ;
1444+ goto out_rcu_read_unlock ;
1445+ }
1446+
1447+ for_each_cpu (cpu , cpumask ) {
1448+ if (cpu == cpu_dev -> id )
1449+ continue ;
1450+
1451+ dev = get_cpu_device (cpu );
1452+ if (!dev ) {
1453+ dev_err (cpu_dev , "%s: failed to get cpu%d device\n" ,
1454+ __func__ , cpu );
1455+ continue ;
1456+ }
1457+
1458+ list_dev = _add_list_dev (dev , dev_opp );
1459+ if (!list_dev ) {
1460+ dev_err (dev , "%s: failed to add list-dev for cpu%d device\n" ,
1461+ __func__ , cpu );
1462+ continue ;
1463+ }
1464+ }
1465+ out_rcu_read_unlock :
1466+ rcu_read_unlock ();
1467+
1468+ return 0 ;
1469+ }
1470+ EXPORT_SYMBOL_GPL (set_cpus_sharing_opps );
1471+
1472+ /*
1473+ * Works only for OPP v2 bindings.
1474+ *
1475+ * cpumask should be already set to mask of cpu_dev->id.
1476+ * Returns -ENOENT if operating-points-v2 bindings aren't supported.
1477+ */
1478+ int of_get_cpus_sharing_opps (struct device * cpu_dev , cpumask_var_t cpumask )
1479+ {
1480+ struct device_node * np , * tmp_np ;
1481+ struct device * tcpu_dev ;
1482+ int cpu , ret = 0 ;
1483+
1484+ /* Get OPP descriptor node */
1485+ np = _of_get_opp_desc_node (cpu_dev );
1486+ if (IS_ERR (np )) {
1487+ dev_dbg (cpu_dev , "%s: Couldn't find opp node: %ld\n" , __func__ ,
1488+ PTR_ERR (np ));
1489+ return - ENOENT ;
1490+ }
1491+
1492+ /* OPPs are shared ? */
1493+ if (!of_property_read_bool (np , "opp-shared" ))
1494+ goto put_cpu_node ;
1495+
1496+ for_each_possible_cpu (cpu ) {
1497+ if (cpu == cpu_dev -> id )
1498+ continue ;
1499+
1500+ tcpu_dev = get_cpu_device (cpu );
1501+ if (!tcpu_dev ) {
1502+ dev_err (cpu_dev , "%s: failed to get cpu%d device\n" ,
1503+ __func__ , cpu );
1504+ ret = - ENODEV ;
1505+ goto put_cpu_node ;
1506+ }
1507+
1508+ /* Get OPP descriptor node */
1509+ tmp_np = _of_get_opp_desc_node (tcpu_dev );
1510+ if (IS_ERR (tmp_np )) {
1511+ dev_err (tcpu_dev , "%s: Couldn't find opp node: %ld\n" ,
1512+ __func__ , PTR_ERR (tmp_np ));
1513+ ret = PTR_ERR (tmp_np );
1514+ goto put_cpu_node ;
1515+ }
1516+
1517+ /* CPUs are sharing opp node */
1518+ if (np == tmp_np )
1519+ cpumask_set_cpu (cpu , cpumask );
1520+
1521+ of_node_put (tmp_np );
1522+ }
1523+
1524+ put_cpu_node :
1525+ of_node_put (np );
1526+ return ret ;
1527+ }
1528+ EXPORT_SYMBOL_GPL (of_get_cpus_sharing_opps );
13541529#endif
0 commit comments