ARM: tegra11: Remove display from pg skiplist
[linux-3.10.git] / arch / arm / mach-tegra / powergate-t11x.c
1 /*
2  * Copyright (c) 2012, 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
18 #include <asm/atomic.h>
19
20 #include <mach/powergate.h>
21
22 #include "powergate-priv.h"
23 #include "powergate-ops-txx.h"
24 #include "powergate-ops-t1xx.h"
25
26 enum mc_client {
27         MC_CLIENT_AVPC          = 1,
28         MC_CLIENT_DC            = 2,
29         MC_CLIENT_DCB           = 3,
30         MC_CLIENT_EPP           = 4,
31         MC_CLIENT_G2            = 5,
32         MC_CLIENT_HC            = 6,
33         MC_CLIENT_HDA           = 7,
34         MC_CLIENT_ISP           = 8,
35         MC_CLIENT_MPCORE        = 9,
36         MC_CLIENT_MPCORELP      = 10,
37         MC_CLIENT_MSENC         = 11,
38         MC_CLIENT_NV            = 12,
39         MC_CLIENT_PPCS          = 14,
40         MC_CLIENT_VDE           = 16,
41         MC_CLIENT_VI            = 17,
42         MC_CLIENT_XUSB_HOST     = 19,
43         MC_CLIENT_XUSB_DEV      = 20,
44         MC_CLIENT_EMUCIF        = 21,
45         MC_CLIENT_TSEC          = 22,
46         MC_CLIENT_LAST          = -1,
47         MC_CLIENT_AFI           = MC_CLIENT_LAST,
48         MC_CLIENT_MPE           = MC_CLIENT_LAST,
49         MC_CLIENT_NV2           = MC_CLIENT_LAST,
50         MC_CLIENT_SATA          = MC_CLIENT_LAST,
51 };
52
53 struct tegra11x_powergate_mc_client_info {
54         enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
55 };
56
57 static struct tegra11x_powergate_mc_client_info tegra11x_pg_mc_info[] = {
58         [TEGRA_POWERGATE_3D] = {
59                 .hot_reset_clients = {
60                         [0] = MC_CLIENT_NV,
61                         [1] = MC_CLIENT_LAST,
62                 },
63         },
64         [TEGRA_POWERGATE_VDEC] = {
65                 .hot_reset_clients = {
66                         [0] = MC_CLIENT_VDE,
67                         [1] = MC_CLIENT_LAST,
68                 },
69         },
70         [TEGRA_POWERGATE_MPE] = {
71                 .hot_reset_clients = {
72                         [0] = MC_CLIENT_MSENC,
73                         [1] = MC_CLIENT_LAST,
74                 },
75         },
76         [TEGRA_POWERGATE_VENC] = {
77                 .hot_reset_clients = {
78                         [0] = MC_CLIENT_ISP,
79                         [1] = MC_CLIENT_VI,
80                         [2] = MC_CLIENT_LAST,
81                 },
82         },
83         [TEGRA_POWERGATE_HEG] = {
84                 .hot_reset_clients = {
85                         [0] = MC_CLIENT_G2,
86                         [1] = MC_CLIENT_EPP,
87                         [2] = MC_CLIENT_LAST,
88                 },
89         },
90         [TEGRA_POWERGATE_DISA] = {
91                 .hot_reset_clients = {
92                         [0] = MC_CLIENT_DC,
93                         [1] = MC_CLIENT_LAST,
94                 },
95         },
96         [TEGRA_POWERGATE_DISB] = {
97                 .hot_reset_clients = {
98                         [0] = MC_CLIENT_DCB,
99                         [1] = MC_CLIENT_LAST,
100                 },
101         },
102         [TEGRA_POWERGATE_XUSBA] = {
103                 .hot_reset_clients = {
104                         [0] = MC_CLIENT_LAST,
105                 },
106         },
107         [TEGRA_POWERGATE_XUSBB] = {
108                 .hot_reset_clients = {
109                         [0] = MC_CLIENT_XUSB_DEV,
110                         [1] = MC_CLIENT_LAST
111                 },
112         },
113         [TEGRA_POWERGATE_XUSBC] = {
114                 .hot_reset_clients = {
115                         [0] = MC_CLIENT_XUSB_HOST,
116                         [1] = MC_CLIENT_LAST,
117                 },
118         },
119 };
120
121 static struct powergate_partition_info tegra11x_powergate_partition_info[] = {
122         [TEGRA_POWERGATE_3D] = {
123                 .name = "3d",
124                 .clk_info = {
125                         [0] = { .clk_name = "3d", .clk_type = CLK_AND_RST },
126                 },
127         },
128         [TEGRA_POWERGATE_VDEC] = {
129                 .name = "vde",
130                 .clk_info = {
131                         [0] = { .clk_name = "vde", .clk_type = CLK_AND_RST },
132                 },
133         },
134         [TEGRA_POWERGATE_MPE] = {
135                 .name = "mpe",
136                 .clk_info = {
137                         [0] = { .clk_name = "msenc.cbus", .clk_type = CLK_AND_RST },
138                 },
139         },
140         [TEGRA_POWERGATE_VENC] = {
141                 .name = "ve",
142                 .clk_info = {
143                         [0] = { .clk_name = "isp", .clk_type = CLK_AND_RST },
144                         [1] = { .clk_name = "vi", .clk_type = CLK_AND_RST },
145                         [2] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
146                 },
147         },
148         [TEGRA_POWERGATE_HEG] = {
149                 .name = "heg",
150                 .clk_info = {
151                         [0] = { .clk_name = "2d.cbus", .clk_type = CLK_AND_RST },
152                         [1] = { .clk_name = "epp.cbus", .clk_type = CLK_AND_RST },
153                 },
154         },
155         [TEGRA_POWERGATE_DISA] = {
156                 .name = "disa",
157                 .clk_info = {
158                         [0] = { .clk_name = "disp1", .clk_type = CLK_AND_RST },
159                         [1] = { .clk_name = "dsia", .clk_type = CLK_AND_RST },
160                         [2] = { .clk_name = "dsib", .clk_type = CLK_AND_RST },
161                         [3] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
162                         [4] = { .clk_name = "mipi-cal", .clk_type = CLK_AND_RST },
163                 },
164         },
165         [TEGRA_POWERGATE_DISB] = {
166                 .name = "disb",
167                 .clk_info = {
168                         [0] = { .clk_name = "disp2", .clk_type = CLK_AND_RST },
169                         [1] = { .clk_name = "hdmi", .clk_type = CLK_AND_RST },
170                 },
171         },
172         [TEGRA_POWERGATE_XUSBA] = {
173                 .name = "xusba",
174                 .clk_info = {
175                         [0] = { .clk_name = "xusb_ss", .clk_type = CLK_AND_RST },
176                 },
177         },
178         [TEGRA_POWERGATE_XUSBB] = {
179                 .name = "xusbb",
180                 .clk_info = {
181                         [0] = { .clk_name = "xusb_dev", .clk_type = CLK_AND_RST },
182                 },
183         },
184         [TEGRA_POWERGATE_XUSBC] = {
185                 .name = "xusbc",
186                 .clk_info = {
187                         [0] = { .clk_name = "xusb_host", .clk_type = CLK_AND_RST },
188                 },
189         },
190 };
191
192 static atomic_t ref_count_a = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISA */
193 static atomic_t ref_count_b = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISB */
194
195 static void __iomem *mipi_cal = IO_ADDRESS(TEGRA_MIPI_CAL_BASE);
196 static u32 mipi_cal_read(unsigned long reg)
197 {
198         return readl(mipi_cal + reg);
199 }
200
201 static void mipi_cal_write(u32 val, unsigned long reg)
202 {
203         writel_relaxed(val, mipi_cal + reg);
204 }
205
206 #define MC_CLIENT_HOTRESET_CTRL         0x200
207 #define MC_CLIENT_HOTRESET_STAT         0x204
208
209 static DEFINE_SPINLOCK(tegra11x_powergate_lock);
210
211 /* Forward Declarations */
212 int tegra11x_powergate_mc_flush(int id);
213 int tegra11x_powergate_mc_flush_done(int id);
214 int tegra11x_unpowergate_partition_with_clk_on(int id);
215 int tegra11x_powergate_partition_with_clk_off(int id);
216
217 #define HOTRESET_READ_COUNT     5
218 static bool tegra11x_stable_hotreset_check(u32 *stat)
219 {
220         int i;
221         u32 cur_stat;
222         u32 prv_stat;
223         unsigned long flags;
224
225         spin_lock_irqsave(&tegra11x_powergate_lock, flags);
226         prv_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
227         for (i = 0; i < HOTRESET_READ_COUNT; i++) {
228                 cur_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
229                 if (cur_stat != prv_stat) {
230                         spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
231                         return false;
232                 }
233         }
234         *stat = cur_stat;
235         spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
236         return true;
237 }
238
239 /*
240  * FIXME: sw war for mipi-cal calibration when unpowergating DISA partition
241  */
242 static void tegra11x_mipical_calibrate(int id)
243 {
244         struct reg_offset_val {
245                 u32 offset;
246                 u32 por_value;
247         };
248         u32 status;
249         unsigned long flags;
250
251 #define MIPI_CAL_MIPI_CAL_CTRL_0                0x0
252 #define MIPI_CAL_CIL_MIPI_CAL_STATUS_0          0x8
253 #define MIPI_CAL_CILA_MIPI_CAL_CONFIG_0         0x14
254 #define MIPI_CAL_CILB_MIPI_CAL_CONFIG_0         0x18
255 #define MIPI_CAL_CILC_MIPI_CAL_CONFIG_0         0x1c
256 #define MIPI_CAL_CILD_MIPI_CAL_CONFIG_0         0x20
257 #define MIPI_CAL_CILE_MIPI_CAL_CONFIG_0         0x24
258 #define MIPI_CAL_DSIA_MIPI_CAL_CONFIG_0         0x38
259 #define MIPI_CAL_DSIB_MIPI_CAL_CONFIG_0         0x3c
260 #define MIPI_CAL_DSIC_MIPI_CAL_CONFIG_0         0x40
261 #define MIPI_CAL_DSID_MIPI_CAL_CONFIG_0         0x44
262
263         static struct reg_offset_val mipi_cal_por_values[] = {
264                 { MIPI_CAL_MIPI_CAL_CTRL_0, 0x2a000000 },
265                 { MIPI_CAL_CILA_MIPI_CAL_CONFIG_0, 0x00200000 },
266                 { MIPI_CAL_CILB_MIPI_CAL_CONFIG_0, 0x00200000 },
267                 { MIPI_CAL_CILC_MIPI_CAL_CONFIG_0, 0x00200000 },
268                 { MIPI_CAL_CILD_MIPI_CAL_CONFIG_0, 0x00200000 },
269                 { MIPI_CAL_CILE_MIPI_CAL_CONFIG_0, 0x00000000 },
270                 { MIPI_CAL_DSIA_MIPI_CAL_CONFIG_0, 0x00200000 },
271                 { MIPI_CAL_DSIB_MIPI_CAL_CONFIG_0, 0x00200000 },
272                 { MIPI_CAL_DSIC_MIPI_CAL_CONFIG_0, 0x00200000 },
273                 { MIPI_CAL_DSID_MIPI_CAL_CONFIG_0, 0x00200000 },
274         };
275         int i;
276
277         if (id != TEGRA_POWERGATE_DISA)
278                 return;
279
280         spin_lock_irqsave(&tegra11x_powergate_lock, flags);
281
282         /* mipi cal por restore */
283         for (i = 0; i < ARRAY_SIZE(mipi_cal_por_values); i++) {
284                 mipi_cal_write(mipi_cal_por_values[i].por_value,
285                         mipi_cal_por_values[i].offset);
286         }
287
288         /* mipi cal status clear */
289         status = mipi_cal_read(MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
290         mipi_cal_write(status, MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
291
292         /* mipi cal status read - to flush writes */
293         status = mipi_cal_read(MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
294
295         spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
296 }
297
298 static int tegra11x_powergate_partition_internal(int id,
299         struct powergate_partition_info *pg_info)
300 {
301         int ret;
302
303         if (tegra_powergate_is_powered(id)) {
304                 ret = is_partition_clk_disabled(pg_info);
305                 if (ret < 0) {
306                         /* clock enabled */
307                         ret = tegra11x_powergate_partition_with_clk_off(id);
308                         if (ret < 0)
309                                 return ret;
310                 } else {
311                         ret = tegra_powergate_partition(id);
312                         if (ret < 0)
313                                 return ret;
314                 }
315         }
316         return 0;
317 }
318
319 static int tegra11x_unpowergate_partition_internal(int id,
320         struct powergate_partition_info *pg_info)
321 {
322         int ret;
323
324         if (!tegra_powergate_is_powered(id)) {
325                 ret = is_partition_clk_disabled(pg_info);
326                 if (ret) {
327                         /* clock disabled */
328                         ret = tegra11x_unpowergate_partition_with_clk_on(id);
329                         if (ret < 0)
330                                 return ret;
331                 } else {
332                         ret = tegra_unpowergate_partition(id);
333                         if (ret < 0)
334                                 return ret;
335                 }
336         }
337         return 0;
338 }
339
340 /*
341  * Tegra11x has powergate dependencies between partitions.
342  * This function captures the dependencies.
343  */
344 static int tegra11x_check_partition_pg_seq(int id,
345         struct powergate_partition_info *pg_info)
346 {
347         int ret;
348
349         if (id == TEGRA_POWERGATE_DISA) {
350                 ret = tegra11x_powergate_partition_internal(TEGRA_POWERGATE_VENC,
351                         pg_info);
352                 if (ret < 0)
353                         return ret;
354
355                 ret = tegra11x_powergate_partition_internal(TEGRA_POWERGATE_DISB,
356                         pg_info);
357                 if (ret < 0)
358                         return ret;
359         }
360
361         return 0;
362 }
363
364 /*
365  * This function captures power-ungate dependencies between tegra11x partitions
366  */
367 static int tegra11x_check_partition_pug_seq(int id,
368         struct powergate_partition_info *pg_info)
369 {
370         int ret;
371
372         switch (id) {
373         case TEGRA_POWERGATE_DISB:
374         case TEGRA_POWERGATE_VENC:
375                 ret = tegra11x_unpowergate_partition_internal(TEGRA_POWERGATE_DISA,
376                         pg_info);
377                 if (ret < 0)
378                         return ret;
379
380                 break;
381         }
382         return 0;
383 }
384
385 int tegra11x_powergate_mc_enable(int id)
386 {
387         return 0;
388 }
389
390 int tegra11x_powergate_mc_disable(int id)
391 {
392         return 0;
393 }
394
395 int tegra11x_powergate_mc_flush(int id)
396 {
397         u32 idx, rst_ctrl, rst_stat;
398         enum mc_client mcClientBit;
399         unsigned long flags;
400         bool ret;
401
402         for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
403                 mcClientBit =
404                         tegra11x_pg_mc_info[id].hot_reset_clients[idx];
405                 if (mcClientBit == MC_CLIENT_LAST)
406                         break;
407
408                 spin_lock_irqsave(&tegra11x_powergate_lock, flags);
409                 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
410                 rst_ctrl |= (1 << mcClientBit);
411                 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
412
413                 spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
414
415                 do {
416                         udelay(10);
417                         rst_stat = 0;
418                         ret = tegra11x_stable_hotreset_check(&rst_stat);
419                         if (!ret)
420                                 continue;
421                 } while (!(rst_stat & (1 << mcClientBit)));
422         }
423
424         return 0;
425 }
426
427 int tegra11x_powergate_mc_flush_done(int id)
428 {
429         u32 idx, rst_ctrl;
430         enum mc_client mcClientBit;
431         unsigned long flags;
432
433         for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
434                 mcClientBit =
435                         tegra11x_pg_mc_info[id].hot_reset_clients[idx];
436                 if (mcClientBit == MC_CLIENT_LAST)
437                         break;
438
439                 spin_lock_irqsave(&tegra11x_powergate_lock, flags);
440
441                 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
442                 rst_ctrl &= ~(1 << mcClientBit);
443                 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
444
445                 spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
446         }
447
448         wmb();
449
450         return 0;
451 }
452
453 static int tegra11x_unpowergate(int id,
454         struct powergate_partition_info *pg_info)
455 {
456         int ret;
457
458         /* If first clk_ptr is null, fill clk info for the partition */
459         if (!pg_info->clk_info[0].clk_ptr)
460                 get_clk_info(pg_info);
461
462         if (tegra_powergate_is_powered(id))
463                 return tegra_powergate_reset_module(pg_info);
464
465         ret = tegra_powergate_set(id, true);
466         if (ret)
467                 goto err_power;
468
469         udelay(10);
470
471         /* Un-Powergating fails if all clks are not enabled */
472         ret = partition_clk_enable(pg_info);
473         if (ret)
474                 goto err_clk_on;
475
476         udelay(10);
477
478         ret = tegra_powergate_remove_clamping(id);
479         if (ret)
480                 goto err_clamp;
481
482         udelay(10);
483
484         tegra11x_mipical_calibrate(id);
485
486         powergate_partition_deassert_reset(pg_info);
487
488         udelay(10);
489
490         tegra_powergate_mc_flush_done(id);
491
492         udelay(10);
493
494         /* Disable all clks enabled earlier. Drivers should enable clks */
495         partition_clk_disable(pg_info);
496
497         return 0;
498
499 err_clamp:
500         partition_clk_disable(pg_info);
501 err_clk_on:
502         powergate_module(id);
503 err_power:
504         WARN(1, "Could not Un-Powergate %d", id);
505         return ret;
506 }
507
508 int tegra11x_powergate_partition(int id)
509 {
510         int ret;
511
512         WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
513         WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
514
515         if (id == TEGRA_POWERGATE_DISA && atomic_dec_return(&ref_count_a) != 0)
516                 return 0;
517         else if (id == TEGRA_POWERGATE_DISB &&
518                 atomic_dec_return(&ref_count_b) != 0)
519                 return 0;
520
521         ret = tegra11x_check_partition_pg_seq(id,
522                 &tegra11x_powergate_partition_info[id]);
523         if (ret)
524                 return ret;
525
526         /* call common power-gate API for t1xx */
527         ret = tegra1xx_powergate(id,
528                 &tegra11x_powergate_partition_info[id]);
529
530         return ret;
531 }
532
533 int tegra11x_unpowergate_partition(int id)
534 {
535         int ret;
536
537         WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
538         WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
539
540         if (id == TEGRA_POWERGATE_DISA && atomic_inc_return(&ref_count_a) != 1)
541                 return 0;
542         else if (id == TEGRA_POWERGATE_DISB &&
543                 atomic_inc_return(&ref_count_b) != 1)
544                 return 0;
545
546         ret = tegra11x_check_partition_pug_seq(id,
547                 &tegra11x_powergate_partition_info[id]);
548         if (ret)
549                 return ret;
550
551         /* t11x needs to calibrate mipi in un-power-gate sequence
552          * hence it cannot use common un-power-gate api tegra1xx_unpowergate */
553         ret = tegra11x_unpowergate(id,
554                 &tegra11x_powergate_partition_info[id]);
555
556         return ret;
557 }
558
559 int tegra11x_powergate_partition_with_clk_off(int id)
560 {
561         return tegraxx_powergate_partition_with_clk_off(id,
562                 &tegra11x_powergate_partition_info[id]);
563 }
564
565 int tegra11x_unpowergate_partition_with_clk_on(int id)
566 {
567         return tegraxx_unpowergate_partition_with_clk_on(id,
568                 &tegra11x_powergate_partition_info[id]);
569 }
570
571 const char *tegra11x_get_powergate_domain_name(int id)
572 {
573         return tegra11x_powergate_partition_info[id].name;
574 }
575
576 spinlock_t *tegra11x_get_powergate_lock(void)
577 {
578         return &tegra11x_powergate_lock;
579 }
580
581 static struct powergate_ops tegra11x_powergate_ops = {
582         .soc_name = "tegra11x",
583
584         .num_powerdomains = TEGRA_NUM_POWERGATE,
585
586         .get_powergate_lock = tegra11x_get_powergate_lock,
587         .get_powergate_domain_name = tegra11x_get_powergate_domain_name,
588
589         .powergate_partition = tegra11x_powergate_partition,
590         .unpowergate_partition = tegra11x_unpowergate_partition,
591
592         .powergate_partition_with_clk_off =  tegra11x_powergate_partition_with_clk_off,
593         .unpowergate_partition_with_clk_on = tegra11x_unpowergate_partition_with_clk_on,
594
595         .powergate_mc_enable = tegra11x_powergate_mc_enable,
596         .powergate_mc_disable = tegra11x_powergate_mc_disable,
597
598         .powergate_mc_flush = tegra11x_powergate_mc_flush,
599         .powergate_mc_flush_done = tegra11x_powergate_mc_flush_done,
600 };
601
602 struct powergate_ops *tegra11x_powergate_init_chip_support(void)
603 {
604         return &tegra11x_powergate_ops;
605 }