2 * drivers/video/tegra/host/nvhost_acm.c
4 * Tegra Graphics Host Automatic Clock Management
6 * Copyright (c) 2010, NVIDIA Corporation.
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.
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
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.
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>
33 #define ACM_TIMEOUT 1*HZ
35 #define DISABLE_3D_POWERGATING
36 #define DISABLE_MPE_POWERGATING
38 void nvhost_module_busy(struct nvhost_module *mod)
40 mutex_lock(&mod->lock);
41 cancel_delayed_work(&mod->powerdown);
42 if ((atomic_inc_return(&mod->refcount) == 1) && !mod->powered) {
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]);
51 for (i = 0; i < mod->num_clks; i++)
52 clk_enable(mod->clk[i]);
55 mod->func(mod, NVHOST_POWER_ACTION_ON);
58 mutex_unlock(&mod->lock);
61 static void powerdown_handler(struct work_struct *work)
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) {
69 mod->func(mod, NVHOST_POWER_ACTION_OFF);
70 for (i = 0; i < mod->num_clks; i++) {
71 clk_disable(mod->clk[i]);
73 if (mod->powergate_id != -1) {
74 tegra_periph_reset_assert(mod->clk[0]);
75 tegra_powergate_power_off(mod->powergate_id);
79 nvhost_module_idle(mod->parent);
81 mutex_unlock(&mod->lock);
84 void nvhost_module_idle_mult(struct nvhost_module *mod, int refs)
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);
94 mutex_unlock(&mod->lock);
100 static const char *get_module_clk_id(const char *module, int index)
102 if (index == 1 && strcmp(module, "gr2d") == 0)
104 else if (index == 2 && strcmp(module, "gr2d") == 0)
106 else if (index == 1 && strcmp(module, "gr3d") == 0)
108 else if (index == 1 && strcmp(module, "mpe") == 0)
115 static int get_module_powergate_id(const char *module)
117 if (strcmp(module, "gr3d") == 0)
118 return TEGRA_POWERGATE_3D;
119 else if (strcmp(module, "mpe") == 0)
120 return TEGRA_POWERGATE_MPE;
124 int nvhost_module_init(struct nvhost_module *mod, const char *name,
125 nvhost_modulef func, struct nvhost_module *parent,
131 while (i < NVHOST_MODULE_MAX_CLOCKS) {
133 mod->clk[i] = clk_get(dev, get_module_clk_id(name, i));
134 if (IS_ERR_OR_NULL(mod->clk[i]))
136 rate = clk_round_rate(mod->clk[i], UINT_MAX);
138 pr_err("%s: can't get maximum rate for %s\n",
142 if (rate != clk_get_rate(mod->clk[i])) {
143 clk_set_rate(mod->clk[i], rate);
150 mod->parent = parent;
151 mod->powered = false;
152 mod->powergate_id = get_module_powergate_id(name);
154 #ifdef DISABLE_3D_POWERGATING
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.
160 if (mod->powergate_id == TEGRA_POWERGATE_3D) {
161 tegra_powergate_sequence_power_up(mod->powergate_id,
163 clk_disable(mod->clk[0]);
164 mod->powergate_id = -1;
168 #ifdef DISABLE_MPE_POWERGATING
170 * Disable power gating for MPE as it seems to cause issues with
171 * camera record stress tests when run in loop.
173 if (mod->powergate_id == TEGRA_POWERGATE_MPE) {
174 tegra_powergate_sequence_power_up(mod->powergate_id,
176 clk_disable(mod->clk[0]);
177 mod->powergate_id = -1;
181 mutex_init(&mod->lock);
182 init_waitqueue_head(&mod->idle);
183 INIT_DELAYED_WORK(&mod->powerdown, powerdown_handler);
188 static int is_module_idle(struct nvhost_module *mod)
191 mutex_lock(&mod->lock);
192 count = atomic_read(&mod->refcount);
193 mutex_unlock(&mod->lock);
197 void tegra_clk_dump(void);
199 void nvhost_module_suspend(struct nvhost_module *mod)
203 ret = wait_event_timeout(mod->idle, is_module_idle(mod),
204 ACM_TIMEOUT + msecs_to_jiffies(500));
209 flush_delayed_work(&mod->powerdown);
210 BUG_ON(mod->powered);
213 void nvhost_module_deinit(struct nvhost_module *mod)
216 nvhost_module_suspend(mod);
217 for (i = 0; i < mod->num_clks; i++)
218 clk_put(mod->clk[i]);