Merge remote branch 'tegra/android-tegra-2.6.36' into android-tegra-moto-2.6.36
[firefly-linux-kernel-4.4.55.git] / drivers / video / tegra / host / nvhost_acm.c
1 /*
2  * drivers/video/tegra/host/nvhost_acm.c
3  *
4  * Tegra Graphics Host Automatic Clock Management
5  *
6  * Copyright (c) 2010, NVIDIA Corporation.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22
23 #include "nvhost_acm.h"
24 #include <linux/string.h>
25 #include <linux/sched.h>
26 #include <linux/err.h>
27 #include <linux/device.h>
28 #include <mach/powergate.h>
29 #include <mach/clk.h>
30
31 #include "dev.h"
32
33 #define ACM_TIMEOUT 1*HZ
34
35 #define DISABLE_3D_POWERGATING
36 #define DISABLE_MPE_POWERGATING
37
38 void nvhost_module_busy(struct nvhost_module *mod)
39 {
40         mutex_lock(&mod->lock);
41         cancel_delayed_work(&mod->powerdown);
42         if ((atomic_inc_return(&mod->refcount) == 1) && !mod->powered) {
43                 if (mod->parent)
44                         nvhost_module_busy(mod->parent);
45                 if (mod->powergate_id != -1) {
46                         BUG_ON(mod->num_clks != 1);
47                         tegra_powergate_sequence_power_up(
48                                 mod->powergate_id, mod->clk[0]);
49                 } else {
50                         int i;
51                         for (i = 0; i < mod->num_clks; i++)
52                                 clk_enable(mod->clk[i]);
53                 }
54                 if (mod->func)
55                         mod->func(mod, NVHOST_POWER_ACTION_ON);
56                 mod->powered = true;
57         }
58         mutex_unlock(&mod->lock);
59 }
60
61 static void powerdown_handler(struct work_struct *work)
62 {
63         struct nvhost_module *mod;
64         mod = container_of(to_delayed_work(work), struct nvhost_module, powerdown);
65         mutex_lock(&mod->lock);
66         if ((atomic_read(&mod->refcount) == 0) && mod->powered) {
67                 int i;
68                 if (mod->func)
69                         mod->func(mod, NVHOST_POWER_ACTION_OFF);
70                 for (i = 0; i < mod->num_clks; i++) {
71                         clk_disable(mod->clk[i]);
72                 }
73                 if (mod->powergate_id != -1) {
74                         tegra_periph_reset_assert(mod->clk[0]);
75                         tegra_powergate_power_off(mod->powergate_id);
76                 }
77                 mod->powered = false;
78                 if (mod->parent)
79                         nvhost_module_idle(mod->parent);
80         }
81         mutex_unlock(&mod->lock);
82 }
83
84 void nvhost_module_idle_mult(struct nvhost_module *mod, int refs)
85 {
86         bool kick = false;
87
88         mutex_lock(&mod->lock);
89         if (atomic_sub_return(refs, &mod->refcount) == 0) {
90                 BUG_ON(!mod->powered);
91                 schedule_delayed_work(&mod->powerdown, ACM_TIMEOUT);
92                 kick = true;
93         }
94         mutex_unlock(&mod->lock);
95
96         if (kick)
97                 wake_up(&mod->idle);
98 }
99
100 static const char *get_module_clk_id(const char *module, int index)
101 {
102         if (index == 1 && strcmp(module, "gr2d") == 0)
103                 return "epp";
104         else if (index == 2 && strcmp(module, "gr2d") == 0)
105                 return "emc";
106         else if (index == 1 && strcmp(module, "gr3d") == 0)
107                 return "emc";
108         else if (index == 1 && strcmp(module, "mpe") == 0)
109                 return "emc";
110         else if (index == 0)
111                 return module;
112         return NULL;
113 }
114
115 static int get_module_powergate_id(const char *module)
116 {
117         if (strcmp(module, "gr3d") == 0)
118                 return TEGRA_POWERGATE_3D;
119         else if (strcmp(module, "mpe") == 0)
120                 return TEGRA_POWERGATE_MPE;
121         return -1;
122 }
123
124 int nvhost_module_init(struct nvhost_module *mod, const char *name,
125                 nvhost_modulef func, struct nvhost_module *parent,
126                 struct device *dev)
127 {
128         int i = 0;
129         mod->name = name;
130
131         while (i < NVHOST_MODULE_MAX_CLOCKS) {
132                 long rate;
133                 mod->clk[i] = clk_get(dev, get_module_clk_id(name, i));
134                 if (IS_ERR_OR_NULL(mod->clk[i]))
135                         break;
136                 rate = clk_round_rate(mod->clk[i], UINT_MAX);
137                 if (rate < 0) {
138                         pr_err("%s: can't get maximum rate for %s\n",
139                                 __func__, name);
140                         break;
141                 }
142                 if (rate != clk_get_rate(mod->clk[i])) {
143                         clk_set_rate(mod->clk[i], rate);
144                 }
145                 i++;
146         }
147
148         mod->num_clks = i;
149         mod->func = func;
150         mod->parent = parent;
151         mod->powered = false;
152         mod->powergate_id = get_module_powergate_id(name);
153
154 #ifdef DISABLE_3D_POWERGATING
155         /*
156          * It is possible for the 3d block to generate an invalid memory
157          * request during the power up sequence in some cases.  Workaround
158          * is to disable 3d block power gating.
159          */
160         if (mod->powergate_id == TEGRA_POWERGATE_3D) {
161                 tegra_powergate_sequence_power_up(mod->powergate_id,
162                         mod->clk[0]);
163                 clk_disable(mod->clk[0]);
164                 mod->powergate_id = -1;
165         }
166 #endif
167
168 #ifdef DISABLE_MPE_POWERGATING
169         /*
170          * Disable power gating for MPE as it seems to cause issues with
171          * camera record stress tests when run in loop.
172          */
173         if (mod->powergate_id == TEGRA_POWERGATE_MPE) {
174                 tegra_powergate_sequence_power_up(mod->powergate_id,
175                         mod->clk[0]);
176                 clk_disable(mod->clk[0]);
177                 mod->powergate_id = -1;
178         }
179 #endif
180
181         mutex_init(&mod->lock);
182         init_waitqueue_head(&mod->idle);
183         INIT_DELAYED_WORK(&mod->powerdown, powerdown_handler);
184
185         return 0;
186 }
187
188 static int is_module_idle(struct nvhost_module *mod)
189 {
190         int count;
191         mutex_lock(&mod->lock);
192         count = atomic_read(&mod->refcount);
193         mutex_unlock(&mod->lock);
194         return (count == 0);
195 }
196
197 void tegra_clk_dump(void);
198
199 void nvhost_module_suspend(struct nvhost_module *mod)
200 {
201         int ret;
202
203         ret = wait_event_timeout(mod->idle, is_module_idle(mod),
204                            ACM_TIMEOUT + msecs_to_jiffies(500));
205         if (ret == 0) {
206                 tegra_clk_dump();
207                 nvhost_debug_dump();
208         }
209         flush_delayed_work(&mod->powerdown);
210         BUG_ON(mod->powered);
211 }
212
213 void nvhost_module_deinit(struct nvhost_module *mod)
214 {
215         int i;
216         nvhost_module_suspend(mod);
217         for (i = 0; i < mod->num_clks; i++)
218                 clk_put(mod->clk[i]);
219 }