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>
31 #define ACM_TIMEOUT 1*HZ
33 void nvhost_module_busy(struct nvhost_module *mod)
35 mutex_lock(&mod->lock);
36 cancel_delayed_work(&mod->powerdown);
37 if ((atomic_inc_return(&mod->refcount) == 1) && !mod->powered) {
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]);
46 for (i = 0; i < mod->num_clks; i++)
47 clk_enable(mod->clk[i]);
50 mod->func(mod, NVHOST_POWER_ACTION_ON);
53 mutex_unlock(&mod->lock);
56 static void powerdown_handler(struct work_struct *work)
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) {
64 mod->func(mod, NVHOST_POWER_ACTION_OFF);
65 for (i = 0; i < mod->num_clks; i++) {
66 clk_disable(mod->clk[i]);
68 if (mod->powergate_id != -1) {
69 tegra_periph_reset_assert(mod->clk[0]);
70 tegra_powergate_power_off(mod->powergate_id);
74 nvhost_module_idle(mod->parent);
76 mutex_unlock(&mod->lock);
79 void nvhost_module_idle_mult(struct nvhost_module *mod, int refs)
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);
89 mutex_unlock(&mod->lock);
95 static const char *get_module_clk_id(const char *module, int index)
97 if (index == 1 && strcmp(module, "gr2d") == 0)
104 static int get_module_powergate_id(const char *module)
106 if (strcmp(module, "gr3d") == 0)
107 return TEGRA_POWERGATE_3D;
108 else if (strcmp(module, "mpe") == 0)
109 return TEGRA_POWERGATE_MPE;
113 int nvhost_module_init(struct nvhost_module *mod, const char *name,
114 nvhost_modulef func, struct nvhost_module *parent,
120 while (i < NVHOST_MODULE_MAX_CLOCKS) {
122 mod->clk[i] = clk_get(dev, get_module_clk_id(name, i));
123 if (IS_ERR_OR_NULL(mod->clk[i]))
125 rate = clk_round_rate(mod->clk[i], UINT_MAX);
127 pr_err("%s: can't get maximum rate for %s\n",
131 if (rate != clk_get_rate(mod->clk[i])) {
132 clk_set_rate(mod->clk[i], rate);
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);
149 static int is_module_idle(struct nvhost_module *mod)
152 mutex_lock(&mod->lock);
153 count = atomic_read(&mod->refcount);
154 mutex_unlock(&mod->lock);
158 void nvhost_module_suspend(struct nvhost_module *mod)
160 wait_event(mod->idle, is_module_idle(mod));
161 flush_delayed_work(&mod->powerdown);
162 BUG_ON(mod->powered);
165 void nvhost_module_deinit(struct nvhost_module *mod)
168 nvhost_module_suspend(mod);
169 for (i = 0; i < mod->num_clks; i++)
170 clk_put(mod->clk[i]);