* Copyright (C) 2018 Samsung
*/
+#define pr_fmt(fmt) "SCHED_POWER: " fmt
#include <linux/sched.h>
#include <linux/thermal.h>
/////////////////thermal governor////////////////////////
+struct _thermal_zone {
+ struct thermal_zone_device *tz;
+ bool single_cooling_dev;
+ struct list_head node;
+};
+
+struct _cooling_dev {
+ struct thermal_cooling_device *cdev;
+ struct list_head node;
+ int max_single_state; /* max state (0-highest) which means freqency */
+ int min_sum_single_idle; /* minimum sum of idle calculated for 'single'
+ zone. Other zones should not go bellow this
+ value for the same 'state' (frequency) */
+ bool cpu_dev;
+ unsigned long int cpus[0];
+};
+
+static LIST_HEAD(tz_list);
+static LIST_HEAD(cdev_list);
+static DEFINE_MUTEX(tz_list_lock);
+static DEFINE_MUTEX(cdev_list_lock);
+
static int cdev_get_min_power(struct thermal_cooling_device *cdev,
u64 *min_power)
{
return 0;
}
+static struct _thermal_zone *find_zone(struct thermal_zone_device *tz)
+{
+ struct _thermal_zone *zone;
+
+ mutex_lock(&tz_list_lock);
+ list_for_each_entry(zone, &tz_list, node) {
+ if (zone->tz == tz) {
+ mutex_unlock(&tz_list_lock);
+ return zone;
+ }
+ }
+ mutex_unlock(&tz_list_lock);
+
+ return NULL;
+}
+static int inject_more_idle(struct _thermal_zone *zone)
+{
+
+ return 0;
+}
+
+static int throttle_single_cdev(struct _thermal_zone *zone)
+{
+
+
+ return 0;
+}
+
static int sched_power_gov_throttle(struct thermal_zone_device *tz, int trip)
{
+ struct thermal_cooling_device *cdev;
+ struct thermal_instance *inst;
+ u32 dev_power;
+ struct _thermal_zone *zone;
+ int ret;
+
+ zone = find_zone(tz);
+ if (!zone)
+ return -EINVAL;
+
+ if (zone->single_cooling_dev) {
+ /* ret = calc_power_budget(tz); */
+ ret = inject_more_idle(zone);
+ if (ret) {
+ throttle_single_cdev(zone);
+ power_actor_set_power(cdev, inst, dev_power);
+ }
+ } else {
+
+ }
+
return 0;
}
+static inline bool is_cpufreq_cooling(struct thermal_cooling_device *cdev)
+{
+ const char dev_name[] = "thermal-cpufreq-";
+
+ return strncmp(dev_name, cdev->type, strlen(dev_name)) ?
+ false : true;
+}
+
+static void cleanup_percpu_cooling_dev(struct _cooling_dev *cooling)
+{
+ int i;
+ struct cpumask *cpumask;
+ struct cpu_power *cpower;
+
+ cpumask = to_cpumask(cooling->cpus);
+ for_each_cpu(i, cpumask) {
+ cpower = (&per_cpu(cpu_power, i));
+ raw_spin_lock(&cpower->update_lock);
+ cpower->cooling = NULL;
+ raw_spin_unlock(&cpower->update_lock);
+ }
+ kfree(cooling);
+}
+
static int sched_power_gov_bind(struct thermal_zone_device *tz)
{
struct thermal_instance *inst;
+ struct _thermal_zone *zone;
+ struct _cooling_dev *cooling, *prev_cooling;
+ struct thermal_cooling_device *cdev;
+ int i = 0;
+ int cpu;
+ int cpus_size;
+ struct cpumask *cpumask;
+ struct cpu_power *cpower;
+
+ zone = kzalloc(sizeof(*zone), GFP_KERNEL);
+ if (!zone)
+ return -ENOMEM;
+ mutex_lock(&cdev_list_lock);
list_for_each_entry(inst, &tz->thermal_instances, tz_node) {
+ cdev = inst->cdev;
+ if (is_cpufreq_cooling(cdev))
+ cpus_size = cpumask_size();
+ else
+ cpus_size = 0;
+
+ cooling = kzalloc(sizeof(*cooling) + cpus_size, GFP_KERNEL);
+ if (!cooling)
+ goto cleanup;
+
+ cooling->cpu_dev = !!cpus_size;
+ cooling->cdev = cdev;
+ if (cooling->cpu_dev) {
+ cpumask = to_cpumask(cooling->cpus);
+
+ cpufreq_cooling_copy_cpumask(cdev, cpumask);
+
+ for_each_cpu(cpu, cpumask) {
+ cpower = (&per_cpu(cpu_power, cpu));
+ raw_spin_lock(&cpower->update_lock);
+ cpower->cooling = cooling;
+ raw_spin_unlock(&cpower->update_lock);
+ }
+ }
+ list_add(&cooling->node, &cdev_list);
+ pr_info("registred new cooling device for CPUs\n");
+ prev_cooling = cooling;
+ i++;
}
+ mutex_unlock(&cdev_list_lock);
+
+ zone->single_cooling_dev = (i == 1 ? true : false);
+ zone->tz = tz;
+
+ mutex_lock(&tz_list_lock);
+ list_add(&zone->node, &tz_list);
+ mutex_unlock(&tz_list_lock);
return 0;
+
+cleanup:
+ list_for_each_entry_reverse(cooling, &prev_cooling->node, node) {
+ if (i-- == 0)
+ break;
+ list_del(&prev_cooling->node);
+ cleanup_percpu_cooling_dev(prev_cooling);
+ prev_cooling = cooling;
+ }
+ mutex_unlock(&cdev_list_lock);
+ kfree(zone);
+
+ return -ENOMEM;
}
static void sched_power_gov_unbind(struct thermal_zone_device *tz)