[COMMON] thermal: gpu_cooling: introduce power_allocator API
authorHyeonseong Gil <hs.gil@samsung.com>
Fri, 1 Apr 2016 00:39:23 +0000 (09:39 +0900)
committerChungwoo Park <cww.park@samsung.com>
Mon, 21 May 2018 08:09:33 +0000 (17:09 +0900)
Resolved migration conflicts from kernel 4.9 to 4.14.
- rename gpufreq_device -> gpufreq_cdev
- but keep up idr, power_table and build_dyn_power_table

Change-Id: I9e18fb576053a2d2371a79c11757be0364982fb6
Signed-off-by: Hyeonseong Gil <hs.gil@samsung.com>
drivers/thermal/gpu_cooling.c
drivers/thermal/samsung/exynos_tmu.c
include/linux/gpu_cooling.h
include/trace/events/thermal.h

index 852c4ab01d40bb994be3c2f686c97368ddf981fa..8df12c8d1d0081d7b55845d173e3c3dcf886540c 100644 (file)
 #include <linux/cpu.h>
 #include <linux/gpu_cooling.h>
 #include <soc/samsung/tmu.h>
+#include <trace/events/thermal.h>
+
+/**
+ * struct power_table - frequency to power conversion
+ * @frequency: frequency in KHz
+ * @power:     power in mW
+ *
+ * This structure is built when the cooling device registers and helps
+ * in translating frequency to power and viceversa.
+ */
+struct power_table {
+       u32 frequency;
+       u32 power;
+};
 
 /**
  * struct gpufreq_cooling_device - data for cooling device with gpufreq
@@ -39,7 +53,6 @@
  *     cooling devices.
  * @gpufreq_val: integer value representing the absolute value of the clipped
  *     frequency.
- * @allowed_gpus: all the gpus involved for this gpufreq_cooling_device.
  *
  * This structure is required for keeping information of each
  * gpufreq_cooling_device registered. In order to prevent corruption of this a
@@ -50,12 +63,17 @@ struct gpufreq_cooling_device {
        struct thermal_cooling_device *cool_dev;
        unsigned int gpufreq_state;
        unsigned int gpufreq_val;
+       u32 last_load;
+       struct power_table *dyn_power_table;
+       int dyn_power_table_entries;
+       get_static_t plat_get_static_power;
 };
+
 static DEFINE_IDR(gpufreq_idr);
 static DEFINE_MUTEX(cooling_gpu_lock);
 static BLOCKING_NOTIFIER_HEAD(gpu_notifier);
 
-static unsigned int gpufreq_dev_count;
+static unsigned int gpufreq_cdev_count;
 
 extern struct cpufreq_frequency_table gpu_freq_table[];
 
@@ -215,9 +233,151 @@ unsigned long gpufreq_cooling_get_level(unsigned int gpu, unsigned int freq)
 }
 EXPORT_SYMBOL_GPL(gpufreq_cooling_get_level);
 
+/**
+ * build_dyn_power_table() - create a dynamic power to frequency table
+ * @gpufreq_cdev:      the gpufreq cooling device in which to store the table
+ * @capacitance: dynamic power coefficient for these gpus
+ *
+ * Build a dynamic power to frequency table for this gpu and store it
+ * in @gpufreq_cdev.  This table will be used in gpu_power_to_freq() and
+ * gpu_freq_to_power() to convert between power and frequency
+ * efficiently.  Power is stored in mW, frequency in KHz.  The
+ * resulting table is in ascending order.
+ *
+ * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs,
+ * -ENOMEM if we run out of memory or -EAGAIN if an OPP was
+ * added/enabled while the function was executing.
+ */
+static int build_dyn_power_table(struct gpufreq_cooling_device *gpufreq_cdev,
+                                u32 capacitance)
+{
+       struct power_table *power_table;
+       int num_opps = 0, i, cnt = 0;
+       unsigned long freq;
+
+       num_opps = gpu_dvfs_get_step();
+
+       if (num_opps == 0)
+               return -EINVAL;
+
+       power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL);
+       if (!power_table)
+               return -ENOMEM;
+
+       for (freq = 0, i = 0; i < num_opps; i++) {
+               u32 voltage_mv;
+               u64 power;
+
+               freq = gpu_dvfs_get_clock(num_opps - i - 1);
+
+               if (freq > gpu_dvfs_get_max_freq())
+                       continue;
+
+               voltage_mv = gpu_dvfs_get_voltage(freq) / 1000;
+
+               /*
+                * Do the multiplication with MHz and millivolt so as
+                * to not overflow.
+                */
+               power = (u64)capacitance * freq * voltage_mv * voltage_mv;
+               do_div(power, 1000000000);
+
+               power_table[i].frequency = freq;
+
+               /* power is stored in mW */
+               power_table[i].power = power;
+               cnt++;
+       }
+
+       gpufreq_cdev->dyn_power_table = power_table;
+       gpufreq_cdev->dyn_power_table_entries = cnt;
+
+       return 0;
+}
+
+static u32 gpu_freq_to_power(struct gpufreq_cooling_device *gpufreq_cdev,
+                            u32 freq)
+{
+       int i;
+       struct power_table *pt = gpufreq_cdev->dyn_power_table;
+
+       for (i = 1; i < gpufreq_cdev->dyn_power_table_entries; i++)
+               if (freq < pt[i].frequency)
+                       break;
+
+       return pt[i - 1].power;
+}
+
+static u32 gpu_power_to_freq(struct gpufreq_cooling_device *gpufreq_cdev,
+                            u32 power)
+{
+       int i;
+       struct power_table *pt = gpufreq_cdev->dyn_power_table;
+
+       for (i = 1; i < gpufreq_cdev->dyn_power_table_entries; i++)
+               if (power < pt[i].power)
+                       break;
+
+       return pt[i - 1].frequency;
+}
+
+/**
+ * get_static_power() - calculate the static power consumed by the gpus
+ * @gpufreq_cdev:      struct &gpufreq_cooling_device for this gpu cdev
+ * @tz:                thermal zone device in which we're operating
+ * @freq:      frequency in KHz
+ * @power:     pointer in which to store the calculated static power
+ *
+ * Calculate the static power consumed by the gpus described by
+ * @gpu_actor running at frequency @freq.  This function relies on a
+ * platform specific function that should have been provided when the
+ * actor was registered.  If it wasn't, the static power is assumed to
+ * be negligible.  The calculated static power is stored in @power.
+ *
+ * Return: 0 on success, -E* on failure.
+ */
+static int get_static_power(struct gpufreq_cooling_device *gpufreq_cdev,
+                           struct thermal_zone_device *tz, unsigned long freq,
+                           u32 *power)
+{
+       unsigned long voltage;
+
+       if (!gpufreq_cdev->plat_get_static_power || freq == 0) {
+               *power = 0;
+               return 0;
+       }
+
+       voltage = gpu_dvfs_get_voltage(freq);
+
+       if (voltage == 0) {
+               pr_warn("Failed to get voltage for frequency %lu\n", freq);
+               return -EINVAL;
+       }
+
+       return gpufreq_cdev->plat_get_static_power(NULL, tz->passive_delay,
+                                                    voltage, power);
+}
+
+/**
+ * get_dynamic_power() - calculate the dynamic power
+ * @gpufreq_cdev:      &gpufreq_cooling_device for this cdev
+ * @freq:      current frequency
+ *
+ * Return: the dynamic power consumed by the gpus described by
+ * @gpufreq_cdev.
+ */
+static u32 get_dynamic_power(struct gpufreq_cooling_device *gpufreq_cdev,
+                            unsigned long freq)
+{
+       u32 raw_gpu_power;
+
+       raw_gpu_power = gpu_freq_to_power(gpufreq_cdev, freq);
+       return (raw_gpu_power * gpufreq_cdev->last_load) / 100;
+}
+
 /**
  * gpufreq_apply_cooling - function to apply frequency clipping.
- * @gpufreq_device: gpufreq_cooling_device pointer containing frequency
+ * @gpufreq_cdev: gpufreq_cooling_device pointer containing frequency
  *     clipping data.
  * @cooling_state: value of the cooling state.
  *
@@ -227,14 +387,14 @@ EXPORT_SYMBOL_GPL(gpufreq_cooling_get_level);
  * Return: 0 on success, an error code otherwise (-EINVAL in case wrong
  * cooling state).
  */
