ARM: tegra: cpuquiet: Notify the cpuquiet governor when the driver is busy
Sai Charan Gurrappadi [Wed, 11 Jul 2012 00:33:58 +0000 (17:33 -0700)]
Added generic busy/free notifiers that the driver can invoke to let the
governor know that it cannot process further core online/offline
requests (invoked in our case whenever we switch to the LP cluster).

Change-Id: I5e3f7f28f38806a7f87050e8d0c8d2f2cf7521aa
Signed-off-by: Sai Charan Gurrappadi <sgurrappadi@nvidia.com>
Reviewed-on: http://git-master/r/114807
Reviewed-by: Lokesh Pathak <lpathak@nvidia.com>
Tested-by: Lokesh Pathak <lpathak@nvidia.com>

arch/arm/mach-tegra/cpuquiet.c
drivers/cpuquiet/governor.c
include/linux/cpuquiet.h

index 26adce2..e3a16d0 100644 (file)
@@ -134,7 +134,7 @@ static void apply_core_config(void)
 
 static void tegra_cpuquiet_work_func(struct work_struct *work)
 {
-       bool update_cr_config = false;
+       bool state_changed = false;
 
        mutex_lock(tegra3_cpu_lock);
 
@@ -148,7 +148,7 @@ static void tegra_cpuquiet_work_func(struct work_struct *work)
                                        /*catch-up with governor target speed */
                                        tegra_cpu_set_speed_cap(NULL);
                                        /* process pending core requests*/
-                                       update_cr_config = true;
+                                       state_changed = true;
                                }
                        }
                        break;
@@ -159,6 +159,7 @@ static void tegra_cpuquiet_work_func(struct work_struct *work)
                                if (!clk_set_parent(cpu_clk, cpu_lp_clk)) {
                                        /*catch-up with governor target speed*/
                                        tegra_cpu_set_speed_cap(NULL);
+                                       state_changed = true;
                                }
                        }
                        break;
@@ -169,8 +170,12 @@ static void tegra_cpuquiet_work_func(struct work_struct *work)
 
        mutex_unlock(tegra3_cpu_lock);
 
-       if (update_cr_config)
+       if (state_changed && cpq_state == TEGRA_CPQ_SWITCH_TO_LP) {
+               cpuquiet_device_busy();
+       } else if (state_changed && cpq_state == TEGRA_CPQ_SWITCH_TO_G) {
                apply_core_config();
+               cpuquiet_device_free();
+       }
 }
 
 static void min_max_constraints_workfunc(struct work_struct *work)
@@ -212,6 +217,8 @@ static void min_max_constraints_workfunc(struct work_struct *work)
 
 static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
 {
+       bool g_cluster = false;
+
        mutex_lock(tegra3_cpu_lock);
 
        if ((n >= 1) && is_lp_cluster()) {
@@ -221,6 +228,7 @@ static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
                tegra_update_cpu_speed(speed);
 
                clk_set_parent(cpu_clk, cpu_g_clk);
+               g_cluster = true;
        }
 
        tegra_cpu_set_speed_cap(NULL);
@@ -228,6 +236,9 @@ static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
 
        schedule_work(&minmax_work);
 
+       if (g_cluster)
+               cpuquiet_device_free();
+
        return NOTIFY_OK;
 }
 
@@ -253,6 +264,7 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq, bool suspend)
                /* Switch to G-mode if suspend rate is high enough */
                if (is_lp_cluster() && (cpu_freq >= idle_bottom_freq)) {
                        clk_set_parent(cpu_clk, cpu_g_clk);
+                       cpuquiet_device_free();
                }
                return;
        }
@@ -306,11 +318,13 @@ static void enable_callback(struct cpuquiet_attribute *attr)
                mutex_unlock(tegra3_cpu_lock);
                cancel_delayed_work_sync(&cpuquiet_work);
                pr_info("Tegra cpuquiet clusterswitch disabled\n");
+               cpuquiet_device_busy();
                mutex_lock(tegra3_cpu_lock);
        } else if (enable && cpq_state == TEGRA_CPQ_DISABLED) {
                cpq_state = TEGRA_CPQ_IDLE;
                pr_info("Tegra cpuquiet clusterswitch enabled\n");
                tegra_cpu_set_speed_cap(NULL);
+               cpuquiet_device_free();
        }
 
        mutex_unlock(tegra3_cpu_lock);
index 7895fcc..176ba3b 100644 (file)
@@ -100,3 +100,17 @@ void cpuquiet_unregister_governor(struct cpuquiet_governor *gov)
        list_del(&gov->governor_list);
        mutex_unlock(&cpuquiet_lock);
 }
+
+void cpuquiet_device_busy(void)
+{
+       if (cpuquiet_curr_governor &&
+                       cpuquiet_curr_governor->device_busy_notification)
+               cpuquiet_curr_governor->device_busy_notification();
+}
+
+void cpuquiet_device_free(void)
+{
+       if (cpuquiet_curr_governor &&
+                       cpuquiet_curr_governor->device_free_notification)
+               cpuquiet_curr_governor->device_free_notification();
+}
index fe5a037..5558c01 100644 (file)
@@ -30,6 +30,8 @@ struct cpuquiet_governor {
        int (*start)            (void);
        void (*stop)            (void);
        int (*store_active)     (unsigned int cpu, bool active);
+       void (*device_free_notification) (void);
+       void (*device_busy_notification) (void);
        struct module           *owner;
 };
 
@@ -47,6 +49,8 @@ extern int cpuquiet_register_driver(struct cpuquiet_driver *drv);
 extern void cpuquiet_unregister_driver(struct cpuquiet_driver *drv);
 extern int cpuquiet_add_group(struct attribute_group *attrs);
 extern void cpuquiet_remove_group(struct attribute_group *attrs);
+extern void cpuquiet_device_busy(void);
+extern void cpuquiet_device_free(void);
 int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type,
                                char *name);
 extern unsigned int nr_cluster_ids;