Merge branch 'android-2.6.36' into android-tegra-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 #define ACM_TIMEOUT 1*HZ
32
33 void nvhost_module_busy(struct nvhost_module *mod)
34 {
35         mutex_lock(&mod->lock);
36         cancel_delayed_work(&mod->powerdown);
37         if ((atomic_inc_return(&mod->refcount) == 1) && !mod->powered) {
38                 if (mod->parent)
39                         nvhost_module_busy(mod->parent);
40                 if (mod->powergate_id != -1) {
41                         BUG_ON(mod->num_clks != 1);
42                         tegra_powergate_sequence_power_up(
43                                 mod->powergate_id, mod->clk[0]);
44                 } else {
45                         int i;
46                         for (i = 0; i < mod->num_clks; i++)
47                                 clk_enable(mod->clk[i]);
48                 }
49                 if (mod->func)
50                         mod->func(mod, NVHOST_POWER_ACTION_ON);
51                 mod->powered = true;
52         }
53         mutex_unlock(&mod->lock);
54 }
55
56 static void powerdown_handler(struct work_struct *work)
57 {
58         struct nvhost_module *mod;
59         mod = container_of(to_delayed_work(work), struct nvhost_module, powerdown);
60         mutex_lock(&mod->lock);
61         if ((atomic_read(&mod->refcount) == 0) && mod->powered) {
62                 int i;
63                 if (mod->func)
64                         mod->func(mod, NVHOST_POWER_ACTION_OFF);
65                 for (i = 0; i < mod->num_clks; i++) {
66                         clk_disable(mod->clk[i]);
67                 }
68                 if (mod->powergate_id != -1) {
69                         tegra_periph_reset_assert(mod->clk[0]);
70                         tegra_powergate_power_off(mod->powergate_id);
71                 }
72                 mod->powered = false;
73                 if (mod->parent)
74                         nvhost_module_idle(mod->parent);
75         }
76         mutex_unlock(&mod->lock);
77 }
78
79 void nvhost_module_idle_mult(struct nvhost_module *mod, int refs)
80 {
81         bool kick = false;
82
83         mutex_lock(&mod->lock);
84         if (atomic_sub_return(refs, &mod->refcount) == 0) {
85                 BUG_ON(!mod->powered);
86                 schedule_delayed_work(&mod->powerdown, ACM_TIMEOUT);
87                 kick = true;
88         }
89         mutex_unlock(&mod->lock);
90
91         if (kick)
92                 wake_up(&mod->idle);
93 }
94
95 static const char *get_module_clk_id(const char *module, int index)
96 {
97         if (index == 1 && strcmp(module, "gr2d") == 0)
98                 return "epp";
99         else if (index == 0)
100                 return module;
101         return NULL;
102 }
103
104 static int get_module_powergate_id(const char *module)
105 {
106         if (strcmp(module, "gr3d") == 0)
107                 return TEGRA_POWERGATE_3D;
108         else if (strcmp(module, "mpe") == 0)
109                 return TEGRA_POWERGATE_MPE;
110         return -1;
111 }
112
113 int nvhost_module_init(struct nvhost_module *mod, const char *name,
114                 nvhost_modulef func, struct nvhost_module *parent,
115                 struct device *dev)
116 {
117         int i = 0;
118         mod->name = name;
119
120         while (i < NVHOST_MODULE_MAX_CLOCKS) {
121                 long rate;
122                 mod->clk[i] = clk_get(dev, get_module_clk_id(name, i));
123                 if (IS_ERR_OR_NULL(mod->clk[i]))
124                         break;
125                 rate = clk_round_rate(mod->clk[i], UINT_MAX);
126                 if (rate < 0) {
127                         pr_err("%s: can't get maximum rate for %s\n",
128                                 __func__, name);
129                         break;
130                 }
131                 if (rate != clk_get_rate(mod->clk[i])) {
132                         clk_set_rate(mod->clk[i], rate);
133                 }
134                 i++;
135         }
136
137         mod->num_clks = i;
138         mod->func = func;
139         mod->parent = parent;
140         mod->powered = false;
141         mod->powergate_id = get_module_powergate_id(name);
142         mutex_init(&mod->lock);
143         init_waitqueue_head(&mod->idle);
144         INIT_DELAYED_WORK(&mod->powerdown, powerdown_handler);
145
146         return 0;
147 }
148
149 static int is_module_idle(struct nvhost_module *mod)
150 {
151         int count;
152         mutex_lock(&mod->lock);
153         count = atomic_read(&mod->refcount);
154         mutex_unlock(&mod->lock);
155         return (count == 0);
156 }
157
158 void nvhost_module_suspend(struct nvhost_module *mod)
159 {
160         wait_event(mod->idle, is_module_idle(mod));
161         flush_delayed_work(&mod->powerdown);
162         BUG_ON(mod->powered);
163 }
164
165 void nvhost_module_deinit(struct nvhost_module *mod)
166 {
167         int i;
168         nvhost_module_suspend(mod);
169         for (i = 0; i < mod->num_clks; i++)
170                 clk_put(mod->clk[i]);
171 }