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.c723
1 files changed, 688 insertions, 35 deletions
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
index 3cee9aa1f2c..76d264fcad0 100644
--- a/arch/arm/mach-tegra/powergate.c
+++ b/arch/arm/mach-tegra/powergate.c
@@ -2,6 +2,7 @@
2 * drivers/powergate/tegra-powergate.c 2 * drivers/powergate/tegra-powergate.c
3 * 3 *
4 * Copyright (c) 2010 Google, Inc 4 * Copyright (c) 2010 Google, Inc
5 * Copyright (C) 2011 NVIDIA Corporation.
5 * 6 *
6 * Author: 7 * Author:
7 * Colin Cross <ccross@google.com> 8 * Colin Cross <ccross@google.com>
@@ -31,15 +32,138 @@
31#include <mach/iomap.h> 32#include <mach/iomap.h>
32#include <mach/powergate.h> 33#include <mach/powergate.h>
33 34
35#include "clock.h"
36
34#define PWRGATE_TOGGLE 0x30 37#define PWRGATE_TOGGLE 0x30
35#define PWRGATE_TOGGLE_START (1 << 8) 38#define PWRGATE_TOGGLE_START (1 << 8)
36 39
37#define REMOVE_CLAMPING 0x34 40#define REMOVE_CLAMPING 0x34
38 41
39#define PWRGATE_STATUS 0x38 42#define PWRGATE_STATUS 0x38
40 43
44#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
45enum mc_client {
46 MC_CLIENT_AFI = 0,
47 MC_CLIENT_AVPC = 1,
48 MC_CLIENT_DC = 2,
49 MC_CLIENT_DCB = 3,
50 MC_CLIENT_EPP = 4,
51 MC_CLIENT_G2 = 5,
52 MC_CLIENT_HC = 6,
53 MC_CLIENT_HDA = 7,
54 MC_CLIENT_ISP = 8,
55 MC_CLIENT_MPCORE = 9,
56 MC_CLIENT_MPCORELP = 10,
57 MC_CLIENT_MPE = 11,
58 MC_CLIENT_NV = 12,
59 MC_CLIENT_NV2 = 13,
60 MC_CLIENT_PPCS = 14,
61 MC_CLIENT_SATA = 15,
62 MC_CLIENT_VDE = 16,
63 MC_CLIENT_VI = 17,
64 MC_CLIENT_LAST = -1,
65};
66#else
67enum mc_client {
68 MC_CLIENT_AVPC = 0,
69 MC_CLIENT_DC = 1,
70 MC_CLIENT_DCB = 2,
71 MC_CLIENT_EPP = 3,
72 MC_CLIENT_G2 = 4,
73 MC_CLIENT_HC = 5,
74 MC_CLIENT_ISP = 6,
75 MC_CLIENT_MPCORE = 7,
76 MC_CLIENT_MPEA = 8,
77 MC_CLIENT_MPEB = 9,
78 MC_CLIENT_MPEC = 10,
79 MC_CLIENT_NV = 11,
80 MC_CLIENT_PPCS = 12,
81 MC_CLIENT_VDE = 13,
82 MC_CLIENT_VI = 14,
83 MC_CLIENT_LAST = -1,
84 MC_CLIENT_AFI = MC_CLIENT_LAST,
85};
86#endif
87
88#define MAX_CLK_EN_NUM 4
89
41static DEFINE_SPINLOCK(tegra_powergate_lock); 90static DEFINE_SPINLOCK(tegra_powergate_lock);
42 91
92#define MAX_HOTRESET_CLIENT_NUM 4
93
94enum clk_type {
95 CLK_AND_RST,
96 RST_ONLY,
97 CLK_ONLY,
98};
99
100struct partition_clk_info {
101 const char *clk_name;
102 enum clk_type clk_type;
103 /* true if clk is only used in assert/deassert reset and not while enable-den*/
104 struct clk *clk_ptr;
105};
106
107struct powergate_partition {
108 const char *name;
109 enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
110 struct partition_clk_info clk_info[MAX_CLK_EN_NUM];
111};
112
113static struct powergate_partition powergate_partition_info[TEGRA_NUM_POWERGATE] = {
114 [TEGRA_POWERGATE_CPU] = { "cpu0", {MC_CLIENT_LAST}, },
115 [TEGRA_POWERGATE_L2] = { "l2", {MC_CLIENT_LAST}, },
116 [TEGRA_POWERGATE_3D] = { "3d0",
117 {MC_CLIENT_NV, MC_CLIENT_LAST},
118 {{"3d", CLK_AND_RST} }, },
119 [TEGRA_POWERGATE_PCIE] = { "pcie",
120 {MC_CLIENT_AFI, MC_CLIENT_LAST},
121 {{"afi", CLK_AND_RST},
122 {"pcie", CLK_AND_RST},
123#ifndef CONFIG_ARCH_TEGRA_2x_SOC
124 {"cml0", CLK_ONLY},
125#endif
126 {"pciex", RST_ONLY} }, },
127 [TEGRA_POWERGATE_VDEC] = { "vde",
128 {MC_CLIENT_VDE, MC_CLIENT_LAST},
129 {{"vde", CLK_AND_RST} }, },
130 [TEGRA_POWERGATE_MPE] = { "mpe",
131#ifndef CONFIG_ARCH_TEGRA_2x_SOC
132 {MC_CLIENT_MPE, MC_CLIENT_LAST},
133#else
134 {MC_CLIENT_MPEA, MC_CLIENT_MPEB,
135 MC_CLIENT_MPEC, MC_CLIENT_LAST},
136#endif
137 {{"mpe", CLK_AND_RST} }, },
138 [TEGRA_POWERGATE_VENC] = { "ve",
139 {MC_CLIENT_ISP, MC_CLIENT_VI, MC_CLIENT_LAST},
140 {{"isp", CLK_AND_RST},
141 {"vi", CLK_AND_RST},
142 {"csi", CLK_AND_RST} }, },
143#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
144 [TEGRA_POWERGATE_CPU1] = { "cpu1", {MC_CLIENT_LAST}, },
145 [TEGRA_POWERGATE_CPU2] = { "cpu2", {MC_CLIENT_LAST}, },
146 [TEGRA_POWERGATE_CPU3] = { "cpu3", {MC_CLIENT_LAST}, },
147 [TEGRA_POWERGATE_CELP] = { "celp", {MC_CLIENT_LAST}, },
148 [TEGRA_POWERGATE_SATA] = { "sata", {MC_CLIENT_SATA, MC_CLIENT_LAST},
149 {{"sata", CLK_AND_RST},
150 {"sata_oob", CLK_AND_RST},
151 {"cml1", CLK_ONLY},
152 {"sata_cold", RST_ONLY} }, },
153 [TEGRA_POWERGATE_3D1] = { "3d1",
154 {MC_CLIENT_NV2, MC_CLIENT_LAST},
155 {{"3d2", CLK_AND_RST} }, },
156 [TEGRA_POWERGATE_HEG] = { "heg",
157 {MC_CLIENT_G2, MC_CLIENT_EPP,
158 MC_CLIENT_HC,
159 MC_CLIENT_LAST},
160 {{"2d", CLK_AND_RST},
161 {"epp", CLK_AND_RST},
162 {"host1x", CLK_AND_RST},
163 {"3d", RST_ONLY} }, },
164#endif
165};
166
43static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); 167static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
44 168
45static u32 pmc_read(unsigned long reg) 169static u32 pmc_read(unsigned long reg)
@@ -52,40 +176,306 @@ static void pmc_write(u32 val, unsigned long reg)
52 writel(val, pmc + reg); 176 writel(val, pmc + reg);
53} 177}
54 178
179static void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
180
181static u32 mc_read(unsigned long reg)
182{
183 return readl(mc + reg);
184}
185
186static void mc_write(u32 val, unsigned long reg)
187{
188 writel(val, mc + reg);
189}
190
191#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
192
193#define MC_CLIENT_HOTRESET_CTRL 0x200
194#define MC_CLIENT_HOTRESET_STAT 0x204
195
196static void mc_flush(int id)
197{
198 u32 idx, rst_ctrl, rst_stat;
199 enum mc_client mcClientBit;
200 unsigned long flags;
201
202 BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
203
204 for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
205 mcClientBit = powergate_partition_info[id].hot_reset_clients[idx];
206 if (mcClientBit == MC_CLIENT_LAST)
207 break;
208
209 spin_lock_irqsave(&tegra_powergate_lock, flags);
210 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
211 rst_ctrl |= (1 << mcClientBit);
212 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
213
214 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
215
216 do {
217 udelay(10);
218 rst_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
219 } while (!(rst_stat & (1 << mcClientBit)));
220 }
221}
222
223static void mc_flush_done(int id)
224{
225 u32 idx, rst_ctrl;
226 enum mc_client mcClientBit;
227 unsigned long flags;
228
229 BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
230
231 for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
232 mcClientBit = powergate_partition_info[id].hot_reset_clients[idx];
233 if (mcClientBit == MC_CLIENT_LAST)
234 break;
235
236 spin_lock_irqsave(&tegra_powergate_lock, flags);
237
238 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
239 rst_ctrl &= ~(1 << mcClientBit);
240 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
241
242 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
243 }
244
245 wmb();
246}
247
248int tegra_powergate_mc_flush(int id)
249{
250 if (id < 0 || id >= TEGRA_NUM_POWERGATE)
251 return -EINVAL;
252 mc_flush(id);
253 return 0;
254}
255
256int tegra_powergate_mc_flush_done(int id)
257{
258 if (id < 0 || id >= TEGRA_NUM_POWERGATE)
259 return -EINVAL;
260 mc_flush_done(id);
261 return 0;
262}
263
264int tegra_powergate_mc_disable(int id)
265{
266 return 0;
267}
268
269int tegra_powergate_mc_enable(int id)
270{
271 return 0;
272}
273
274#else
275
276#define MC_CLIENT_CTRL 0x100
277#define MC_CLIENT_HOTRESETN 0x104
278#define MC_CLIENT_ORRC_BASE 0x140
279
280int tegra_powergate_mc_disable(int id)
281{
282 u32 idx, clt_ctrl, orrc_reg;
283 enum mc_client mcClientBit;
284 unsigned long flags;
285
286 if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
287 WARN_ON(1);
288 return -EINVAL;
289 }
290
291 for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
292 mcClientBit =
293 powergate_partition_info[id].hot_reset_clients[idx];
294 if (mcClientBit == MC_CLIENT_LAST)
295 break;
296
297 spin_lock_irqsave(&tegra_powergate_lock, flags);
298
299 /* clear client enable bit */
300 clt_ctrl = mc_read(MC_CLIENT_CTRL);
301 clt_ctrl &= ~(1 << mcClientBit);
302 mc_write(clt_ctrl, MC_CLIENT_CTRL);
303
304 /* read back to flush write */
305 clt_ctrl = mc_read(MC_CLIENT_CTRL);
306
307 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
308
309 /* wait for outstanding requests to reach 0 */
310 orrc_reg = MC_CLIENT_ORRC_BASE + (mcClientBit * 4);
311 while (mc_read(orrc_reg) != 0)
312 udelay(10);
313 }
314 return 0;
315}
316
317int tegra_powergate_mc_flush(int id)
318{
319 u32 idx, hot_rstn;
320 enum mc_client mcClientBit;
321 unsigned long flags;
322
323 if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
324 WARN_ON(1);
325 return -EINVAL;
326 }
327
328 for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
329 mcClientBit =
330 powergate_partition_info[id].hot_reset_clients[idx];
331 if (mcClientBit == MC_CLIENT_LAST)
332 break;
333
334 spin_lock_irqsave(&tegra_powergate_lock, flags);
335
336 /* assert hotreset (client module is currently in reset) */
337 hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
338 hot_rstn &= ~(1 << mcClientBit);
339 mc_write(hot_rstn, MC_CLIENT_HOTRESETN);
340
341 /* read back to flush write */
342 hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
343
344 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
345 }
346 return 0;
347}
348
349int tegra_powergate_mc_flush_done(int id)
350{
351 u32 idx, hot_rstn;
352 enum mc_client mcClientBit;
353 unsigned long flags;
354
355 if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
356 WARN_ON(1);
357 return -EINVAL;
358 }
359
360 for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
361 mcClientBit =
362 powergate_partition_info[id].hot_reset_clients[idx];
363 if (mcClientBit == MC_CLIENT_LAST)
364 break;
365
366 spin_lock_irqsave(&tegra_powergate_lock, flags);
367
368 /* deassert hotreset */
369 hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
370 hot_rstn |= (1 << mcClientBit);
371 mc_write(hot_rstn, MC_CLIENT_HOTRESETN);
372
373 /* read back to flush write */
374 hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
375
376 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
377 }
378 return 0;
379}
380
381int tegra_powergate_mc_enable(int id)
382{
383 u32 idx, clt_ctrl;
384 enum mc_client mcClientBit;
385 unsigned long flags;
386
387 if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
388 WARN_ON(1);
389 return -EINVAL;
390 }
391
392 for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
393 mcClientBit =
394 powergate_partition_info[id].hot_reset_clients[idx];
395 if (mcClientBit == MC_CLIENT_LAST)
396 break;
397
398 spin_lock_irqsave(&tegra_powergate_lock, flags);
399
400 /* enable client */
401 clt_ctrl = mc_read(MC_CLIENT_CTRL);
402 clt_ctrl |= (1 << mcClientBit);
403 mc_write(clt_ctrl, MC_CLIENT_CTRL);
404
405 /* read back to flush write */
406 clt_ctrl = mc_read(MC_CLIENT_CTRL);
407
408 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
409 }
410 return 0;
411}
412
413static void mc_flush(int id) {}
414static void mc_flush_done(int id) {}
415#endif
416
55static int tegra_powergate_set(int id, bool new_state) 417static int tegra_powergate_set(int id, bool new_state)
56{ 418{
57 bool status; 419 bool status;
58 unsigned long flags; 420 unsigned long flags;
421 /* 10us timeout for toggle operation if it takes affect*/
422 int toggle_timeout = 10;
423 /* 100 * 10 = 1000us timeout for toggle command to take affect in case
424 of contention with h/w initiated CPU power gating */
425 int contention_timeout = 100;
59 426
60 spin_lock_irqsave(&tegra_powergate_lock, flags); 427 spin_lock_irqsave(&tegra_powergate_lock, flags);
61 428
62 status = pmc_read(PWRGATE_STATUS) & (1 << id); 429 status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
63 430
64 if (status == new_state) { 431 if (status == new_state) {
65 spin_unlock_irqrestore(&tegra_powergate_lock, flags); 432 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
66 return -EINVAL; 433 return 0;
434 }
435
436 if (TEGRA_IS_CPU_POWERGATE_ID(id)) {
437 /* CPU ungated in s/w only during boot/resume with outer
438 waiting loop and no contention from other CPUs */
439 pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
440 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
441 return 0;
67 } 442 }
68 443
69 pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); 444 do {
445 pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
446 do {
447 udelay(1);
448 status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
449
450 toggle_timeout--;
451 } while ((status != new_state) && (toggle_timeout > 0));
452
453 contention_timeout--;
454 } while ((status != new_state) && (contention_timeout > 0));
70 455
71 spin_unlock_irqrestore(&tegra_powergate_lock, flags); 456 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
72 457
458 if (status != new_state) {
459 WARN(1, "Could not set powergate %d to %d", id, new_state);
460 return -EBUSY;
461 }
462
73 return 0; 463 return 0;
74} 464}
75 465
76int tegra_powergate_power_on(int id) 466static int unpowergate_module(int id)
77{ 467{
78 if (id < 0 || id >= TEGRA_NUM_POWERGATE) 468 if (id < 0 || id >= TEGRA_NUM_POWERGATE)
79 return -EINVAL; 469 return -EINVAL;
80
81 return tegra_powergate_set(id, true); 470 return tegra_powergate_set(id, true);
82} 471}
83 472
84int tegra_powergate_power_off(int id) 473static int powergate_module(int id)
85{ 474{
86 if (id < 0 || id >= TEGRA_NUM_POWERGATE) 475 if (id < 0 || id >= TEGRA_NUM_POWERGATE)
87 return -EINVAL; 476 return -EINVAL;
88 477
478 mc_flush(id);
89 return tegra_powergate_set(id, false); 479 return tegra_powergate_set(id, false);
90} 480}
91 481
@@ -94,7 +484,7 @@ bool tegra_powergate_is_powered(int id)
94 u32 status; 484 u32 status;
95 485
96 if (id < 0 || id >= TEGRA_NUM_POWERGATE) 486 if (id < 0 || id >= TEGRA_NUM_POWERGATE)
97 return -EINVAL; 487 return false;
98 488
99 status = pmc_read(PWRGATE_STATUS) & (1 << id); 489 status = pmc_read(PWRGATE_STATUS) & (1 << id);
100 return !!status; 490 return !!status;
@@ -103,17 +493,16 @@ bool tegra_powergate_is_powered(int id)
103int tegra_powergate_remove_clamping(int id) 493int tegra_powergate_remove_clamping(int id)
104{ 494{
105 u32 mask; 495 u32 mask;
106
107 if (id < 0 || id >= TEGRA_NUM_POWERGATE) 496 if (id < 0 || id >= TEGRA_NUM_POWERGATE)
108 return -EINVAL; 497 return -EINVAL;
109 498
110 /* 499 /*
111 * Tegra 2 has a bug where PCIE and VDE clamping masks are 500 * PCIE and VDE clamping masks are swapped with respect to their
112 * swapped relatively to the partition ids 501 * partition ids
113 */ 502 */
114 if (id == TEGRA_POWERGATE_VDEC) 503 if (id == TEGRA_POWERGATE_VDEC)
115 mask = (1 << TEGRA_POWERGATE_PCIE); 504 mask = (1 << TEGRA_POWERGATE_PCIE);
116 else if (id == TEGRA_POWERGATE_PCIE) 505 else if (id == TEGRA_POWERGATE_PCIE)
117 mask = (1 << TEGRA_POWERGATE_VDEC); 506 mask = (1 << TEGRA_POWERGATE_VDEC);
118 else 507 else
119 mask = (1 << id); 508 mask = (1 << id);
@@ -123,20 +512,184 @@ int tegra_powergate_remove_clamping(int id)
123 return 0; 512 return 0;
124} 513}
125 514
126/* Must be called with clk disabled, and returns with clk enabled */ 515static void get_clk_info(int id)
127int tegra_powergate_sequence_power_up(int id, struct clk *clk) 516{
517 int idx;
518
519 for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
520 if (!powergate_partition_info[id].clk_info[idx].clk_name)
521 break;
522 powergate_partition_info[id].
523 clk_info[idx].clk_ptr =
524 tegra_get_clock_by_name(
525 powergate_partition_info[id].clk_info[idx].clk_name);
526 }
527}
528
529static int partition_clk_enable(int id)
530{
531 int ret;
532 u32 idx;
533 struct clk *clk;
534 struct partition_clk_info *clk_info;
535
536 BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
537
538 for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
539 clk_info = &powergate_partition_info[id].clk_info[idx];
540 clk = clk_info->clk_ptr;
541 if (!clk)
542 break;
543
544 if (clk_info->clk_type != RST_ONLY) {
545 ret = clk_enable(clk);
546 if (ret)
547 goto err_clk_en;
548 }
549 }
550
551 return 0;
552
553err_clk_en:
554 WARN(1, "Could not enable clk %s", clk->name);
555 while (idx--) {
556 clk_info = &powergate_partition_info[id].clk_info[idx];
557 if (clk_info->clk_type != RST_ONLY)
558 clk_disable(clk_info->clk_ptr);
559 }
560
561 return ret;
562}
563
564static int is_partition_clk_disabled(int id)
565{
566 u32 idx;
567 struct clk *clk;
568 struct partition_clk_info *clk_info;
569 int ret = 0;
570
571 BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
572
573 for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
574 clk_info = &powergate_partition_info[id].clk_info[idx];
575 clk = clk_info->clk_ptr;
576 if (!clk)
577 break;
578
579 if (clk_info->clk_type != RST_ONLY) {
580 if (tegra_is_clk_enabled(clk)) {
581 ret = -1;
582 break;
583 }
584 }
585 }
586
587 return ret;
588}
589
590static void partition_clk_disable(int id)
591{
592 u32 idx;
593 struct clk *clk;
594 struct partition_clk_info *clk_info;
595
596 BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
597
598 for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
599 clk_info = &powergate_partition_info[id].clk_info[idx];
600 clk = clk_info->clk_ptr;
601 if (!clk)
602 break;
603
604 if (clk_info->clk_type != RST_ONLY)
605 clk_disable(clk);
606 }
607}
608
609static void powergate_partition_assert_reset(int id)
610{
611 u32 idx;
612 struct clk *clk_ptr;
613 struct partition_clk_info *clk_info;
614
615 BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
616
617 for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
618 clk_info = &powergate_partition_info[id].clk_info[idx];
619 clk_ptr = clk_info->clk_ptr;
620 if (!clk_ptr)
621 break;
622 if (clk_info->clk_type != CLK_ONLY)
623 tegra_periph_reset_assert(clk_ptr);
624 }
625}
626
627static void powergate_partition_deassert_reset(int id)
628{
629 u32 idx;
630 struct clk *clk_ptr;
631 struct partition_clk_info *clk_info;
632
633 BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
634
635 for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
636 clk_info = &powergate_partition_info[id].clk_info[idx];
637 clk_ptr = clk_info->clk_ptr;
638 if (!clk_ptr)
639 break;
640 if (clk_info->clk_type != CLK_ONLY)
641 tegra_periph_reset_deassert(clk_ptr);
642 }
643}
644
645/* Must be called with clk disabled, and returns with clk disabled */
646static int tegra_powergate_reset_module(int id)
647{
648 int ret;
649
650 powergate_partition_assert_reset(id);
651
652 udelay(10);
653
654 ret = partition_clk_enable(id);
655 if (ret)
656 return ret;
657
658 udelay(10);
659
660 powergate_partition_deassert_reset(id);
661
662 partition_clk_disable(id);
663
664 return 0;
665}
666
667/*
668 * Must be called with clk disabled, and returns with clk disabled
669 * Drivers should enable clks for partition. Unpowergates only the
670 * partition.
671 */
672int tegra_unpowergate_partition(int id)
128{ 673{
129 int ret; 674 int ret;
130 675
131 tegra_periph_reset_assert(clk); 676 /* If first clk_ptr is null, fill clk info for the partition */
677 if (!powergate_partition_info[id].clk_info[0].clk_ptr)
678 get_clk_info(id);
132 679
133 ret = tegra_powergate_power_on(id); 680 if (tegra_powergate_is_powered(id))
681 return tegra_powergate_reset_module(id);
682
683 ret = unpowergate_module(id);
134 if (ret) 684 if (ret)
135 goto err_power; 685 goto err_power;
136 686
137 ret = clk_enable(clk); 687 powergate_partition_assert_reset(id);
688
689 /* Un-Powergating fails if all clks are not enabled */
690 ret = partition_clk_enable(id);
138 if (ret) 691 if (ret)
139 goto err_clk; 692 goto err_clk_on;
140 693
141 udelay(10); 694 udelay(10);
142 695
@@ -145,29 +698,130 @@ int tegra_powergate_sequence_power_up(int id, struct clk *clk)
145 goto err_clamp; 698 goto err_clamp;
146 699
147 udelay(10); 700 udelay(10);
148 tegra_periph_reset_deassert(clk); 701 powergate_partition_deassert_reset(id);
702
703 mc_flush_done(id);
704
705 /* Disable all clks enabled earlier. Drivers should enable clks */
706 partition_clk_disable(id);
149 707
150 return 0; 708 return 0;
151 709
152err_clamp: 710err_clamp:
153 clk_disable(clk); 711 partition_clk_disable(id);
154err_clk: 712err_clk_on:
155 tegra_powergate_power_off(id); 713 powergate_module(id);
156err_power: 714err_power:
715 WARN(1, "Could not Un-Powergate %d", id);
157 return ret; 716 return ret;
158} 717}
159 718
160#ifdef CONFIG_DEBUG_FS 719/*
720 * Must be called with clk disabled, and returns with clk enabled
721 * Unpowergates the partition and enables all required clks.
722 */
723int tegra_unpowergate_partition_with_clk_on(int id)
724{
725 int ret = 0;
726
727#ifndef CONFIG_ARCH_TEGRA_2x_SOC
728 /* Restrict this functions use to few partitions */
729 BUG_ON(id != TEGRA_POWERGATE_SATA && id != TEGRA_POWERGATE_PCIE);
730#else
731 /* Restrict this functions use to few partitions */
732 BUG_ON(id != TEGRA_POWERGATE_PCIE);
733#endif
161 734
162static const char * const powergate_name[] = { 735 ret = tegra_unpowergate_partition(id);
163 [TEGRA_POWERGATE_CPU] = "cpu", 736 if (ret)
164 [TEGRA_POWERGATE_3D] = "3d", 737 goto err_unpowergating;
165 [TEGRA_POWERGATE_VENC] = "venc", 738
166 [TEGRA_POWERGATE_VDEC] = "vdec", 739 /* Enable clks for the partition */
167 [TEGRA_POWERGATE_PCIE] = "pcie", 740 ret = partition_clk_enable(id);
168 [TEGRA_POWERGATE_L2] = "l2", 741 if (ret)
169 [TEGRA_POWERGATE_MPE] = "mpe", 742 goto err_unpowergate_clk;
170}; 743
744 return ret;
745
746err_unpowergate_clk:
747 tegra_powergate_partition(id);
748 WARN(1, "Could not Un-Powergate %d, err in enabling clk", id);
749err_unpowergating:
750 WARN(1, "Could not Un-Powergate %d", id);
751 return ret;
752}
753
754/*
755 * Must be called with clk disabled. Powergates the partition only
756 */
757int tegra_powergate_partition(int id)
758{
759 int ret;
760
761 /* If first clk_ptr is null, fill clk info for the partition */
762 if (powergate_partition_info[id].clk_info[0].clk_ptr)
763 get_clk_info(id);
764 powergate_partition_assert_reset(id);
765
766 /* Powergating is done only if refcnt of all clks is 0 */
767 ret = is_partition_clk_disabled(id);
768 if (ret)
769 goto err_clk_off;
770
771 ret = powergate_module(id);
772 if (ret)
773 goto err_power_off;
774
775 return 0;
776
777err_power_off:
778 WARN(1, "Could not Powergate Partition %d", id);
779err_clk_off:
780 WARN(1, "Could not Powergate Partition %d, all clks not disabled", id);
781 return ret;
782}
783
784int tegra_powergate_partition_with_clk_off(int id)
785{
786 int ret = 0;
787
788#ifndef CONFIG_ARCH_TEGRA_2x_SOC
789 /* Restrict functions use to selected partitions */
790 BUG_ON(id != TEGRA_POWERGATE_PCIE && id != TEGRA_POWERGATE_SATA);
791#else
792 /* Restrict functions use to selected partitions */
793 BUG_ON(id != TEGRA_POWERGATE_PCIE);
794#endif
795 /* Disable clks for the partition */
796 partition_clk_disable(id);
797
798 ret = is_partition_clk_disabled(id);
799 if (ret)
800 goto err_powergate_clk;
801
802 ret = tegra_powergate_partition(id);
803 if (ret)
804 goto err_powergating;
805
806 return ret;
807
808err_powergate_clk:
809 WARN(1, "Could not Powergate Partition %d, all clks not disabled", id);
810err_powergating:
811 partition_clk_enable(id);
812 WARN(1, "Could not Powergate Partition %d", id);
813 return ret;
814}
815
816const char *tegra_powergate_get_name(int id)
817{
818 if (id < 0 || id >= TEGRA_NUM_POWERGATE)
819 return "invalid";
820
821 return powergate_partition_info[id].name;
822}
823
824#ifdef CONFIG_DEBUG_FS
171 825
172static int powergate_show(struct seq_file *s, void *data) 826static int powergate_show(struct seq_file *s, void *data)
173{ 827{
@@ -177,7 +831,7 @@ static int powergate_show(struct seq_file *s, void *data)
177 seq_printf(s, "------------------\n"); 831 seq_printf(s, "------------------\n");
178 832
179 for (i = 0; i < TEGRA_NUM_POWERGATE; i++) 833 for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
180 seq_printf(s, " %9s %7s\n", powergate_name[i], 834 seq_printf(s, " %9s %7s\n", powergate_partition_info[i].name,
181 tegra_powergate_is_powered(i) ? "yes" : "no"); 835 tegra_powergate_is_powered(i) ? "yes" : "no");
182 return 0; 836 return 0;
183} 837}
@@ -197,14 +851,13 @@ static const struct file_operations powergate_fops = {
197static int __init powergate_debugfs_init(void) 851static int __init powergate_debugfs_init(void)
198{ 852{
199 struct dentry *d; 853 struct dentry *d;
200 int err = -ENOMEM;
201 854
202 d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL, 855 d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
203 &powergate_fops); 856 &powergate_fops);
204 if (!d) 857 if (!d)
205 return -ENOMEM; 858 return -ENOMEM;
206 859
207 return err; 860 return 0;
208} 861}
209 862
210late_initcall(powergate_debugfs_init); 863late_initcall(powergate_debugfs_init);