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