ARM: tegra: add GPU frequency capping
Hyungwoo Yang [Thu, 1 Aug 2013 19:19:40 +0000 (12:19 -0700)]
add GPU frequency capping into balanced cooling device

In this chagne, GPU frequency capping is done by directly using gk20a driver.
This part will be removed when GPU clock tree is available in Linux Clock
Framework.

Bug 1315460

Change-Id: Ia6927eb3522875417d5a081d98f0ddde4927cfab
Signed-off-by: Hyungwoo Yang <hyungwooy@nvidia.com>
Reviewed-on: http://git-master/r/256975
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>

arch/arm/mach-tegra/include/mach/thermal.h
arch/arm/mach-tegra/tegra3_throttle.c

index 7bddb97..b031859 100644 (file)
@@ -31,7 +31,11 @@ struct tegra_cooling_device {
 #define CPU_THROT_LOW          0 /* lowest throttle freq. only used for CPU */
 
 #ifdef CONFIG_TEGRA_DUAL_CBUS
+#ifdef CONFIG_TEGRA_GPU_DVFS
+#define NUM_OF_CAP_FREQS       5 /* cpu, gpu, c3bus, sclk, emc */
+#else
 #define NUM_OF_CAP_FREQS       5 /* cpu, c2bus, c3bus, sclk, emc */
+#endif
 #else
 #define NUM_OF_CAP_FREQS       4 /* cpu, cbus, sclk, emc */
 #endif
@@ -49,6 +53,13 @@ struct balanced_throttle {
        struct throttle_table *throt_tab;
 };
 
+/* TODO : remove when GPU clock is available in Linux Clock Framework */
+struct gk20a_clk_cap_info {
+       struct gk20a *g;
+       int (*set_cap_thermal)(struct gk20a *g, unsigned long rate);
+       unsigned long (*get_max)(void);
+};
+
 #ifdef CONFIG_TEGRA_THERMAL_THROTTLE
 int tegra_throttle_init(struct mutex *cpu_lock);
 struct thermal_cooling_device *balanced_throttle_register(
@@ -57,6 +68,8 @@ struct thermal_cooling_device *balanced_throttle_register(
 void tegra_throttle_exit(void);
 bool tegra_is_throttling(int *count);
 unsigned long tegra_throttle_governor_speed(unsigned long requested_speed);
+/* TODO : remove when GPU clock is available in Linux Clock Framework */
+int tegra_throttle_gk20a_clk_cap_register(struct gk20a_clk_cap_info *gk20a_clk);
 #else
 static inline int tegra_throttle_init(struct mutex *cpu_lock)
 { return 0; }
@@ -71,6 +84,10 @@ static inline bool tegra_is_throttling(int *count)
 static inline unsigned long tegra_throttle_governor_speed(
        unsigned long requested_speed)
 { return requested_speed; }
+/* TODO : remove when GPU clock is available in Linux Clock Framework */
+static int tegra_throttle_gk20a_clk_cap_register(
+                               struct gk20a_clk_cap_info *gk20a_clk)
+{ return 0; }
 #endif /* CONFIG_TEGRA_THERMAL_THROTTLE */
 
 #endif /* __MACH_THERMAL_H */
index 1ffd3c9..0b42a54 100644 (file)
@@ -33,6 +33,9 @@
 #include "clock.h"
 #include "cpu-tegra.h"
 
+/* TODO : remove when GPU clock is in Linux Clock Framework */
+struct gk20a_clk_cap_info *gk20a_clk_cap;
+
 /* cpu_throttle_lock is tegra_cpu_lock from cpu-tegra.c */
 static struct mutex *cpu_throttle_lock;
 static DEFINE_MUTEX(bthrot_list_lock);
@@ -48,7 +51,13 @@ static struct {
        unsigned long cap_freq;
 } cap_freqs_table[] = {
 #ifdef CONFIG_TEGRA_DUAL_CBUS
-       { .cap_name = "cap.throttle.c2bus" },
+#ifdef CONFIG_TEGRA_GPU_DVFS
+       { .cap_name = "cap.throttle.gpu" },
+#else
+       /* TODO : use c2bus and remove gpu when CONFIG_TEGRA_GPU_DVFS==y */
+       /* { .cap_name = "cap.throttle.c2bus" }, */
+       { .cap_name = "cap.throttle.gpu" },
+#endif
        { .cap_name = "cap.throttle.c3bus" },
 #else
        { .cap_name = "cap.throttle.cbus" },
@@ -138,19 +147,40 @@ static void tegra_throttle_set_cap_clk(struct throttle_table *throt_tab,
                                        int cap_clk_index)
 {
        unsigned long cap_rate, clk_rate;
+       struct nvhost_device_data *pdata;
 
        if (tegra_throttle_init_failed)
                return;
 
        cap_rate = throt_tab->cap_freqs[cap_clk_index];
 
-       if (cap_rate == NO_CAP)
-               clk_rate = clk_get_max_rate(CAP_TBL_CAP_CLK(cap_clk_index-1));
-       else
+       if (cap_rate == NO_CAP) {
+               /* TODO : remove when GPU clock is in Linux Clock Framework */
+               if (strcmp(CAP_TBL_CAP_NAME(cap_clk_index-1),
+                                       "cap.throttle.gpu") != 0)
+                       clk_rate = clk_get_max_rate(
+                                       CAP_TBL_CAP_CLK(cap_clk_index-1));
+               else if (gk20a_clk_cap != NULL)
+                       clk_rate = gk20a_clk_cap->get_max() * 1000000UL / 2;
+
+               /* TODO : use when GPU clock is in Linux Clock Framework */
+               /* clk_rate = */
+               /* clk_get_max_rate(CAP_TBL_CAP_CLK(cap_clk_index-1)); */
+       } else
                clk_rate = cap_rate * 1000UL;
 
        if (CAP_TBL_CAP_FREQ(cap_clk_index-1) != clk_rate) {
-               clk_set_rate(CAP_TBL_CAP_CLK(cap_clk_index-1), clk_rate);
+               /* TODO : remove when GPU clock is in Linux Clock Framework */
+               if (strcmp(CAP_TBL_CAP_NAME(cap_clk_index-1),
+                                       "cap.throttle.gpu") != 0)
+                       clk_set_rate(CAP_TBL_CAP_CLK(cap_clk_index-1),
+                                                               clk_rate);
+               else if (gk20a_clk_cap != NULL)
+                       gk20a_clk_cap->set_cap_thermal(gk20a_clk_cap->g,
+                                               clk_rate / 1000000UL * 2);
+
+               /* TODO : use when GPU clock is in Linux Clock Framework */
+               /* clk_set_rate(CAP_TBL_CAP_CLK(cap_clk_index-1), clk_rate); */
                CAP_TBL_CAP_FREQ(cap_clk_index-1) = clk_rate;
        }
 }
@@ -181,13 +211,7 @@ tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev,
        int num_of_cap_clocks = ARRAY_SIZE(cap_freqs_table);
        unsigned long bthrot_speed;
        struct throttle_table *throt_entry;
-       struct throttle_table cur_throt_freq = {
-#ifdef CONFIG_TEGRA_DUAL_CBUS
-               { NO_CAP, NO_CAP, NO_CAP, NO_CAP, NO_CAP}
-#else
-               { NO_CAP, NO_CAP, NO_CAP, NO_CAP}
-#endif
-       };
+       struct throttle_table cur_throt_freq;
 
        if (cpu_freq_table == NULL)
                return 0;
@@ -201,6 +225,9 @@ tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev,
        direction = bthrot->cur_state >= cur_state;
        bthrot->cur_state = cur_state;
 
+       for (i = 0; i <= num_of_cap_clocks; i++)
+               cur_throt_freq.cap_freqs[i] = NO_CAP;
+
        mutex_lock(&bthrot_list_lock);
        list_for_each_entry(bthrot, &bthrot_list, node) {
                if (bthrot->cur_state) {
@@ -345,6 +372,12 @@ struct thermal_cooling_device *balanced_throttle_register(
        return bthrot->cdev;
 }
 
+/* TODO : remove when GPU clock is available in Linux clock framework */
+int tegra_throttle_gk20a_clk_cap_register(struct gk20a_clk_cap_info *gk20a_clk)
+{
+       gk20a_clk_cap = gk20a_clk;
+}
+
 int __init tegra_throttle_init(struct mutex *cpu_lock)
 {
        int i;
@@ -365,16 +398,23 @@ int __init tegra_throttle_init(struct mutex *cpu_lock)
 #endif
 
        for (i = 0; i < ARRAY_SIZE(cap_freqs_table); i++) {
-               c = tegra_get_clock_by_name(CAP_TBL_CAP_NAME(i));
-               if (!c) {
-                       pr_err("tegra_throttle: cannot get clock %s\n",
-                               CAP_TBL_CAP_NAME(i));
-                       tegra_throttle_init_failed = true;
-                       continue;
-               }
+               /* TODO : need to be modified when GPU clock is available in */
+               /* Linux Clock Framework */
+               if (strcmp(CAP_TBL_CAP_NAME(i),
+                                       "cap.throttle.gpu") != 0) {
+                       c = tegra_get_clock_by_name(CAP_TBL_CAP_NAME(i));
+                       if (!c) {
+                               pr_err("tegra_throttle: cannot get clock %s\n",
+                                       CAP_TBL_CAP_NAME(i));
+                               tegra_throttle_init_failed = true;
+                               continue;
+                       }
 
-               CAP_TBL_CAP_CLK(i) = c;
-               CAP_TBL_CAP_FREQ(i) = clk_get_max_rate(c);
+                       CAP_TBL_CAP_CLK(i) = c;
+                       CAP_TBL_CAP_FREQ(i) = clk_get_max_rate(c);
+               } else if (gk20a_clk_cap != NULL)
+                       CAP_TBL_CAP_FREQ(i) = gk20a_clk_cap->get_max() *
+                                                               1000000UL / 2;
        }
        pr_info("tegra_throttle : init %s\n",
                tegra_throttle_init_failed ? "FAILED" : "passed");