LCOV - code coverage report
Current view: top level - drivers/opp - cpu.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 41 69 59.4 %
Date: 2020-09-30 20:25:40 Functions: 4 6 66.7 %
Branches: 19 40 47.5 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : /*
       3                 :            :  * Generic OPP helper interface for CPU device
       4                 :            :  *
       5                 :            :  * Copyright (C) 2009-2014 Texas Instruments Incorporated.
       6                 :            :  *      Nishanth Menon
       7                 :            :  *      Romit Dasgupta
       8                 :            :  *      Kevin Hilman
       9                 :            :  */
      10                 :            : 
      11                 :            : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      12                 :            : 
      13                 :            : #include <linux/cpu.h>
      14                 :            : #include <linux/cpufreq.h>
      15                 :            : #include <linux/err.h>
      16                 :            : #include <linux/errno.h>
      17                 :            : #include <linux/export.h>
      18                 :            : #include <linux/slab.h>
      19                 :            : 
      20                 :            : #include "opp.h"
      21                 :            : 
      22                 :            : #ifdef CONFIG_CPU_FREQ
      23                 :            : 
      24                 :            : /**
      25                 :            :  * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device
      26                 :            :  * @dev:        device for which we do this operation
      27                 :            :  * @table:      Cpufreq table returned back to caller
      28                 :            :  *
      29                 :            :  * Generate a cpufreq table for a provided device- this assumes that the
      30                 :            :  * opp table is already initialized and ready for usage.
      31                 :            :  *
      32                 :            :  * This function allocates required memory for the cpufreq table. It is
      33                 :            :  * expected that the caller does the required maintenance such as freeing
      34                 :            :  * the table as required.
      35                 :            :  *
      36                 :            :  * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM
      37                 :            :  * if no memory available for the operation (table is not populated), returns 0
      38                 :            :  * if successful and table is populated.
      39                 :            :  *
      40                 :            :  * WARNING: It is  important for the callers to ensure refreshing their copy of
      41                 :            :  * the table if any of the mentioned functions have been invoked in the interim.
      42                 :            :  */
      43                 :        207 : int dev_pm_opp_init_cpufreq_table(struct device *dev,
      44                 :            :                                   struct cpufreq_frequency_table **table)
      45                 :            : {
      46                 :            :         struct dev_pm_opp *opp;
      47                 :            :         struct cpufreq_frequency_table *freq_table = NULL;
      48                 :            :         int i, max_opps, ret = 0;
      49                 :            :         unsigned long rate;
      50                 :            : 
      51                 :        207 :         max_opps = dev_pm_opp_get_opp_count(dev);
      52         [ -  + ]:        207 :         if (max_opps <= 0)
      53         [ #  # ]:          0 :                 return max_opps ? max_opps : -ENODATA;
      54                 :            : 
      55                 :        207 :         freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_KERNEL);
      56         [ +  - ]:        207 :         if (!freq_table)
      57                 :            :                 return -ENOMEM;
      58                 :            : 
      59         [ +  + ]:       1035 :         for (i = 0, rate = 0; i < max_opps; i++, rate++) {
      60                 :            :                 /* find next rate */
      61                 :        828 :                 opp = dev_pm_opp_find_freq_ceil(dev, &rate);
      62         [ -  + ]:        828 :                 if (IS_ERR(opp)) {
      63                 :            :                         ret = PTR_ERR(opp);
      64                 :          0 :                         goto out;
      65                 :            :                 }
      66                 :        828 :                 freq_table[i].driver_data = i;
      67                 :        828 :                 freq_table[i].frequency = rate / 1000;
      68                 :            : 
      69                 :            :                 /* Is Boost/turbo opp ? */
      70         [ -  + ]:        828 :                 if (dev_pm_opp_is_turbo(opp))
      71                 :          0 :                         freq_table[i].flags = CPUFREQ_BOOST_FREQ;
      72                 :            : 
      73                 :        828 :                 dev_pm_opp_put(opp);
      74                 :            :         }
      75                 :            : 
      76                 :        207 :         freq_table[i].driver_data = i;
      77                 :        207 :         freq_table[i].frequency = CPUFREQ_TABLE_END;
      78                 :            : 
      79                 :        207 :         *table = &freq_table[0];
      80                 :            : 
      81                 :            : out:
      82         [ -  + ]:        207 :         if (ret)
      83                 :          0 :                 kfree(freq_table);
      84                 :            : 
      85                 :        207 :         return ret;
      86                 :            : }
      87                 :            : EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table);
      88                 :            : 
      89                 :            : /**
      90                 :            :  * dev_pm_opp_free_cpufreq_table() - free the cpufreq table
      91                 :            :  * @dev:        device for which we do this operation
      92                 :            :  * @table:      table to free
      93                 :            :  *
      94                 :            :  * Free up the table allocated by dev_pm_opp_init_cpufreq_table
      95                 :            :  */
      96                 :          0 : void dev_pm_opp_free_cpufreq_table(struct device *dev,
      97                 :            :                                    struct cpufreq_frequency_table **table)
      98                 :            : {
      99         [ #  # ]:          0 :         if (!table)
     100                 :          0 :                 return;
     101                 :            : 
     102                 :          0 :         kfree(*table);
     103                 :          0 :         *table = NULL;
     104                 :            : }
     105                 :            : EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
     106                 :            : #endif  /* CONFIG_CPU_FREQ */
     107                 :            : 
     108                 :        207 : void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask,
     109                 :            :                                       int last_cpu)
     110                 :            : {
     111                 :            :         struct device *cpu_dev;
     112                 :            :         int cpu;
     113                 :            : 
     114         [ -  + ]:        207 :         WARN_ON(cpumask_empty(cpumask));
     115                 :            : 
     116         [ +  - ]:        207 :         for_each_cpu(cpu, cpumask) {
     117         [ -  + ]:        207 :                 if (cpu == last_cpu)
     118                 :            :                         break;
     119                 :            : 
     120                 :          0 :                 cpu_dev = get_cpu_device(cpu);
     121         [ #  # ]:          0 :                 if (!cpu_dev) {
     122                 :          0 :                         pr_err("%s: failed to get cpu%d device\n", __func__,
     123                 :            :                                cpu);
     124                 :          0 :                         continue;
     125                 :            :                 }
     126                 :            : 
     127                 :          0 :                 _dev_pm_opp_find_and_remove_table(cpu_dev);
     128                 :            :         }
     129                 :        207 : }
     130                 :            : 
     131                 :            : /**
     132                 :            :  * dev_pm_opp_cpumask_remove_table() - Removes OPP table for @cpumask
     133                 :            :  * @cpumask:    cpumask for which OPP table needs to be removed
     134                 :            :  *
     135                 :            :  * This removes the OPP tables for CPUs present in the @cpumask.
     136                 :            :  * This should be used to remove all the OPPs entries associated with
     137                 :            :  * the cpus in @cpumask.
     138                 :            :  */
     139                 :          0 : void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask)
     140                 :            : {
     141                 :          0 :         _dev_pm_opp_cpumask_remove_table(cpumask, -1);
     142                 :          0 : }
     143                 :            : EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_remove_table);
     144                 :            : 
     145                 :            : /**
     146                 :            :  * dev_pm_opp_set_sharing_cpus() - Mark OPP table as shared by few CPUs
     147                 :            :  * @cpu_dev:    CPU device for which we do this operation
     148                 :            :  * @cpumask:    cpumask of the CPUs which share the OPP table with @cpu_dev
     149                 :            :  *
     150                 :            :  * This marks OPP table of the @cpu_dev as shared by the CPUs present in
     151                 :            :  * @cpumask.
     152                 :            :  *
     153                 :            :  * Returns -ENODEV if OPP table isn't already present.
     154                 :            :  */
     155                 :        207 : int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev,
     156                 :            :                                 const struct cpumask *cpumask)
     157                 :            : {
     158                 :            :         struct opp_device *opp_dev;
     159                 :            :         struct opp_table *opp_table;
     160                 :            :         struct device *dev;
     161                 :            :         int cpu, ret = 0;
     162                 :            : 
     163                 :        207 :         opp_table = _find_opp_table(cpu_dev);
     164         [ +  - ]:        207 :         if (IS_ERR(opp_table))
     165                 :          0 :                 return PTR_ERR(opp_table);
     166                 :            : 
     167         [ +  + ]:       1035 :         for_each_cpu(cpu, cpumask) {
     168         [ +  + ]:        828 :                 if (cpu == cpu_dev->id)
     169                 :        207 :                         continue;
     170                 :            : 
     171                 :        621 :                 dev = get_cpu_device(cpu);
     172         [ -  + ]:        621 :                 if (!dev) {
     173                 :          0 :                         dev_err(cpu_dev, "%s: failed to get cpu%d device\n",
     174                 :            :                                 __func__, cpu);
     175                 :          0 :                         continue;
     176                 :            :                 }
     177                 :            : 
     178                 :        621 :                 opp_dev = _add_opp_dev(dev, opp_table);
     179         [ -  + ]:        621 :                 if (!opp_dev) {
     180                 :          0 :                         dev_err(dev, "%s: failed to add opp-dev for cpu%d device\n",
     181                 :            :                                 __func__, cpu);
     182                 :          0 :                         continue;
     183                 :            :                 }
     184                 :            : 
     185                 :            :                 /* Mark opp-table as multiple CPUs are sharing it now */
     186                 :        621 :                 opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
     187                 :            :         }
     188                 :            : 
     189                 :        207 :         dev_pm_opp_put_opp_table(opp_table);
     190                 :            : 
     191                 :        207 :         return ret;
     192                 :            : }
     193                 :            : EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus);
     194                 :            : 
     195                 :            : /**
     196                 :            :  * dev_pm_opp_get_sharing_cpus() - Get cpumask of CPUs sharing OPPs with @cpu_dev
     197                 :            :  * @cpu_dev:    CPU device for which we do this operation
     198                 :            :  * @cpumask:    cpumask to update with information of sharing CPUs
     199                 :            :  *
     200                 :            :  * This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev.
     201                 :            :  *
     202                 :            :  * Returns -ENODEV if OPP table isn't already present and -EINVAL if the OPP
     203                 :            :  * table's status is access-unknown.
     204                 :            :  */
     205                 :        207 : int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
     206                 :            : {
     207                 :            :         struct opp_device *opp_dev;
     208                 :            :         struct opp_table *opp_table;
     209                 :            :         int ret = 0;
     210                 :            : 
     211                 :        207 :         opp_table = _find_opp_table(cpu_dev);
     212         [ -  + ]:        207 :         if (IS_ERR(opp_table))
     213                 :          0 :                 return PTR_ERR(opp_table);
     214                 :            : 
     215         [ -  + ]:        207 :         if (opp_table->shared_opp == OPP_TABLE_ACCESS_UNKNOWN) {
     216                 :            :                 ret = -EINVAL;
     217                 :            :                 goto put_opp_table;
     218                 :            :         }
     219                 :            : 
     220                 :            :         cpumask_clear(cpumask);
     221                 :            : 
     222                 :            :         if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) {
     223                 :          0 :                 mutex_lock(&opp_table->lock);
     224         [ #  # ]:          0 :                 list_for_each_entry(opp_dev, &opp_table->dev_list, node)
     225                 :          0 :                         cpumask_set_cpu(opp_dev->dev->id, cpumask);
     226                 :          0 :                 mutex_unlock(&opp_table->lock);
     227                 :            :         } else {
     228                 :          0 :                 cpumask_set_cpu(cpu_dev->id, cpumask);
     229                 :            :         }
     230                 :            : 
     231                 :            : put_opp_table:
     232                 :        207 :         dev_pm_opp_put_opp_table(opp_table);
     233                 :            : 
     234                 :        207 :         return ret;
     235                 :            : }
     236                 :            : EXPORT_SYMBOL_GPL(dev_pm_opp_get_sharing_cpus);

Generated by: LCOV version 1.14