aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/powergate.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/powergate.c')
-rw-r--r--arch/arm/mach-tegra/powergate.c516
1 files changed, 0 insertions, 516 deletions
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
deleted file mode 100644
index 0a14b8638437..000000000000
--- a/arch/arm/mach-tegra/powergate.c
+++ /dev/null
@@ -1,516 +0,0 @@
1/*
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/clk.h>
21#include <linux/clk/tegra.h>
22#include <linux/debugfs.h>
23#include <linux/delay.h>
24#include <linux/err.h>
25#include <linux/export.h>
26#include <linux/init.h>
27#include <linux/io.h>
28#include <linux/kernel.h>
29#include <linux/reset.h>
30#include <linux/seq_file.h>
31#include <linux/spinlock.h>
32
33#include <soc/tegra/fuse.h>
34#include <soc/tegra/powergate.h>
35
36#include "iomap.h"
37
38#define DPD_SAMPLE 0x020
39#define DPD_SAMPLE_ENABLE (1 << 0)
40#define DPD_SAMPLE_DISABLE (0 << 0)
41
42#define PWRGATE_TOGGLE 0x30
43#define PWRGATE_TOGGLE_START (1 << 8)
44
45#define REMOVE_CLAMPING 0x34
46
47#define PWRGATE_STATUS 0x38
48
49#define IO_DPD_REQ 0x1b8
50#define IO_DPD_REQ_CODE_IDLE (0 << 30)
51#define IO_DPD_REQ_CODE_OFF (1 << 30)
52#define IO_DPD_REQ_CODE_ON (2 << 30)
53#define IO_DPD_REQ_CODE_MASK (3 << 30)
54
55#define IO_DPD_STATUS 0x1bc
56#define IO_DPD2_REQ 0x1c0
57#define IO_DPD2_STATUS 0x1c4
58#define SEL_DPD_TIM 0x1c8
59
60#define GPU_RG_CNTRL 0x2d4
61
62static int tegra_num_powerdomains;
63static int tegra_num_cpu_domains;
64static const u8 *tegra_cpu_domains;
65
66static const u8 tegra30_cpu_domains[] = {
67 TEGRA_POWERGATE_CPU,
68 TEGRA_POWERGATE_CPU1,
69 TEGRA_POWERGATE_CPU2,
70 TEGRA_POWERGATE_CPU3,
71};
72
73static const u8 tegra114_cpu_domains[] = {
74 TEGRA_POWERGATE_CPU0,
75 TEGRA_POWERGATE_CPU1,
76 TEGRA_POWERGATE_CPU2,
77 TEGRA_POWERGATE_CPU3,
78};
79
80static const u8 tegra124_cpu_domains[] = {
81 TEGRA_POWERGATE_CPU0,
82 TEGRA_POWERGATE_CPU1,
83 TEGRA_POWERGATE_CPU2,
84 TEGRA_POWERGATE_CPU3,
85};
86
87static DEFINE_SPINLOCK(tegra_powergate_lock);
88
89static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
90
91static u32 pmc_read(unsigned long reg)
92{
93 return readl(pmc + reg);
94}
95
96static void pmc_write(u32 val, unsigned long reg)
97{
98 writel(val, pmc + reg);
99}
100
101static int tegra_powergate_set(int id, bool new_state)
102{
103 bool status;
104 unsigned long flags;
105
106 spin_lock_irqsave(&tegra_powergate_lock, flags);
107
108 status = pmc_read(PWRGATE_STATUS) & (1 << id);
109
110 if (status == new_state) {
111 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
112 return 0;
113 }
114
115 pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
116
117 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
118
119 return 0;
120}
121
122int tegra_powergate_power_on(int id)
123{
124 if (id < 0 || id >= tegra_num_powerdomains)
125 return -EINVAL;
126
127 return tegra_powergate_set(id, true);
128}
129
130int tegra_powergate_power_off(int id)
131{
132 if (id < 0 || id >= tegra_num_powerdomains)
133 return -EINVAL;
134
135 return tegra_powergate_set(id, false);
136}
137EXPORT_SYMBOL(tegra_powergate_power_off);
138
139int tegra_powergate_is_powered(int id)
140{
141 u32 status;
142
143 if (id < 0 || id >= tegra_num_powerdomains)
144 return -EINVAL;
145
146 status = pmc_read(PWRGATE_STATUS) & (1 << id);
147 return !!status;
148}
149
150int tegra_powergate_remove_clamping(int id)
151{
152 u32 mask;
153
154 if (id < 0 || id >= tegra_num_powerdomains)
155 return -EINVAL;
156
157 /*
158 * The Tegra124 GPU has a separate register (with different semantics)
159 * to remove clamps.
160 */
161 if (tegra_get_chip_id() == TEGRA124) {
162 if (id == TEGRA_POWERGATE_3D) {
163 pmc_write(0, GPU_RG_CNTRL);
164 return 0;
165 }
166 }
167
168 /*
169 * Tegra 2 has a bug where PCIE and VDE clamping masks are
170 * swapped relatively to the partition ids
171 */
172 if (id == TEGRA_POWERGATE_VDEC)
173 mask = (1 << TEGRA_POWERGATE_PCIE);
174 else if (id == TEGRA_POWERGATE_PCIE)
175 mask = (1 << TEGRA_POWERGATE_VDEC);
176 else
177 mask = (1 << id);
178
179 pmc_write(mask, REMOVE_CLAMPING);
180
181 return 0;
182}
183EXPORT_SYMBOL(tegra_powergate_remove_clamping);
184
185/* Must be called with clk disabled, and returns with clk enabled */
186int tegra_powergate_sequence_power_up(int id, struct clk *clk,
187 struct reset_control *rst)
188{
189 int ret;
190
191 reset_control_assert(rst);
192
193 ret = tegra_powergate_power_on(id);
194 if (ret)
195 goto err_power;
196
197 ret = clk_prepare_enable(clk);
198 if (ret)
199 goto err_clk;
200
201 udelay(10);
202
203 ret = tegra_powergate_remove_clamping(id);
204 if (ret)
205 goto err_clamp;
206
207 udelay(10);
208 reset_control_deassert(rst);
209
210 return 0;
211
212err_clamp:
213 clk_disable_unprepare(clk);
214err_clk:
215 tegra_powergate_power_off(id);
216err_power:
217 return ret;
218}
219EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
220
221int tegra_cpu_powergate_id(int cpuid)
222{
223 if (cpuid > 0 && cpuid < tegra_num_cpu_domains)
224 return tegra_cpu_domains[cpuid];
225
226 return -EINVAL;
227}
228
229int __init tegra_powergate_init(void)
230{
231 switch (tegra_get_chip_id()) {
232 case TEGRA20:
233 tegra_num_powerdomains = 7;
234 break;
235 case TEGRA30:
236 tegra_num_powerdomains = 14;
237 tegra_num_cpu_domains = 4;
238 tegra_cpu_domains = tegra30_cpu_domains;
239 break;
240 case TEGRA114:
241 tegra_num_powerdomains = 23;
242 tegra_num_cpu_domains = 4;
243 tegra_cpu_domains = tegra114_cpu_domains;
244 break;
245 case TEGRA124:
246 tegra_num_powerdomains = 25;
247 tegra_num_cpu_domains = 4;
248 tegra_cpu_domains = tegra124_cpu_domains;
249 break;
250 default:
251 /* Unknown Tegra variant. Disable powergating */
252 tegra_num_powerdomains = 0;
253 break;
254 }
255
256 return 0;
257}
258
259#ifdef CONFIG_DEBUG_FS
260
261static const char * const *powergate_name;
262
263static const char * const powergate_name_t20[] = {
264 [TEGRA_POWERGATE_CPU] = "cpu",
265 [TEGRA_POWERGATE_3D] = "3d",
266 [TEGRA_POWERGATE_VENC] = "venc",
267 [TEGRA_POWERGATE_VDEC] = "vdec",
268 [TEGRA_POWERGATE_PCIE] = "pcie",
269 [TEGRA_POWERGATE_L2] = "l2",
270 [TEGRA_POWERGATE_MPE] = "mpe",
271};
272
273static const char * const powergate_name_t30[] = {
274 [TEGRA_POWERGATE_CPU] = "cpu0",
275 [TEGRA_POWERGATE_3D] = "3d0",
276 [TEGRA_POWERGATE_VENC] = "venc",
277 [TEGRA_POWERGATE_VDEC] = "vdec",
278 [TEGRA_POWERGATE_PCIE] = "pcie",
279 [TEGRA_POWERGATE_L2] = "l2",
280 [TEGRA_POWERGATE_MPE] = "mpe",
281 [TEGRA_POWERGATE_HEG] = "heg",
282 [TEGRA_POWERGATE_SATA] = "sata",
283 [TEGRA_POWERGATE_CPU1] = "cpu1",
284 [TEGRA_POWERGATE_CPU2] = "cpu2",
285 [TEGRA_POWERGATE_CPU3] = "cpu3",
286 [TEGRA_POWERGATE_CELP] = "celp",
287 [TEGRA_POWERGATE_3D1] = "3d1",
288};
289
290static const char * const powergate_name_t114[] = {
291 [TEGRA_POWERGATE_CPU] = "crail",
292 [TEGRA_POWERGATE_3D] = "3d",
293 [TEGRA_POWERGATE_VENC] = "venc",
294 [TEGRA_POWERGATE_VDEC] = "vdec",
295 [TEGRA_POWERGATE_MPE] = "mpe",
296 [TEGRA_POWERGATE_HEG] = "heg",
297 [TEGRA_POWERGATE_CPU1] = "cpu1",
298 [TEGRA_POWERGATE_CPU2] = "cpu2",
299 [TEGRA_POWERGATE_CPU3] = "cpu3",
300 [TEGRA_POWERGATE_CELP] = "celp",
301 [TEGRA_POWERGATE_CPU0] = "cpu0",
302 [TEGRA_POWERGATE_C0NC] = "c0nc",
303 [TEGRA_POWERGATE_C1NC] = "c1nc",
304 [TEGRA_POWERGATE_DIS] = "dis",
305 [TEGRA_POWERGATE_DISB] = "disb",
306 [TEGRA_POWERGATE_XUSBA] = "xusba",
307 [TEGRA_POWERGATE_XUSBB] = "xusbb",
308 [TEGRA_POWERGATE_XUSBC] = "xusbc",
309};
310
311static const char * const powergate_name_t124[] = {
312 [TEGRA_POWERGATE_CPU] = "crail",
313 [TEGRA_POWERGATE_3D] = "3d",
314 [TEGRA_POWERGATE_VENC] = "venc",
315 [TEGRA_POWERGATE_PCIE] = "pcie",
316 [TEGRA_POWERGATE_VDEC] = "vdec",
317 [TEGRA_POWERGATE_L2] = "l2",
318 [TEGRA_POWERGATE_MPE] = "mpe",
319 [TEGRA_POWERGATE_HEG] = "heg",
320 [TEGRA_POWERGATE_SATA] = "sata",
321 [TEGRA_POWERGATE_CPU1] = "cpu1",
322 [TEGRA_POWERGATE_CPU2] = "cpu2",
323 [TEGRA_POWERGATE_CPU3] = "cpu3",
324 [TEGRA_POWERGATE_CELP] = "celp",
325 [TEGRA_POWERGATE_CPU0] = "cpu0",
326 [TEGRA_POWERGATE_C0NC] = "c0nc",
327 [TEGRA_POWERGATE_C1NC] = "c1nc",
328 [TEGRA_POWERGATE_SOR] = "sor",
329 [TEGRA_POWERGATE_DIS] = "dis",
330 [TEGRA_POWERGATE_DISB] = "disb",
331 [TEGRA_POWERGATE_XUSBA] = "xusba",
332 [TEGRA_POWERGATE_XUSBB] = "xusbb",
333 [TEGRA_POWERGATE_XUSBC] = "xusbc",
334 [TEGRA_POWERGATE_VIC] = "vic",
335 [TEGRA_POWERGATE_IRAM] = "iram",
336};
337
338static int powergate_show(struct seq_file *s, void *data)
339{
340 int i;
341
342 seq_printf(s, " powergate powered\n");
343 seq_printf(s, "------------------\n");
344
345 for (i = 0; i < tegra_num_powerdomains; i++) {
346 if (!powergate_name[i])
347 continue;
348
349 seq_printf(s, " %9s %7s\n", powergate_name[i],
350 tegra_powergate_is_powered(i) ? "yes" : "no");
351 }
352
353 return 0;
354}
355
356static int powergate_open(struct inode *inode, struct file *file)
357{
358 return single_open(file, powergate_show, inode->i_private);
359}
360
361static const struct file_operations powergate_fops = {
362 .open = powergate_open,
363 .read = seq_read,
364 .llseek = seq_lseek,
365 .release = single_release,
366};
367
368int __init tegra_powergate_debugfs_init(void)
369{
370 struct dentry *d;
371
372 switch (tegra_get_chip_id()) {
373 case TEGRA20:
374 powergate_name = powergate_name_t20;
375 break;
376 case TEGRA30:
377 powergate_name = powergate_name_t30;
378 break;
379 case TEGRA114:
380 powergate_name = powergate_name_t114;
381 break;
382 case TEGRA124:
383 powergate_name = powergate_name_t124;
384 break;
385 }
386
387 if (powergate_name) {
388 d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
389 &powergate_fops);
390 if (!d)
391 return -ENOMEM;
392 }
393
394 return 0;
395}
396
397#endif
398
399static int tegra_io_rail_prepare(int id, unsigned long *request,
400 unsigned long *status, unsigned int *bit)
401{
402 unsigned long rate, value;
403 struct clk *clk;
404
405 *bit = id % 32;
406
407 /*
408 * There are two sets of 30 bits to select IO rails, but bits 30 and
409 * 31 are control bits rather than IO rail selection bits.
410 */
411 if (id > 63 || *bit == 30 || *bit == 31)
412 return -EINVAL;
413
414 if (id < 32) {
415 *status = IO_DPD_STATUS;
416 *request = IO_DPD_REQ;
417 } else {
418 *status = IO_DPD2_STATUS;
419 *request = IO_DPD2_REQ;
420 }
421
422 clk = clk_get_sys(NULL, "pclk");
423 if (IS_ERR(clk))
424 return PTR_ERR(clk);
425
426 rate = clk_get_rate(clk);
427 clk_put(clk);
428
429 pmc_write(DPD_SAMPLE_ENABLE, DPD_SAMPLE);
430
431 /* must be at least 200 ns, in APB (PCLK) clock cycles */
432 value = DIV_ROUND_UP(1000000000, rate);
433 value = DIV_ROUND_UP(200, value);
434 pmc_write(value, SEL_DPD_TIM);
435
436 return 0;
437}
438
439static int tegra_io_rail_poll(unsigned long offset, unsigned long mask,
440 unsigned long val, unsigned long timeout)
441{
442 unsigned long value;
443
444 timeout = jiffies + msecs_to_jiffies(timeout);
445
446 while (time_after(timeout, jiffies)) {
447 value = pmc_read(offset);
448 if ((value & mask) == val)
449 return 0;
450
451 usleep_range(250, 1000);
452 }
453
454 return -ETIMEDOUT;
455}
456
457static void tegra_io_rail_unprepare(void)
458{
459 pmc_write(DPD_SAMPLE_DISABLE, DPD_SAMPLE);
460}
461
462int tegra_io_rail_power_on(int id)
463{
464 unsigned long request, status, value;
465 unsigned int bit, mask;
466 int err;
467
468 err = tegra_io_rail_prepare(id, &request, &status, &bit);
469 if (err < 0)
470 return err;
471
472 mask = 1 << bit;
473
474 value = pmc_read(request);
475 value |= mask;
476 value &= ~IO_DPD_REQ_CODE_MASK;
477 value |= IO_DPD_REQ_CODE_OFF;
478 pmc_write(value, request);
479
480 err = tegra_io_rail_poll(status, mask, 0, 250);
481 if (err < 0)
482 return err;
483
484 tegra_io_rail_unprepare();
485
486 return 0;
487}
488EXPORT_SYMBOL(tegra_io_rail_power_on);
489
490int tegra_io_rail_power_off(int id)
491{
492 unsigned long request, status, value;
493 unsigned int bit, mask;
494 int err;
495
496 err = tegra_io_rail_prepare(id, &request, &status, &bit);
497 if (err < 0)
498 return err;
499
500 mask = 1 << bit;
501
502 value = pmc_read(request);
503 value |= mask;
504 value &= ~IO_DPD_REQ_CODE_MASK;
505 value |= IO_DPD_REQ_CODE_ON;
506 pmc_write(value, request);
507
508 err = tegra_io_rail_poll(status, mask, mask, 250);
509 if (err < 0)
510 return err;
511
512 tegra_io_rail_unprepare();
513
514 return 0;
515}
516EXPORT_SYMBOL(tegra_io_rail_power_off);