-static int gpufreq_apply_cooling(struct gpufreq_cooling_device *gpufreq_device,
+static int gpufreq_apply_cooling(struct gpufreq_cooling_device *gpufreq_cdev,
                                 unsigned long cooling_state)
 {
        /* Check if the old cooling action is same as new cooling action */
-       if (gpufreq_device->gpufreq_state == cooling_state)
+       if (gpufreq_cdev->gpufreq_state == cooling_state)
                return 0;
 
-       gpufreq_device->gpufreq_state = cooling_state;
+       gpufreq_cdev->gpufreq_state = cooling_state;
 
        blocking_notifier_call_chain(&gpu_notifier, GPU_THROTTLING, &cooling_state);
 
@@ -280,9 +440,9 @@ static int gpufreq_get_max_state(struct thermal_cooling_device *cdev,
 static int gpufreq_get_cur_state(struct thermal_cooling_device *cdev,
                                 unsigned long *state)
 {
-       struct gpufreq_cooling_device *gpufreq_device = cdev->devdata;
+       struct gpufreq_cooling_device *gpufreq_cdev = cdev->devdata;
 
-       *state = gpufreq_device->gpufreq_state;
+       *state = gpufreq_cdev->gpufreq_state;
 
        return 0;
 }
@@ -300,9 +460,9 @@ static int gpufreq_get_cur_state(struct thermal_cooling_device *cdev,
 static int gpufreq_set_cur_state(struct thermal_cooling_device *cdev,
                                 unsigned long state)
 {
-       struct gpufreq_cooling_device *gpufreq_device = cdev->devdata;
+       struct gpufreq_cooling_device *gpufreq_cdev = cdev->devdata;
 
-       return gpufreq_apply_cooling(gpufreq_device, state);
+       return gpufreq_apply_cooling(gpufreq_cdev, state);
 }
 
 static enum tmu_noti_state_t gpu_tstate = GPU_COLD;
@@ -327,8 +487,151 @@ static int gpufreq_set_cur_temp(struct thermal_cooling_device *cdev,
        return 0;
 }
 
+/**
+ * gpufreq_get_requested_power() - get the current power
+ * @cdev:      &thermal_cooling_device pointer
+ * @tz:                a valid thermal zone device pointer
+ * @power:     pointer in which to store the resulting power
+ *
+ * Calculate the current power consumption of the gpus in milliwatts
+ * and store it in @power.  This function should actually calculate
+ * the requested power, but it's hard to get the frequency that
+ * gpufreq would have assigned if there were no thermal limits.
+ * Instead, we calculate the current power on the assumption that the
+ * immediate future will look like the immediate past.
+ *
+ * We use the current frequency and the average load since this
+ * function was last called.  In reality, there could have been
+ * multiple opps since this function was last called and that affects
+ * the load calculation.  While it's not perfectly accurate, this
+ * simplification is good enough and works.  REVISIT this, as more
+ * complex code may be needed if experiments show that it's not
+ * accurate enough.
+ *
+ * Return: 0 on success, -E* if getting the static power failed.
+ */
+static int gpufreq_get_requested_power(struct thermal_cooling_device *cdev,
+                                      struct thermal_zone_device *tz,
+                                      u32 *power)
+{
+       unsigned long freq;
+       int ret = 0;
+       u32 static_power, dynamic_power;
+       struct gpufreq_cooling_device *gpufreq_cdev = cdev->devdata;
+       u32 load_gpu = 0;
+
+       freq = gpu_dvfs_get_cur_clock();
+
+       load_gpu = gpu_dvfs_get_utilization();;
+
+       gpufreq_cdev->last_load = load_gpu;
+
+       dynamic_power = get_dynamic_power(gpufreq_cdev, freq);
+       ret = get_static_power(gpufreq_cdev, tz, freq, &static_power);
+
+       if (ret)
+               return ret;
+
+       if (trace_thermal_power_gpu_get_power_enabled()) {
+               trace_thermal_power_gpu_get_power(
+                       freq, load_gpu, dynamic_power, static_power);
+       }
+
+       *power = static_power + dynamic_power;
+       return 0;
+}
+
+/**
+ * gpufreq_state2power() - convert a gpu cdev state to power consumed
+ * @cdev:      &thermal_cooling_device pointer
+ * @tz:                a valid thermal zone device pointer
+ * @state:     cooling device state to be converted
+ * @power:     pointer in which to store the resulting power
+ *
+ * Convert cooling device state @state into power consumption in
+ * milliwatts assuming 100% load.  Store the calculated power in
+ * @power.
+ *
+ * Return: 0 on success, -EINVAL if the cooling device state could not
+ * be converted into a frequency or other -E* if there was an error
+ * when calculating the static power.
+ */
+static int gpufreq_state2power(struct thermal_cooling_device *cdev,
+                              struct thermal_zone_device *tz,
+                              unsigned long state, u32 *power)
+{
+       unsigned int freq;
+       u32 static_power, dynamic_power;
+       int ret;
+       struct gpufreq_cooling_device *gpufreq_cdev = cdev->devdata;
+
+       freq = gpu_freq_table[state].frequency / 1000;
+       if (!freq)
+               return -EINVAL;
+
+       dynamic_power = gpu_freq_to_power(gpufreq_cdev, freq);
+       ret = get_static_power(gpufreq_cdev, tz, freq, &static_power);
+       if (ret)
+               return ret;
+
+       *power = static_power + dynamic_power;
+       return 0;
+}
+
+/**
+ * gpufreq_power2state() - convert power to a cooling device state
+ * @cdev:      &thermal_cooling_device pointer
+ * @tz:                a valid thermal zone device pointer
+ * @power:     power in milliwatts to be converted
+ * @state:     pointer in which to store the resulting state
+ *
+ * Calculate a cooling device state for the gpus described by @cdev
+ * that would allow them to consume at most @power mW and store it in
+ * @state.  Note that this calculation depends on external factors
+ * such as the gpu load or the current static power.  Calling this
+ * function with the same power as input can yield different cooling
+ * device states depending on those external factors.
+ *
+ * Return: 0 on success, -ENODEV if no gpus are online or -EINVAL if
+ * the calculated frequency could not be converted to a valid state.
+ * The latter should not happen unless the frequencies available to
+ * gpufreq have changed since the initialization of the gpu cooling
+ * device.
+ */
+static int gpufreq_power2state(struct thermal_cooling_device *cdev,
+                              struct thermal_zone_device *tz, u32 power,
+                              unsigned long *state)
+{
+       unsigned int cur_freq, target_freq;
+       int ret;
+       s32 dyn_power;
+       u32 last_load, normalised_power, static_power;
+       struct gpufreq_cooling_device *gpufreq_cdev = cdev->devdata;
+
+       cur_freq = gpu_dvfs_get_cur_clock();
+       ret = get_static_power(gpufreq_cdev, tz, cur_freq, &static_power);
+       if (ret)
+               return ret;
+
+       dyn_power = power - static_power;
+       dyn_power = dyn_power > 0 ? dyn_power : 0;
+       last_load = gpufreq_cdev->last_load ?: 1;
+       normalised_power = (dyn_power * 100) / last_load;
+       target_freq = gpu_power_to_freq(gpufreq_cdev, normalised_power);
+
+       *state = gpufreq_cooling_get_level(0, target_freq * 1000);
+       if (*state == THERMAL_CSTATE_INVALID) {
+               pr_warn("Failed to convert %dKHz for gpu into a cdev state\n",
+                                    target_freq);
+               return -EINVAL;
+       }
+
+       trace_thermal_power_gpu_limit(target_freq, *state, power);
+       return 0;
+}
+
 /* Bind gpufreq callbacks to thermal cooling device ops */
-static struct thermal_cooling_device_ops const gpufreq_cooling_ops = {
+static struct thermal_cooling_device_ops gpufreq_cooling_ops = {
        .get_max_state = gpufreq_get_max_state,
        .get_cur_state = gpufreq_get_cur_state,
        .set_cur_state = gpufreq_set_cur_state,
@@ -345,6 +648,9 @@ int exynos_gpu_add_notifier(struct notifier_block *n)
  * __gpufreq_cooling_register - helper function to create gpufreq cooling device
  * @np: a valid struct device_node to the cooling device device tree node
  * @clip_gpus: gpumask of gpus where the frequency constraints will happen.
+ * @capacitance: dynamic power coefficient for these gpus
+ * @plat_static_func: function to calculate the static power consumed by these
+ *                    gpus (optional)
  *
  * This interface function registers the gpufreq cooling device with the name
  * "thermal-gpufreq-%x". This api can support multiple instances of gpufreq
@@ -356,39 +662,53 @@ int exynos_gpu_add_notifier(struct notifier_block *n)
  */
 static struct thermal_cooling_device *
 __gpufreq_cooling_register(struct device_node *np,
-                          const struct cpumask *clip_gpus)
+                          const struct cpumask *clip_gpus, u32 capacitance,
+                          get_static_t plat_static_func)
 {
        struct thermal_cooling_device *cool_dev;
-       struct gpufreq_cooling_device *gpufreq_dev = NULL;
+       struct gpufreq_cooling_device *gpufreq_cdev = NULL;
        char dev_name[THERMAL_NAME_LENGTH];
        int ret = 0;
 
-       gpufreq_dev = kzalloc(sizeof(struct gpufreq_cooling_device),
+       gpufreq_cdev = kzalloc(sizeof(struct gpufreq_cooling_device),
                              GFP_KERNEL);
-       if (!gpufreq_dev)
+       if (!gpufreq_cdev)
                return ERR_PTR(-ENOMEM);
 
-       ret = get_idr(&gpufreq_idr, &gpufreq_dev->id);
+       ret = get_idr(&gpufreq_idr, &gpufreq_cdev->id);
        if (ret) {
-               kfree(gpufreq_dev);
+               kfree(gpufreq_cdev);
                return ERR_PTR(-EINVAL);
        }
 
+       if (capacitance) {
+               gpufreq_cooling_ops.get_requested_power =
+                       gpufreq_get_requested_power;
+               gpufreq_cooling_ops.state2power = gpufreq_state2power;
+               gpufreq_cooling_ops.power2state = gpufreq_power2state;
+               gpufreq_cdev->plat_get_static_power = plat_static_func;
+
+               ret = build_dyn_power_table(gpufreq_cdev, capacitance);
+
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+
        snprintf(dev_name, sizeof(dev_name), "thermal-gpufreq-%d",
-                gpufreq_dev->id);
+                gpufreq_cdev->id);
 
-       cool_dev = thermal_of_cooling_device_register(np, dev_name, gpufreq_dev,
+       cool_dev = thermal_of_cooling_device_register(np, dev_name, gpufreq_cdev,
                                                      &gpufreq_cooling_ops);
        if (IS_ERR(cool_dev)) {
-               release_idr(&gpufreq_idr, gpufreq_dev->id);
-               kfree(gpufreq_dev);
+               release_idr(&gpufreq_idr, gpufreq_cdev->id);
+               kfree(gpufreq_cdev);
                return cool_dev;
        }
-       gpufreq_dev->cool_dev = cool_dev;
-       gpufreq_dev->gpufreq_state = 0;
+       gpufreq_cdev->cool_dev = cool_dev;
+       gpufreq_cdev->gpufreq_state = 0;
        mutex_lock(&cooling_gpu_lock);
 
-       gpufreq_dev_count++;
+       gpufreq_cdev_count++;
 
        mutex_unlock(&cooling_gpu_lock);
 
@@ -409,7 +729,7 @@ __gpufreq_cooling_register(struct device_node *np,
 struct thermal_cooling_device *
 gpufreq_cooling_register(const struct cpumask *clip_gpus)
 {
-       return __gpufreq_cooling_register(NULL, clip_gpus);
+       return __gpufreq_cooling_register(NULL, clip_gpus, 0, NULL);
 }
 EXPORT_SYMBOL_GPL(gpufreq_cooling_register);
 
@@ -433,10 +753,78 @@ of_gpufreq_cooling_register(struct device_node *np,
        if (!np)
                return ERR_PTR(-EINVAL);
 
-       return __gpufreq_cooling_register(np, clip_gpus);
+       return __gpufreq_cooling_register(np, clip_gpus, 0, NULL);
 }
 EXPORT_SYMBOL_GPL(of_gpufreq_cooling_register);
 
+/**
+ * gpufreq_power_cooling_register() - create gpufreq cooling device with power extensions
+ * @clip_gpus: gpumask of gpus where the frequency constraints will happen
+ * @capacitance:       dynamic power coefficient for these gpus
+ * @plat_static_func:  function to calculate the static power consumed by these
+ *                     gpus (optional)
+ *
+ * This interface function registers the gpufreq cooling device with
+ * the name "thermal-gpufreq-%x".  This api can support multiple
+ * instances of gpufreq cooling devices.  Using this function, the
+ * cooling device will implement the power extensions by using a
+ * simple gpu power model.  The gpus must have registered their OPPs
+ * using the OPP library.
+ *
+ * An optional @plat_static_func may be provided to calculate the
+ * static power consumed by these gpus.  If the platform's static
+ * power consumption is unknown or negligible, make it NULL.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+gpufreq_power_cooling_register(const struct cpumask *clip_gpus, u32 capacitance,
+                              get_static_t plat_static_func)
+{
+       return __gpufreq_cooling_register(NULL, clip_gpus, capacitance,
+                               plat_static_func);
+}
+EXPORT_SYMBOL(gpufreq_power_cooling_register);
+
+/**
+ * of_gpufreq_power_cooling_register() - create gpufreq cooling device with power extensions
+ * @np:        a valid struct device_node to the cooling device device tree node
+ * @clip_gpus: gpumask of gpus where the frequency constraints will happen
+ * @capacitance:       dynamic power coefficient for these gpus
+ * @plat_static_func:  function to calculate the static power consumed by these
+ *                     gpus (optional)
+ *
+ * This interface function registers the gpufreq cooling device with
+ * the name "thermal-gpufreq-%x".  This api can support multiple
+ * instances of gpufreq cooling devices.  Using this API, the gpufreq
+ * cooling device will be linked to the device tree node provided.
+ * Using this function, the cooling device will implement the power
+ * extensions by using a simple gpu power model.  The gpus must have
+ * registered their OPPs using the OPP library.
+ *
+ * An optional @plat_static_func may be provided to calculate the
+ * static power consumed by these gpus.  If the platform's static
+ * power consumption is unknown or negligible, make it NULL.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+of_gpufreq_power_cooling_register(struct device_node *np,
+                                 const struct cpumask *clip_gpus,
+                                 u32 capacitance,
+                                 get_static_t plat_static_func)
+{
+       if (!np)
+               return ERR_PTR(-EINVAL);
+
+       return __gpufreq_cooling_register(np, clip_gpus, capacitance,
+                               plat_static_func);
+}
+EXPORT_SYMBOL(of_gpufreq_power_cooling_register);
+
+
 /**
  * gpufreq_cooling_unregister - function to remove gpufreq cooling device.
  * @cdev: thermal cooling device pointer.
@@ -445,18 +833,18 @@ EXPORT_SYMBOL_GPL(of_gpufreq_cooling_register);
  */
 void gpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 {
-       struct gpufreq_cooling_device *gpufreq_dev;
+       struct gpufreq_cooling_device *gpufreq_cdev;
 
        if (!cdev)
                return;
 
-       gpufreq_dev = cdev->devdata;
+       gpufreq_cdev = cdev->devdata;
        mutex_lock(&cooling_gpu_lock);
-       gpufreq_dev_count--;
+       gpufreq_cdev_count--;
        mutex_unlock(&cooling_gpu_lock);
 
-       thermal_cooling_device_unregister(gpufreq_dev->cool_dev);
-       release_idr(&gpufreq_idr, gpufreq_dev->id);
-       kfree(gpufreq_dev);
+       thermal_cooling_device_unregister(gpufreq_cdev->cool_dev);
+       release_idr(&gpufreq_idr, gpufreq_cdev->id);
+       kfree(gpufreq_cdev);
 }
 EXPORT_SYMBOL_GPL(gpufreq_cooling_unregister);
index f89ac734d69fd302a3fd7fffbbae6824ef856379..e2a53cf9d2741e466062f54dbc1c294fde42d05f 100644 (file)
@@ -1222,6 +1222,8 @@ static int exynos_gpufreq_cooling_register(struct exynos_tmu_data *data)
        struct device_node *cool_np;
        struct of_phandle_args cooling_spec;
        int ret;
+       const char *governor_name;
+       u32 power_coefficient = 0;
 
        np = of_find_node_by_name(NULL, "thermal-zones");
        if (!np)
@@ -1244,7 +1246,13 @@ static int exynos_gpufreq_cooling_register(struct exynos_tmu_data *data)
 
        cool_np = cooling_spec.np;
 
-       data->cool_dev = of_gpufreq_cooling_register(cool_np, NULL);
+       if (!of_property_read_string(child, "governor", &governor_name)) {
+               if (!strncasecmp(governor_name, "power_allocator", THERMAL_NAME_LENGTH)) {
+                       of_property_read_u32(cool_np, "dynamic-power-coefficient", &power_coefficient);
+               }
+       }
+
+       data->cool_dev = of_gpufreq_power_cooling_register(cool_np, NULL, power_coefficient, NULL);
 
        return ret;
 }
index 1a2435d9b34eb51ea551626b597d71f956bb609e..ff29dde41f7b2fbfe27c041e1741eababb071aae 100755 (executable)
 #include <linux/thermal.h>
 #include <linux/cpumask.h>
 
-#ifdef CONFIG_GPU_THERMAL
+typedef int (*get_static_t)(cpumask_t *cpumask, int interval,
+                           unsigned long voltage, u32 *power);
 
+#ifdef CONFIG_GPU_THERMAL
 /**
  * gpufreq_cooling_register - function to create gpufreq cooling device.
  * @clip_gpus: cpumask of gpus where the frequency constraints will happen
 struct thermal_cooling_device *
 gpufreq_cooling_register(const struct cpumask *clip_gpus);
 
+struct thermal_cooling_device *
+gpufreq_power_cooling_register(const struct cpumask *clip_gpus,
+                              u32 capacitance, get_static_t plat_static_func);
+
 /**
  * of_gpufreq_cooling_register - create gpufreq cooling device based on DT.
  * @np: a valid struct device_node to the cooling device device tree node.
@@ -46,6 +52,12 @@ gpufreq_cooling_register(const struct cpumask *clip_gpus);
 struct thermal_cooling_device *
 of_gpufreq_cooling_register(struct device_node *np,
                            const struct cpumask *clip_gpus);
+
+struct thermal_cooling_device *
+of_gpufreq_power_cooling_register(struct device_node *np,
+                                 const struct cpumask *clip_gpus,
+                                 u32 capacitance,
+                                 get_static_t plat_static_func);
 #else
 static inline struct thermal_cooling_device *
 of_gpufreq_cooling_register(struct device_node *np,
@@ -53,6 +65,15 @@ of_gpufreq_cooling_register(struct device_node *np,
 {
        return NULL;
 }
+
+static inline struct thermal_cooling_device *
+of_gpufreq_power_cooling_register(struct device_node *np,
+                                 const struct cpumask *clip_gpus,
+                                 u32 capacitance,
+                                 get_static_t plat_static_func)
+{
+       return NULL;
+}
 #endif
 
 /**
@@ -68,12 +89,30 @@ gpufreq_cooling_register(const struct cpumask *clip_gpus)
 {
        return NULL;
 }
+
+static inline struct thermal_cooling_device *
+gpufreq_power_cooling_register(const struct cpumask *clip_gpus,
+                              u32 capacitance, get_static_t plat_static_func)
+{
+       return NULL;
+}
+
 static inline struct thermal_cooling_device *
 of_gpufreq_cooling_register(struct device_node *np,
                            const struct cpumask *clip_gpus)
 {
        return NULL;
 }
+
+static inline struct thermal_cooling_device *
+of_gpufreq_power_cooling_register(struct device_node *np,
+                                 const struct cpumask *clip_gpus,
+                                 u32 capacitance,
+                                 get_static_t plat_static_func)
+{
+       return NULL;
+}
+
 static inline
 void gpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 {
@@ -85,5 +124,19 @@ unsigned long gpufreq_cooling_get_level(unsigned int gpu, unsigned int freq)
        return THERMAL_CSTATE_INVALID;
 }
 #endif /* CONFIG_GPU_THERMAL */
-
+#ifdef CONFIG_MALI_DVFS
+extern int gpu_dvfs_get_clock(int level);
+extern int gpu_dvfs_get_voltage(int clock);
+extern int gpu_dvfs_get_step(void);
+extern int gpu_dvfs_get_cur_clock(void);
+extern int gpu_dvfs_get_utilization(void);
+extern int gpu_dvfs_get_max_freq(void);
+#else
+static inline int gpu_dvfs_get_clock(int level) { return 0; }
+static inline int gpu_dvfs_get_voltage(int clock) { return 0; }
+static inline int gpu_dvfs_get_step(void) { return 0; }
+static inline int gpu_dvfs_get_cur_clock(void) { return 0; }
+static inline int gpu_dvfs_get_utilization(void) { return 0; }
+static inline int gpu_dvfs_get_max_freq(void) { return 0; }
+#endif
 #endif /* __GPU_COOLING_H__ */
index 466c09d882ad3e928447f687b39bebb139f0821f..8e1102d43cdcbdd71526e1549e17f7e55aad32c1 100644 (file)
@@ -204,6 +204,52 @@ TRACE_EVENT(thermal_power_devfreq_limit,
                __get_str(type), __entry->freq, __entry->cdev_state,
                __entry->power)
 );
+
+TRACE_EVENT(thermal_power_gpu_get_power,
+       TP_PROTO(unsigned long freq, u32 load, u32 dynamic_power, u32 static_power),
+
+       TP_ARGS(freq, load, dynamic_power, static_power),
+
+       TP_STRUCT__entry(
+               __field(unsigned long, freq          )
+               __field(u32,           load )
+               __field(u32,           dynamic_power )
+               __field(u32,           static_power  )
+       ),
+
+       TP_fast_assign(
+               __entry->freq = freq;
+               __entry->load = load;
+               __entry->dynamic_power = dynamic_power;
+               __entry->static_power = static_power;
+       ),
+
+       TP_printk("freq=%lu load=%d dynamic_power=%d static_power=%d",
+               __entry->freq, __entry->load, __entry->dynamic_power, __entry->static_power)
+);
+
+TRACE_EVENT(thermal_power_gpu_limit,
+       TP_PROTO(unsigned int freq, unsigned long cdev_state, u32 power),
+
+       TP_ARGS(freq, cdev_state, power),
+
+       TP_STRUCT__entry(
+               __field(unsigned int,  freq      )
+               __field(unsigned long, cdev_state)
+               __field(u32,           power     )
+       ),
+
+       TP_fast_assign(
+               __entry->freq = freq;
+               __entry->cdev_state = cdev_state;
+               __entry->power = power;
+       ),
+
+       TP_printk("freq=%u cdev_state=%lu power=%u",
+               __entry->freq, __entry->cdev_state,
+               __entry->power)
+);
+
 #endif /* _TRACE_THERMAL_H */
 
 /* This part must be outside protection */