]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blob - arch/arm/mach-tegra/powergate-t12x.c
arm: tegra: power: add reference counters for pcie
[sojka/nv-tegra/linux-3.10.git] / arch / arm / mach-tegra / powergate-t12x.c
1 /*
2  * Copyright (c) 2013-2014, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14
15 #include <linux/spinlock.h>
16 #include <linux/delay.h>
17 #include <linux/regulator/consumer.h>
18 #include <linux/tegra-powergate.h>
19
20 #include <asm/atomic.h>
21
22 #include "powergate-priv.h"
23 #include "powergate-ops-txx.h"
24 #include "powergate-ops-t1xx.h"
25 #include "dvfs.h"
26
27 enum mc_client {
28         MC_CLIENT_AFI           = 0,
29         MC_CLIENT_DC            = 2,
30         MC_CLIENT_DCB           = 3,
31         MC_CLIENT_ISP           = 8,
32         MC_CLIENT_MSENC         = 11,
33         MC_CLIENT_SATA          = 15,
34         MC_CLIENT_VDE           = 16,
35         MC_CLIENT_VI            = 17,
36         MC_CLIENT_VIC           = 18,
37         MC_CLIENT_XUSB_HOST     = 19,
38         MC_CLIENT_XUSB_DEV      = 20,
39         MC_CLIENT_ISPB          = 33,
40         MC_CLIENT_GPU           = 34,
41         MC_CLIENT_LAST          = -1,
42 };
43
44 struct tegra12x_powergate_mc_client_info {
45         enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
46 };
47
48 static struct tegra12x_powergate_mc_client_info tegra12x_pg_mc_info[] = {
49         [TEGRA_POWERGATE_CRAIL] = {
50                 .hot_reset_clients = {
51                         [0] = MC_CLIENT_LAST,
52                 },
53         },
54         [TEGRA_POWERGATE_GPU] = {
55                 .hot_reset_clients = {
56                         [0] = MC_CLIENT_GPU,
57                         [1] = MC_CLIENT_LAST,
58                 },
59         },
60         [TEGRA_POWERGATE_VDEC] = {
61                 .hot_reset_clients = {
62                         [0] = MC_CLIENT_VDE,
63                         [1] = MC_CLIENT_LAST,
64                 },
65         },
66         [TEGRA_POWERGATE_MPE] = {
67                 .hot_reset_clients = {
68                         [0] = MC_CLIENT_MSENC,
69                         [1] = MC_CLIENT_LAST,
70                 },
71         },
72         [TEGRA_POWERGATE_VENC] = {
73                 .hot_reset_clients = {
74                         [0] = MC_CLIENT_ISP,
75                         [1] = MC_CLIENT_ISPB,
76                         [2] = MC_CLIENT_VI,
77                         [3] = MC_CLIENT_LAST,
78                 },
79         },
80         [TEGRA_POWERGATE_CPU1] = {
81                 .hot_reset_clients = {
82                         [0] = MC_CLIENT_LAST,
83                 },
84         },
85         [TEGRA_POWERGATE_CPU2] = {
86                 .hot_reset_clients = {
87                         [0] = MC_CLIENT_LAST,
88                 },
89         },
90         [TEGRA_POWERGATE_CPU3] = {
91                 .hot_reset_clients = {
92                         [0] = MC_CLIENT_LAST,
93                 },
94         },
95         [TEGRA_POWERGATE_CELP] = {
96                 .hot_reset_clients = {
97                         [0] = MC_CLIENT_LAST,
98                 },
99         },
100         [TEGRA_POWERGATE_CPU0] = {
101                 .hot_reset_clients = {
102                         [0] = MC_CLIENT_LAST,
103                 },
104         },
105         [TEGRA_POWERGATE_C0NC] = {
106                 .hot_reset_clients = {
107                         [0] = MC_CLIENT_LAST,
108                 },
109         },
110         [TEGRA_POWERGATE_C1NC] = {
111                 .hot_reset_clients = {
112                         [0] = MC_CLIENT_LAST,
113                 },
114         },
115         [TEGRA_POWERGATE_DISA] = {
116                 .hot_reset_clients = {
117                         [0] = MC_CLIENT_DC,
118                         [1] = MC_CLIENT_LAST,
119                 },
120         },
121         [TEGRA_POWERGATE_DISB] = {
122                 .hot_reset_clients = {
123                         [0] = MC_CLIENT_DCB,
124                         [1] = MC_CLIENT_LAST,
125                 },
126         },
127         [TEGRA_POWERGATE_XUSBA] = {
128                 .hot_reset_clients = {
129                         [0] = MC_CLIENT_LAST,
130                 },
131         },
132         [TEGRA_POWERGATE_XUSBB] = {
133                 .hot_reset_clients = {
134                         [0] = MC_CLIENT_XUSB_DEV,
135                         [1] = MC_CLIENT_LAST
136                 },
137         },
138         [TEGRA_POWERGATE_XUSBC] = {
139                 .hot_reset_clients = {
140                         [0] = MC_CLIENT_XUSB_HOST,
141                         [1] = MC_CLIENT_LAST,
142                 },
143         },
144 #ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
145         [TEGRA_POWERGATE_PCIE] = {
146                 .hot_reset_clients = {
147                         [0] = MC_CLIENT_AFI,
148                         [1] = MC_CLIENT_LAST,
149                 },
150         },
151 #endif
152 #ifdef CONFIG_ARCH_TEGRA_HAS_SATA
153         [TEGRA_POWERGATE_SATA] = {
154                 .hot_reset_clients = {
155                         [0] = MC_CLIENT_SATA,
156                         [1] = MC_CLIENT_LAST,
157                 },
158         },
159 #endif
160         [TEGRA_POWERGATE_SOR] = {
161                 .hot_reset_clients = {
162                         [0] = MC_CLIENT_LAST,
163                 },
164         },
165 #ifdef CONFIG_ARCH_TEGRA_VIC
166         [TEGRA_POWERGATE_VIC] = {
167                 .hot_reset_clients = {
168                         [0] = MC_CLIENT_VIC,
169                         [1] = MC_CLIENT_LAST,
170                 },
171         },
172 #endif
173 };
174
175 static struct powergate_partition_info tegra12x_powergate_partition_info[] = {
176         [TEGRA_POWERGATE_CRAIL] = { .name = "crail" },
177         [TEGRA_POWERGATE_GPU] = {
178                 .name = "gpu",
179                 .clk_info = {
180                         [0] = { .clk_name = "gpu_ref", .clk_type = CLK_AND_RST },
181                         [1] = { .clk_name = "pll_p_out5", .clk_type = CLK_ONLY },
182                 },
183         },
184         [TEGRA_POWERGATE_VDEC] = {
185                 .name = "vde",
186                 .clk_info = {
187                         [0] = { .clk_name = "vde", .clk_type = CLK_AND_RST },
188                 },
189         },
190         [TEGRA_POWERGATE_MPE] = {
191                 .name = "mpe",
192                 .clk_info = {
193                         [0] = { .clk_name = "msenc.cbus", .clk_type = CLK_AND_RST },
194                 },
195         },
196         [TEGRA_POWERGATE_VENC] = {
197                 .name = "ve",
198                 .clk_info = {
199                         [0] = { .clk_name = "ispa", .clk_type = CLK_AND_RST },
200                         [1] = { .clk_name = "ispb", .clk_type = CLK_AND_RST },
201                         [2] = { .clk_name = "vi", .clk_type = CLK_AND_RST },
202                         [3] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
203                 },
204         },
205         [TEGRA_POWERGATE_CPU1] = { .name = "cpu1" },
206         [TEGRA_POWERGATE_CPU2] = { .name = "cpu2" },
207         [TEGRA_POWERGATE_CPU3] = { .name = "cpu3" },
208         [TEGRA_POWERGATE_CELP] = { .name = "celp" },
209         [TEGRA_POWERGATE_CPU0] = { .name = "cpu0" },
210         [TEGRA_POWERGATE_C0NC] = { .name = "c0nc" },
211         [TEGRA_POWERGATE_C1NC] = { .name = "c1nc" },
212         [TEGRA_POWERGATE_DISA] = {
213                 .name = "disa",
214                 .clk_info = {
215                         [0] = { .clk_name = "disp1", .clk_type = CLK_AND_RST },
216                 },
217         },
218         [TEGRA_POWERGATE_DISB] = {
219                 .name = "disb",
220                 .clk_info = {
221                         [0] = { .clk_name = "disp2", .clk_type = CLK_AND_RST },
222                 },
223         },
224         [TEGRA_POWERGATE_XUSBA] = {
225                 .name = "xusba",
226                 .clk_info = {
227                         [0] = { .clk_name = "xusb_ss", .clk_type = CLK_AND_RST },
228                 },
229         },
230         [TEGRA_POWERGATE_XUSBB] = {
231                 .name = "xusbb",
232                 .clk_info = {
233                         [0] = { .clk_name = "xusb_dev", .clk_type = CLK_AND_RST },
234                 },
235         },
236         [TEGRA_POWERGATE_XUSBC] = {
237                 .name = "xusbc",
238                 .clk_info = {
239                         [0] = { .clk_name = "xusb_host", .clk_type = CLK_AND_RST },
240                 },
241         },
242 #ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
243         [TEGRA_POWERGATE_PCIE] = {
244                 .name = "pcie",
245                 .clk_info = {
246                         [0] = { .clk_name = "afi", .clk_type = CLK_AND_RST },
247                         [1] = { .clk_name = "pcie", .clk_type = CLK_AND_RST },
248                         [2] = { .clk_name = "cml0", .clk_type = CLK_ONLY },
249                         [3] = { .clk_name = "pciex", .clk_type = RST_ONLY },
250                 },
251         },
252 #endif
253 #ifdef CONFIG_ARCH_TEGRA_HAS_SATA
254         [TEGRA_POWERGATE_SATA] = {
255                 .name = "sata",
256                 .clk_info = {
257                         [0] = { .clk_name = "sata", .clk_type = CLK_AND_RST },
258                         [1] = { .clk_name = "sata_oob", .clk_type = CLK_AND_RST },
259                         [2] = { .clk_name = "cml1", .clk_type = CLK_ONLY },
260                         [3] = { .clk_name = "sata_cold", .clk_type = RST_ONLY },
261                 },
262         },
263 #endif
264         [TEGRA_POWERGATE_SOR] = {
265                 .name = "sor",
266                 .clk_info = {
267                         [0] = { .clk_name = "sor0", .clk_type = CLK_AND_RST },
268                         [1] = { .clk_name = "dsia", .clk_type = CLK_AND_RST },
269                         [2] = { .clk_name = "dsib", .clk_type = CLK_AND_RST },
270                         [3] = { .clk_name = "hdmi", .clk_type = CLK_AND_RST },
271                         [4] = { .clk_name = "mipi-cal", .clk_type = CLK_AND_RST },
272                         [5] = { .clk_name = "dpaux", .clk_type = CLK_ONLY },
273                 },
274         },
275 #ifdef CONFIG_ARCH_TEGRA_VIC
276         [TEGRA_POWERGATE_VIC] = {
277                 .name = "vic",
278                 .clk_info = {
279                         [0] = { .clk_name = "vic03.cbus", .clk_type = CLK_AND_RST },
280                 },
281         },
282 #endif
283 };
284
285 #define MC_CLIENT_HOTRESET_CTRL         0x200
286 #define MC_CLIENT_HOTRESET_STAT         0x204
287 #define MC_CLIENT_HOTRESET_CTRL_1       0x970
288 #define MC_CLIENT_HOTRESET_STAT_1       0x974
289 #define MC_VIDEO_PROTECT_REG_CTRL       0x650
290
291 #define PMC_GPU_RG_CNTRL_0              0x2d4
292
293 static DEFINE_SPINLOCK(tegra12x_powergate_lock);
294 static DEFINE_MUTEX(tegra12x_powergate_disp_lock);
295
296 static struct dvfs_rail *gpu_rail;
297
298 #define HOTRESET_READ_COUNT     5
299 static bool tegra12x_stable_hotreset_check(u32 stat_reg, u32 *stat)
300 {
301         int i;
302         u32 cur_stat;
303         u32 prv_stat;
304         unsigned long flags;
305
306         spin_lock_irqsave(&tegra12x_powergate_lock, flags);
307         prv_stat = mc_read(stat_reg);
308         for (i = 0; i < HOTRESET_READ_COUNT; i++) {
309                 cur_stat = mc_read(stat_reg);
310                 if (cur_stat != prv_stat) {
311                         spin_unlock_irqrestore(&tegra12x_powergate_lock, flags);
312                         return false;
313                 }
314         }
315         *stat = cur_stat;
316         spin_unlock_irqrestore(&tegra12x_powergate_lock, flags);
317         return true;
318 }
319
320 int tegra12x_powergate_mc_enable(int id)
321 {
322         return 0;
323 }
324
325 int tegra12x_powergate_mc_disable(int id)
326 {
327         return 0;
328 }
329
330 int tegra12x_powergate_mc_flush(int id)
331 {
332         u32 idx, rst_ctrl, rst_stat;
333         u32 rst_ctrl_reg, rst_stat_reg;
334         enum mc_client mcClientBit;
335         unsigned long flags;
336         bool ret;
337
338         for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
339                 mcClientBit =
340                         tegra12x_pg_mc_info[id].hot_reset_clients[idx];
341                 if (mcClientBit == MC_CLIENT_LAST)
342                         break;
343
344                 if (mcClientBit < 32) {
345                         rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL;
346                         rst_stat_reg = MC_CLIENT_HOTRESET_STAT;
347                 } else {
348                         mcClientBit %= 32;
349                         rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL_1;
350                         rst_stat_reg = MC_CLIENT_HOTRESET_STAT_1;
351                 }
352
353                 spin_lock_irqsave(&tegra12x_powergate_lock, flags);
354
355                 rst_ctrl = mc_read(rst_ctrl_reg);
356                 rst_ctrl |= (1 << mcClientBit);
357                 mc_write(rst_ctrl, rst_ctrl_reg);
358
359                 spin_unlock_irqrestore(&tegra12x_powergate_lock, flags);
360
361                 do {
362                         udelay(10);
363                         rst_stat = 0;
364                         ret = tegra12x_stable_hotreset_check(rst_stat_reg, &rst_stat);
365                         if (!ret)
366                                 continue;
367                 } while (!(rst_stat & (1 << mcClientBit)));
368         }
369
370         return 0;
371 }
372
373 int tegra12x_powergate_mc_flush_done(int id)
374 {
375         u32 idx, rst_ctrl, rst_ctrl_reg;
376         enum mc_client mcClientBit;
377         unsigned long flags;
378
379         for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
380                 mcClientBit =
381                         tegra12x_pg_mc_info[id].hot_reset_clients[idx];
382                 if (mcClientBit == MC_CLIENT_LAST)
383                         break;
384
385                 if (mcClientBit < 32)
386                         rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL;
387                 else {
388                         mcClientBit %= 32;
389                         rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL_1;
390                 }
391
392                 spin_lock_irqsave(&tegra12x_powergate_lock, flags);
393
394                 rst_ctrl = mc_read(rst_ctrl_reg);
395                 rst_ctrl &= ~(1 << mcClientBit);
396                 mc_write(rst_ctrl, rst_ctrl_reg);
397                 mc_read(rst_ctrl_reg);
398
399                 spin_unlock_irqrestore(&tegra12x_powergate_lock, flags);
400         }
401
402         wmb();
403
404         return 0;
405 }
406
407 static int tegra12x_gpu_powergate(int id, struct powergate_partition_info *pg_info)
408 {
409         int ret;
410
411         /* If first clk_ptr is null, fill clk info for the partition */
412         if (!pg_info->clk_info[0].clk_ptr)
413                 get_clk_info(pg_info);
414
415         tegra_powergate_mc_flush(id);
416
417         udelay(10);
418
419         /* enable clamp */
420         pmc_write(0x1, PMC_GPU_RG_CNTRL_0);
421         pmc_read(PMC_GPU_RG_CNTRL_0);
422
423         udelay(10);
424
425         powergate_partition_assert_reset(pg_info);
426
427         udelay(10);
428
429         /*
430          * GPCPLL is already disabled before entering this function; reference
431          * clocks are enabled until now - disable them just before rail gating
432          */
433         partition_clk_disable(pg_info);
434
435         udelay(10);
436
437         if (gpu_rail && tegra_powergate_is_powered(id)) {
438                 ret = tegra_dvfs_rail_power_down(gpu_rail);
439                 if (ret)
440                         goto err_power_off;
441         } else
442                 pr_info("No GPU regulator?\n");
443
444         return 0;
445
446 err_power_off:
447         WARN(1, "Could not Railgate Partition %d", id);
448         return ret;
449 }
450
451 static int mc_check_vpr(void)
452 {
453         int ret = 0;
454         u32 val = mc_read(MC_VIDEO_PROTECT_REG_CTRL);
455         if ((val & 1) == 0) {
456                 pr_err("VPR configuration not locked down\n");
457                 ret = -EINVAL;
458         }
459         return ret;
460 }
461
462 static int tegra12x_gpu_unpowergate(int id,
463         struct powergate_partition_info *pg_info)
464 {
465         int ret = 0;
466         bool first = false;
467
468         ret = mc_check_vpr();
469         if (ret)
470                 return ret;
471
472         if (!gpu_rail) {
473                 gpu_rail = tegra_dvfs_get_rail_by_name("vdd_gpu");
474                 if (IS_ERR_OR_NULL(gpu_rail)) {
475                         WARN(1, "No GPU regulator?\n");
476                         goto err_power;
477                 }
478                 first = true;
479         } else {
480                 ret = tegra_dvfs_rail_power_up(gpu_rail);
481                 if (ret)
482                         goto err_power;
483         }
484
485         /* If first clk_ptr is null, fill clk info for the partition */
486         if (!pg_info->clk_info[0].clk_ptr)
487                 get_clk_info(pg_info);
488
489         /*
490          * GPU reference clocks are initially enabled - skip clock enable if
491          * 1st unpowergate, and in any case leave reference clock enabled on
492          * exit. GPCPLL is still disabled, and will be enabled by driver.
493          */
494         if (!first) {
495                 /* Un-Powergating fails if all clks are not enabled */
496                 ret = partition_clk_enable(pg_info);
497                 if (ret)
498                         goto err_clk_on;
499         }
500
501         udelay(10);
502
503         powergate_partition_assert_reset(pg_info);
504
505         udelay(10);
506
507         /* disable clamp */
508         pmc_write(0, PMC_GPU_RG_CNTRL_0);
509         pmc_read(PMC_GPU_RG_CNTRL_0);
510
511         udelay(10);
512
513         powergate_partition_deassert_reset(pg_info);
514
515         udelay(10);
516
517         tegra_powergate_mc_flush_done(id);
518
519         udelay(10);
520
521         return 0;
522
523 err_clk_on:
524         powergate_module(id);
525 err_power:
526         WARN(1, "Could not Un-Railgate %d", id);
527         return ret;
528 }
529
530 static atomic_t ref_count_dispa = ATOMIC_INIT(0);
531 static atomic_t ref_count_dispb = ATOMIC_INIT(0);
532 static atomic_t ref_count_venc = ATOMIC_INIT(0);
533 static atomic_t ref_count_pcie = ATOMIC_INIT(0);
534
535 #define CHECK_RET(x)                    \
536         do {                            \
537                 ret = (x);              \
538                 if (ret != 0)           \
539                         return ret;     \
540         } while (0)
541
542
543 static inline int tegra12x_powergate(int id)
544 {
545         if (tegra_powergate_is_powered(id))
546                 return tegra1xx_powergate(id,
547                         &tegra12x_powergate_partition_info[id]);
548         return 0;
549 }
550
551 static inline int tegra12x_unpowergate(int id)
552 {
553         if (!tegra_powergate_is_powered(id))
554                 return tegra1xx_unpowergate(id,
555                         &tegra12x_powergate_partition_info[id]);
556         return 0;
557 }
558
559 static int tegra12x_disp_powergate(int id)
560 {
561         int ret = 0;
562         int ref_counta = 0;
563         int ref_countb = 0;
564
565         mutex_lock(&tegra12x_powergate_disp_lock);
566
567         ref_counta = atomic_read(&ref_count_dispa);
568         ref_countb = atomic_read(&ref_count_dispb);
569
570         if (id == TEGRA_POWERGATE_DISA) {
571                 if (ref_counta > 0)
572                         ref_counta = atomic_dec_return(&ref_count_dispa);
573                 if ((ref_counta <= 0) &&
574                         tegra12x_powergate(TEGRA_POWERGATE_DISA)) {
575                         ret = -EBUSY;
576                         goto error_out;
577                 }
578         } else if (id == TEGRA_POWERGATE_DISB) {
579                 if (ref_countb > 0)
580                         ref_countb = atomic_dec_return(&ref_count_dispb);
581                 if ((ref_countb <= 0) &&
582                         tegra12x_powergate(TEGRA_POWERGATE_DISB)) {
583                         ret = -EBUSY;
584                         goto error_out;
585                 }
586         }
587
588         if ((ref_counta <= 0) && (ref_countb <= 0)) {
589                 if (tegra12x_powergate(TEGRA_POWERGATE_SOR)) {
590                         ret = -EBUSY;
591                         goto error_out;
592                 }
593         }
594
595 error_out:
596         mutex_unlock(&tegra12x_powergate_disp_lock);
597         return ret;
598 }
599
600 static int tegra12x_disp_unpowergate(int id)
601 {
602         int ret = 0;
603
604         mutex_lock(&tegra12x_powergate_disp_lock);
605         /* always unpowergate SOR partition */
606         if (tegra12x_unpowergate(TEGRA_POWERGATE_SOR)) {
607                 ret = -EBUSY;
608                 goto error_out;
609         }
610
611         if (id == TEGRA_POWERGATE_DISA)
612                 atomic_inc(&ref_count_dispa);
613         else if (id == TEGRA_POWERGATE_DISB)
614                 atomic_inc(&ref_count_dispb);
615         ret = tegra12x_unpowergate(id);
616
617 error_out:
618         mutex_unlock(&tegra12x_powergate_disp_lock);
619         return ret;
620 }
621
622 static int tegra12x_venc_powergate(int id)
623 {
624         int ret = 0;
625         int ref_count = 0;
626
627         if (!TEGRA_IS_VENC_POWERGATE_ID(id))
628                 return -EINVAL;
629
630         ref_count = atomic_dec_return(&ref_count_venc);
631         WARN_ON(ref_count < 0);
632
633         /* only powergate when decrementing ref_count from 1 to 0 */
634         if (ref_count == 0) {
635                 CHECK_RET(tegra12x_powergate(id));
636                 CHECK_RET(tegra12x_disp_powergate(TEGRA_POWERGATE_DISA));
637         }
638
639         return ret;
640 }
641
642 static int tegra12x_venc_unpowergate(int id)
643 {
644         int ret = 0;
645         int ref_count = 0;
646
647         if (!TEGRA_IS_VENC_POWERGATE_ID(id))
648                 return -EINVAL;
649
650         ref_count = atomic_inc_return(&ref_count_venc);
651         WARN_ON(ref_count < 1);
652
653         /* only unpowergate when incrementing ref_count from 0 to 1 */
654         if (ref_count == 1) {
655                 CHECK_RET(tegra12x_disp_unpowergate(TEGRA_POWERGATE_DISA));
656                 CHECK_RET(tegra12x_unpowergate(id));
657         }
658
659         return ret;
660 }
661
662 static int tegra12x_pcie_powergate(int id)
663 {
664         int ret = 0;
665         int ref_count = 0;
666
667         if (!TEGRA_IS_PCIE_POWERGATE_ID(id))
668                 return -EINVAL;
669
670         ref_count = atomic_dec_return(&ref_count_pcie);
671         WARN_ON(ref_count < 0);
672
673         /* only powergate when decrementing ref_count from 1 to 0 */
674         if (ref_count == 0)
675                 CHECK_RET(tegra12x_powergate(id));
676
677         return ret;
678 }
679
680 static int tegra12x_pcie_unpowergate(int id)
681 {
682         int ret = 0;
683         int ref_count = 0;
684
685         if (!TEGRA_IS_PCIE_POWERGATE_ID(id))
686                 return -EINVAL;
687
688         ref_count = atomic_inc_return(&ref_count_pcie);
689         WARN_ON(ref_count < 1);
690
691         /* only unpowergate when incrementing ref_count from 0 to 1 */
692         if (ref_count == 1)
693                 CHECK_RET(tegra12x_unpowergate(id));
694
695         return ret;
696 }
697
698 int tegra12x_powergate_partition(int id)
699 {
700         int ret;
701
702         if (TEGRA_IS_GPU_POWERGATE_ID(id)) {
703                 ret = tegra12x_gpu_powergate(id,
704                         &tegra12x_powergate_partition_info[id]);
705         } else if (TEGRA_IS_DISP_POWERGATE_ID(id))
706                 ret = tegra12x_disp_powergate(id);
707         else if (id == TEGRA_POWERGATE_CRAIL)
708                 ret = tegra_powergate_set(id, false);
709         else if (id == TEGRA_POWERGATE_VENC)
710                 ret = tegra12x_venc_powergate(id);
711         else if (id == TEGRA_POWERGATE_PCIE)
712                 ret = tegra12x_pcie_powergate(id);
713         else {
714                 /* call common power-gate API for t1xx */
715                 ret = tegra1xx_powergate(id,
716                         &tegra12x_powergate_partition_info[id]);
717         }
718
719         return ret;
720 }
721
722 int tegra12x_unpowergate_partition(int id)
723 {
724         int ret;
725
726         if (TEGRA_IS_GPU_POWERGATE_ID(id)) {
727                 ret = tegra12x_gpu_unpowergate(id,
728                         &tegra12x_powergate_partition_info[id]);
729         } else if (TEGRA_IS_DISP_POWERGATE_ID(id))
730                 ret = tegra12x_disp_unpowergate(id);
731         else if (id == TEGRA_POWERGATE_CRAIL)
732                 ret = tegra_powergate_set(id, true);
733         else if (id == TEGRA_POWERGATE_VENC)
734                 ret = tegra12x_venc_unpowergate(id);
735         else if (id == TEGRA_POWERGATE_PCIE)
736                 ret = tegra12x_pcie_unpowergate(id);
737         else {
738                 ret = tegra1xx_unpowergate(id,
739                         &tegra12x_powergate_partition_info[id]);
740         }
741
742         return ret;
743 }
744
745 int tegra12x_powergate_partition_with_clk_off(int id)
746 {
747         BUG_ON(TEGRA_IS_GPU_POWERGATE_ID(id));
748
749         return tegraxx_powergate_partition_with_clk_off(id,
750                 &tegra12x_powergate_partition_info[id]);
751 }
752
753 int tegra12x_unpowergate_partition_with_clk_on(int id)
754 {
755         BUG_ON(TEGRA_IS_GPU_POWERGATE_ID(id));
756
757         return tegraxx_unpowergate_partition_with_clk_on(id,
758                 &tegra12x_powergate_partition_info[id]);
759 }
760
761 const char *tegra12x_get_powergate_domain_name(int id)
762 {
763         return tegra12x_powergate_partition_info[id].name;
764 }
765
766 spinlock_t *tegra12x_get_powergate_lock(void)
767 {
768         return &tegra12x_powergate_lock;
769 }
770
771 bool tegra12x_powergate_skip(int id)
772 {
773         switch (id) {
774 #ifdef CONFIG_ARCH_TEGRA_HAS_SATA
775         case TEGRA_POWERGATE_SATA:
776 #endif
777                 return true;
778
779         default:
780                 return false;
781         }
782 }
783
784 bool tegra12x_powergate_is_powered(int id)
785 {
786         u32 status = 0;
787
788         if (TEGRA_IS_GPU_POWERGATE_ID(id)) {
789                 if (gpu_rail)
790                         return tegra_dvfs_is_rail_up(gpu_rail);
791         } else {
792                 status = pmc_read(PWRGATE_STATUS) & (1 << id);
793                 return !!status;
794         }
795         return status;
796 }
797
798 static int tegra12x_powergate_init_refcount(void)
799 {
800         bool disa_powered = tegra_powergate_is_powered(TEGRA_POWERGATE_DISA);
801         bool venc_powered = tegra_powergate_is_powered(TEGRA_POWERGATE_VENC);
802         bool pcie_powered = tegra_powergate_is_powered(TEGRA_POWERGATE_PCIE);
803
804         WARN_ON(venc_powered && !disa_powered);
805
806         /* if it wasn't powered on, power it on */
807         if (!disa_powered) {
808                 tegra12x_disp_unpowergate(TEGRA_POWERGATE_DISA);
809         } else { /* if it was, set the refcount to 1 */
810                 atomic_set(&ref_count_dispa, 1);
811         }
812         /* either way you end up with disa powered on and the
813          * refcount set to 1
814          */
815
816         if (venc_powered) {
817                 /* venc_unpowergate() take a ref_count on dispa to account for
818                  * the hardware dependency between the two. This needs to
819                  * happen here as well to match that behaviour.
820                  */
821                 atomic_inc(&ref_count_dispa);
822                 atomic_set(&ref_count_venc, 1);
823         } else {
824                 atomic_set(&ref_count_venc, 0);
825         }
826
827         /* PCIE needs refcount menchanism due to HW Bug#1320346.  PCIE should be
828          * powergated only when both XUSB and PCIE are not active.
829          */
830
831         if (pcie_powered)
832                 atomic_set(&ref_count_pcie, 1);
833         else
834                 atomic_set(&ref_count_pcie, 0);
835
836         return 0;
837 }
838
839 static struct powergate_ops tegra12x_powergate_ops = {
840         .soc_name = "tegra12x",
841
842         .num_powerdomains = TEGRA_NUM_POWERGATE,
843
844         .get_powergate_lock = tegra12x_get_powergate_lock,
845         .get_powergate_domain_name = tegra12x_get_powergate_domain_name,
846
847         .powergate_partition = tegra12x_powergate_partition,
848         .unpowergate_partition = tegra12x_unpowergate_partition,
849
850         .powergate_partition_with_clk_off =  tegra12x_powergate_partition_with_clk_off,
851         .unpowergate_partition_with_clk_on = tegra12x_unpowergate_partition_with_clk_on,
852
853         .powergate_mc_enable = tegra12x_powergate_mc_enable,
854         .powergate_mc_disable = tegra12x_powergate_mc_disable,
855
856         .powergate_mc_flush = tegra12x_powergate_mc_flush,
857         .powergate_mc_flush_done = tegra12x_powergate_mc_flush_done,
858
859         .powergate_skip = tegra12x_powergate_skip,
860
861         .powergate_init_refcount = tegra12x_powergate_init_refcount,
862         .powergate_is_powered = tegra12x_powergate_is_powered,
863 };
864
865 struct powergate_ops *tegra12x_powergate_init_chip_support(void)
866 {
867         return &tegra12x_powergate_ops;
868 }