]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/thermal/cpu_cooling.c
thermal: cpu_cooling: propagate error returned by idr_alloc()
[karo-tx-linux.git] / drivers / thermal / cpu_cooling.c
index 1ab0018271c5c622c0b7556fb92a3a1d9ad38e5b..5c9a2efeec2214051e9218df0e38a976b1f81954 100644 (file)
 #include <linux/cpu.h>
 #include <linux/cpu_cooling.h>
 
+/*
+ * Cooling state <-> CPUFreq frequency
+ *
+ * Cooling states are translated to frequencies throughout this driver and this
+ * is the relation between them.
+ *
+ * Highest cooling state corresponds to lowest possible frequency.
+ *
+ * i.e.
+ *     level 0 --> 1st Max Freq
+ *     level 1 --> 2nd Max Freq
+ *     ...
+ */
+
 /**
  * struct cpufreq_cooling_device - data for cooling device with cpufreq
  * @id: unique integer value corresponding to each cpufreq_cooling_device
@@ -40,9 +54,8 @@
  *     frequency.
  * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
  *
- * This structure is required for keeping information of each
- * cpufreq_cooling_device registered. In order to prevent corruption of this a
- * mutex lock cooling_cpufreq_lock is used.
+ * This structure is required for keeping information of each registered
+ * cpufreq_cooling_device.
  */
 struct cpufreq_cooling_device {
        int id;
@@ -50,15 +63,14 @@ struct cpufreq_cooling_device {
        unsigned int cpufreq_state;
        unsigned int cpufreq_val;
        struct cpumask allowed_cpus;
+       struct list_head node;
 };
 static DEFINE_IDR(cpufreq_idr);
 static DEFINE_MUTEX(cooling_cpufreq_lock);
 
 static unsigned int cpufreq_dev_count;
 
-/* notify_table passes value to the CPUFREQ_ADJUST callback function. */
-#define NOTIFY_INVALID NULL
-static struct cpufreq_cooling_device *notify_device;
+static LIST_HEAD(cpufreq_dev_list);
 
 /**
  * get_idr - function to get a unique id.
@@ -122,7 +134,7 @@ enum cpufreq_cooling_property {
 };
 
 /**
- * get_property - fetch a property of interest for a give cpu.
+ * get_property - fetch a property of interest for a given cpu.
  * @cpu: cpu for which the property is required
  * @input: query parameter
  * @output: query return
@@ -132,6 +144,7 @@ enum cpufreq_cooling_property {
  * 1. get maximum cpu cooling states
  * 2. translate frequency to cooling state
  * 3. translate cooling state to frequency
+ *
  * Note that the code may be not in good shape
  * but it is written in this way in order to:
  * a) reduce duplicate code as most of the code can be shared.
@@ -212,7 +225,7 @@ static int get_property(unsigned int cpu, unsigned long input,
 }
 
 /**
- * cpufreq_cooling_get_level - for a give cpu, return the cooling level.
+ * cpufreq_cooling_get_level - for a given cpu, return the cooling level.
  * @cpu: cpu for which the level is required
  * @freq: the frequency of interest
  *
@@ -287,15 +300,12 @@ static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device,
 
        cpufreq_device->cpufreq_state = cooling_state;
        cpufreq_device->cpufreq_val = clip_freq;
-       notify_device = cpufreq_device;
 
        for_each_cpu(cpuid, mask) {
                if (is_cpufreq_valid(cpuid))
                        cpufreq_update_policy(cpuid);
        }
 
-       notify_device = NOTIFY_INVALID;
-
        return 0;
 }
 
@@ -316,21 +326,28 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
 {
        struct cpufreq_policy *policy = data;
        unsigned long max_freq = 0;
+       struct cpufreq_cooling_device *cpufreq_dev;
 
-       if (event != CPUFREQ_ADJUST || notify_device == NOTIFY_INVALID)
+       if (event != CPUFREQ_ADJUST)
                return 0;
 
-       if (cpumask_test_cpu(policy->cpu, &notify_device->allowed_cpus))
-               max_freq = notify_device->cpufreq_val;
-       else
-               return 0;
+       mutex_lock(&cooling_cpufreq_lock);
+       list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
+               if (!cpumask_test_cpu(policy->cpu,
+                                       &cpufreq_dev->allowed_cpus))
+                       continue;
+
+               if (!cpufreq_dev->cpufreq_val)
+                       cpufreq_dev->cpufreq_val = get_cpu_frequency(
+                                       cpumask_any(&cpufreq_dev->allowed_cpus),
+                                       cpufreq_dev->cpufreq_state);
 
-       /* Never exceed user_policy.max */
-       if (max_freq > policy->user_policy.max)
-               max_freq = policy->user_policy.max;
+               max_freq = cpufreq_dev->cpufreq_val;
 
-       if (policy->max != max_freq)
-               cpufreq_verify_within_limits(policy, 0, max_freq);
+               if (policy->max != max_freq)
+                       cpufreq_verify_within_limits(policy, 0, max_freq);
+       }
+       mutex_unlock(&cooling_cpufreq_lock);
 
        return 0;
 }
