blob: 9b828f2ed2b62c9b501808b1bef9b8bf834a7bc8 [file] [log] [blame]
Colin Crossce1e3262010-05-24 17:07:46 -07001/*
2 * drivers/powergate/tegra-powergate.c
3 *
4 * Copyright (c) 2010 Google, Inc
5 *
6 * Author:
7 * Colin Cross <ccross@google.com>
8 *
9 * This software is licensed under the terms of the GNU General Public
10 * License version 2, as published by the Free Software Foundation, and
11 * may be copied, distributed, and modified under those terms.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 */
19
20#include <linux/kernel.h>
21#include <linux/clk.h>
22#include <linux/debugfs.h>
23#include <linux/delay.h>
24#include <linux/err.h>
25#include <linux/init.h>
26#include <linux/io.h>
27#include <linux/seq_file.h>
28#include <linux/spinlock.h>
29
30#include <mach/clk.h>
31#include <mach/iomap.h>
32#include <mach/powergate.h>
33
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +020034#include "fuse.h"
35
Colin Crossce1e3262010-05-24 17:07:46 -070036#define PWRGATE_TOGGLE 0x30
37#define PWRGATE_TOGGLE_START (1 << 8)
38
39#define REMOVE_CLAMPING 0x34
40
41#define PWRGATE_STATUS 0x38
42
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +020043static int tegra_num_powerdomains;
44
Colin Crossce1e3262010-05-24 17:07:46 -070045static DEFINE_SPINLOCK(tegra_powergate_lock);
46
47static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
48
49static u32 pmc_read(unsigned long reg)
50{
51 return readl(pmc + reg);
52}
53
54static void pmc_write(u32 val, unsigned long reg)
55{
56 writel(val, pmc + reg);
57}
58
59static int tegra_powergate_set(int id, bool new_state)
60{
61 bool status;
62 unsigned long flags;
63
64 spin_lock_irqsave(&tegra_powergate_lock, flags);
65
66 status = pmc_read(PWRGATE_STATUS) & (1 << id);
67
68 if (status == new_state) {
69 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
70 return -EINVAL;
71 }
72
73 pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
74
75 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
76
77 return 0;
78}
79
80int tegra_powergate_power_on(int id)
81{
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +020082 if (id < 0 || id >= tegra_num_powerdomains)
Colin Crossce1e3262010-05-24 17:07:46 -070083 return -EINVAL;
84
85 return tegra_powergate_set(id, true);
86}
87
88int tegra_powergate_power_off(int id)
89{
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +020090 if (id < 0 || id >= tegra_num_powerdomains)
Colin Crossce1e3262010-05-24 17:07:46 -070091 return -EINVAL;
92
93 return tegra_powergate_set(id, false);
94}
95
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +020096static int tegra_powergate_is_powered(int id)
Colin Crossce1e3262010-05-24 17:07:46 -070097{
98 u32 status;
99
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +0200100 if (id < 0 || id >= tegra_num_powerdomains)
101 return -EINVAL;
Colin Crossce1e3262010-05-24 17:07:46 -0700102
103 status = pmc_read(PWRGATE_STATUS) & (1 << id);
104 return !!status;
105}
106
107int tegra_powergate_remove_clamping(int id)
108{
109 u32 mask;
110
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +0200111 if (id < 0 || id >= tegra_num_powerdomains)
Colin Crossce1e3262010-05-24 17:07:46 -0700112 return -EINVAL;
113
114 /*
115 * Tegra 2 has a bug where PCIE and VDE clamping masks are
116 * swapped relatively to the partition ids
117 */
118 if (id == TEGRA_POWERGATE_VDEC)
119 mask = (1 << TEGRA_POWERGATE_PCIE);
120 else if (id == TEGRA_POWERGATE_PCIE)
121 mask = (1 << TEGRA_POWERGATE_VDEC);
122 else
123 mask = (1 << id);
124
125 pmc_write(mask, REMOVE_CLAMPING);
126
127 return 0;
128}
129
130/* Must be called with clk disabled, and returns with clk enabled */
131int tegra_powergate_sequence_power_up(int id, struct clk *clk)
132{
133 int ret;
134
135 tegra_periph_reset_assert(clk);
136
137 ret = tegra_powergate_power_on(id);
138 if (ret)
139 goto err_power;
140
141 ret = clk_enable(clk);
142 if (ret)
143 goto err_clk;
144
145 udelay(10);
146
147 ret = tegra_powergate_remove_clamping(id);
148 if (ret)
149 goto err_clamp;
150
151 udelay(10);
152 tegra_periph_reset_deassert(clk);
153
154 return 0;
155
156err_clamp:
157 clk_disable(clk);
158err_clk:
159 tegra_powergate_power_off(id);
160err_power:
161 return ret;
162}
163
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +0200164int __init tegra_powergate_init(void)
165{
166 switch (tegra_chip_id) {
167 case TEGRA20:
168 tegra_num_powerdomains = 7;
169 break;
170 default:
171 /* Unknown Tegra variant. Disable powergating */
172 tegra_num_powerdomains = 0;
173 break;
174 }
175
176 return 0;
177}
178arch_initcall(tegra_powergate_init);
179
Colin Crossce1e3262010-05-24 17:07:46 -0700180#ifdef CONFIG_DEBUG_FS
181
182static const char * const powergate_name[] = {
183 [TEGRA_POWERGATE_CPU] = "cpu",
184 [TEGRA_POWERGATE_3D] = "3d",
185 [TEGRA_POWERGATE_VENC] = "venc",
186 [TEGRA_POWERGATE_VDEC] = "vdec",
187 [TEGRA_POWERGATE_PCIE] = "pcie",
188 [TEGRA_POWERGATE_L2] = "l2",
189 [TEGRA_POWERGATE_MPE] = "mpe",
190};
191
192static int powergate_show(struct seq_file *s, void *data)
193{
194 int i;
195
196 seq_printf(s, " powergate powered\n");
197 seq_printf(s, "------------------\n");
198
Peter De Schrijver8f5d6f12012-02-10 01:47:46 +0200199 for (i = 0; i < tegra_num_powerdomains; i++)
Colin Crossce1e3262010-05-24 17:07:46 -0700200 seq_printf(s, " %9s %7s\n", powergate_name[i],
201 tegra_powergate_is_powered(i) ? "yes" : "no");
202 return 0;
203}
204
205static int powergate_open(struct inode *inode, struct file *file)
206{
207 return single_open(file, powergate_show, inode->i_private);
208}
209
210static const struct file_operations powergate_fops = {
211 .open = powergate_open,
212 .read = seq_read,
213 .llseek = seq_lseek,
214 .release = single_release,
215};
216
217static int __init powergate_debugfs_init(void)
218{
219 struct dentry *d;
220 int err = -ENOMEM;
221
222 d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
223 &powergate_fops);
224 if (!d)
225 return -ENOMEM;
226
227 return err;
228}
229
230late_initcall(powergate_debugfs_init);
231
232#endif