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