@@ -434,12 +451,17 @@ __cpufreq_cooling_register(struct device_node *np,
                           const struct cpumask *clip_cpus)
 {
        struct thermal_cooling_device *cool_dev;
-       struct cpufreq_cooling_device *cpufreq_dev = NULL;
+       struct cpufreq_cooling_device *cpufreq_dev;
        unsigned int min = 0, max = 0;
        char dev_name[THERMAL_NAME_LENGTH];
-       int ret = 0, i;
+       int ret, i;
        struct cpufreq_policy policy;
 
+       if (!cpufreq_frequency_get_table(cpumask_first(clip_cpus))) {
+               pr_debug("%s: CPUFreq table not found\n", __func__);
+               return ERR_PTR(-EPROBE_DEFER);
+       }
+
        /* Verify that all the clip cpus have same freq_min, freq_max limit */
        for_each_cpu(i, clip_cpus) {
                /* continue if cpufreq policy not found and not return error */
@@ -454,8 +476,7 @@ __cpufreq_cooling_register(struct device_node *np,
                                return ERR_PTR(-EINVAL);
                }
        }
-       cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device),
-                             GFP_KERNEL);
+       cpufreq_dev = kzalloc(sizeof(*cpufreq_dev), GFP_KERNEL);
        if (!cpufreq_dev)
                return ERR_PTR(-ENOMEM);
 
@@ -464,7 +485,7 @@ __cpufreq_cooling_register(struct device_node *np,
        ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
        if (ret) {
                kfree(cpufreq_dev);
-               return ERR_PTR(-EINVAL);
+               return ERR_PTR(ret);
        }
 
        snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
@@ -478,7 +499,7 @@ __cpufreq_cooling_register(struct device_node *np,
                return cool_dev;
        }
        cpufreq_dev->cool_dev = cool_dev;
-       cpufreq_dev->cpufreq_state = 0;
+
        mutex_lock(&cooling_cpufreq_lock);
 
        /* Register the notifier for first cpufreq cooling device */
@@ -486,6 +507,7 @@ __cpufreq_cooling_register(struct device_node *np,
                cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
                                          CPUFREQ_POLICY_NOTIFIER);
        cpufreq_dev_count++;
+       list_add(&cpufreq_dev->node, &cpufreq_dev_list);
 
        mutex_unlock(&cooling_cpufreq_lock);
 
@@ -549,6 +571,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 
        cpufreq_dev = cdev->devdata;
        mutex_lock(&cooling_cpufreq_lock);
+       list_del(&cpufreq_dev->node);
        cpufreq_dev_count--;
 
        /* Unregister the notifier for the last cpufreq cooling device */