2 * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
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.
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.
15 #include <linux/spinlock.h>
16 #include <linux/delay.h>
17 #include <linux/tegra-powergate.h>
19 #include <asm/atomic.h>
21 #include "powergate-priv.h"
22 #include "powergate-ops-txx.h"
23 #include "powergate-ops-t1xx.h"
35 MC_CLIENT_MPCORELP = 10,
41 MC_CLIENT_XUSB_HOST = 19,
42 MC_CLIENT_XUSB_DEV = 20,
43 MC_CLIENT_EMUCIF = 21,
46 MC_CLIENT_AFI = MC_CLIENT_LAST,
47 MC_CLIENT_MPE = MC_CLIENT_LAST,
48 MC_CLIENT_NV2 = MC_CLIENT_LAST,
49 MC_CLIENT_SATA = MC_CLIENT_LAST,
52 struct tegra11x_powergate_mc_client_info {
53 enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
56 static struct tegra11x_powergate_mc_client_info tegra11x_pg_mc_info[] = {
57 [TEGRA_POWERGATE_CRAIL] = {
58 .hot_reset_clients = {
62 [TEGRA_POWERGATE_3D] = {
63 .hot_reset_clients = {
68 [TEGRA_POWERGATE_VDEC] = {
69 .hot_reset_clients = {
74 [TEGRA_POWERGATE_MPE] = {
75 .hot_reset_clients = {
76 [0] = MC_CLIENT_MSENC,
80 [TEGRA_POWERGATE_VENC] = {
81 .hot_reset_clients = {
87 [TEGRA_POWERGATE_HEG] = {
88 .hot_reset_clients = {
94 [TEGRA_POWERGATE_CPU1] = {
95 .hot_reset_clients = {
99 [TEGRA_POWERGATE_CPU2] = {
100 .hot_reset_clients = {
101 [0] = MC_CLIENT_LAST,
104 [TEGRA_POWERGATE_CPU3] = {
105 .hot_reset_clients = {
106 [0] = MC_CLIENT_LAST,
109 [TEGRA_POWERGATE_CELP] = {
110 .hot_reset_clients = {
111 [0] = MC_CLIENT_LAST,
114 [TEGRA_POWERGATE_CPU0] = {
115 .hot_reset_clients = {
116 [0] = MC_CLIENT_LAST,
119 [TEGRA_POWERGATE_C0NC] = {
120 .hot_reset_clients = {
121 [0] = MC_CLIENT_LAST,
124 [TEGRA_POWERGATE_C1NC] = {
125 .hot_reset_clients = {
126 [0] = MC_CLIENT_LAST,
129 [TEGRA_POWERGATE_DISA] = {
130 .hot_reset_clients = {
132 [1] = MC_CLIENT_LAST,
135 [TEGRA_POWERGATE_DISB] = {
136 .hot_reset_clients = {
138 [1] = MC_CLIENT_LAST,
141 [TEGRA_POWERGATE_XUSBA] = {
142 .hot_reset_clients = {
143 [0] = MC_CLIENT_LAST,
146 [TEGRA_POWERGATE_XUSBB] = {
147 .hot_reset_clients = {
148 [0] = MC_CLIENT_XUSB_DEV,
152 [TEGRA_POWERGATE_XUSBC] = {
153 .hot_reset_clients = {
154 [0] = MC_CLIENT_XUSB_HOST,
155 [1] = MC_CLIENT_LAST,
160 static struct powergate_partition_info tegra11x_powergate_partition_info[] = {
161 [TEGRA_POWERGATE_CRAIL] = { .name = "crail" },
162 [TEGRA_POWERGATE_3D] = {
165 [0] = { .clk_name = "3d", .clk_type = CLK_AND_RST },
168 [TEGRA_POWERGATE_VDEC] = {
171 [0] = { .clk_name = "vde", .clk_type = CLK_AND_RST },
174 [TEGRA_POWERGATE_MPE] = {
177 [0] = { .clk_name = "msenc.cbus", .clk_type = CLK_AND_RST },
180 [TEGRA_POWERGATE_VENC] = {
183 [0] = { .clk_name = "isp", .clk_type = CLK_AND_RST },
184 [1] = { .clk_name = "vi", .clk_type = CLK_AND_RST },
185 [2] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
188 [TEGRA_POWERGATE_HEG] = {
191 [0] = { .clk_name = "2d.cbus", .clk_type = CLK_AND_RST },
192 [1] = { .clk_name = "epp.cbus", .clk_type = CLK_AND_RST },
195 [TEGRA_POWERGATE_CPU1] = { .name = "cpu1" },
196 [TEGRA_POWERGATE_CPU2] = { .name = "cpu2" },
197 [TEGRA_POWERGATE_CPU3] = { .name = "cpu3" },
198 [TEGRA_POWERGATE_CELP] = { .name = "celp" },
199 [TEGRA_POWERGATE_CPU0] = { .name = "cpu0" },
200 [TEGRA_POWERGATE_C0NC] = { .name = "c0nc" },
201 [TEGRA_POWERGATE_C1NC] = { .name = "c1nc" },
202 [TEGRA_POWERGATE_DISA] = {
205 [0] = { .clk_name = "disp1", .clk_type = CLK_AND_RST },
206 [1] = { .clk_name = "dsia", .clk_type = CLK_AND_RST },
207 [2] = { .clk_name = "dsib", .clk_type = CLK_AND_RST },
208 [3] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
209 [4] = { .clk_name = "mipi-cal", .clk_type = CLK_AND_RST },
212 [TEGRA_POWERGATE_DISB] = {
215 [0] = { .clk_name = "disp2", .clk_type = CLK_AND_RST },
216 [1] = { .clk_name = "hdmi", .clk_type = CLK_AND_RST },
219 [TEGRA_POWERGATE_XUSBA] = {
222 [0] = { .clk_name = "xusb_ss", .clk_type = CLK_AND_RST },
225 [TEGRA_POWERGATE_XUSBB] = {
228 [0] = { .clk_name = "xusb_dev", .clk_type = CLK_AND_RST },
231 [TEGRA_POWERGATE_XUSBC] = {
234 [0] = { .clk_name = "xusb_host", .clk_type = CLK_AND_RST },
239 static atomic_t ref_count_a = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISA */
240 static atomic_t ref_count_b = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISB */
242 static void __iomem *mipi_cal = IO_ADDRESS(TEGRA_MIPI_CAL_BASE);
243 static u32 mipi_cal_read(unsigned long reg)
245 return readl(mipi_cal + reg);
248 static void mipi_cal_write(u32 val, unsigned long reg)
250 writel_relaxed(val, mipi_cal + reg);
253 #define MC_CLIENT_HOTRESET_CTRL 0x200
254 #define MC_CLIENT_HOTRESET_STAT 0x204
256 static DEFINE_SPINLOCK(tegra11x_powergate_lock);
258 /* Forward Declarations */
259 int tegra11x_powergate_mc_flush(int id);
260 int tegra11x_powergate_mc_flush_done(int id);
261 int tegra11x_unpowergate_partition_with_clk_on(int id);
262 int tegra11x_powergate_partition_with_clk_off(int id);
264 bool tegra11x_powergate_check_clamping(int id)
268 * PCIE and VDE clamping masks are swapped with respect to their
271 if (id == TEGRA_POWERGATE_VDEC)
272 mask = (1 << TEGRA_POWERGATE_PCIE);
273 else if (id == TEGRA_POWERGATE_PCIE)
274 mask = (1 << TEGRA_POWERGATE_VDEC);
278 return !!(pmc_read(PWRGATE_CLAMP_STATUS) & mask);
281 #define HOTRESET_READ_COUNT 5
282 static bool tegra11x_stable_hotreset_check(u32 *stat)
289 spin_lock_irqsave(&tegra11x_powergate_lock, flags);
290 prv_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
291 for (i = 0; i < HOTRESET_READ_COUNT; i++) {
292 cur_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
293 if (cur_stat != prv_stat) {
294 spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
299 spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
304 * FIXME: sw war for mipi-cal calibration when unpowergating DISA partition
306 static void tegra11x_mipical_calibrate(int id)
308 struct reg_offset_val {
315 #define MIPI_CAL_MIPI_CAL_CTRL_0 0x0
316 #define MIPI_CAL_CIL_MIPI_CAL_STATUS_0 0x8
317 #define MIPI_CAL_CILA_MIPI_CAL_CONFIG_0 0x14
318 #define MIPI_CAL_CILB_MIPI_CAL_CONFIG_0 0x18
319 #define MIPI_CAL_CILC_MIPI_CAL_CONFIG_0 0x1c
320 #define MIPI_CAL_CILD_MIPI_CAL_CONFIG_0 0x20
321 #define MIPI_CAL_CILE_MIPI_CAL_CONFIG_0 0x24
322 #define MIPI_CAL_DSIA_MIPI_CAL_CONFIG_0 0x38
323 #define MIPI_CAL_DSIB_MIPI_CAL_CONFIG_0 0x3c
324 #define MIPI_CAL_DSIC_MIPI_CAL_CONFIG_0 0x40
325 #define MIPI_CAL_DSID_MIPI_CAL_CONFIG_0 0x44
327 static struct reg_offset_val mipi_cal_por_values[] = {
328 { MIPI_CAL_MIPI_CAL_CTRL_0, 0x2a000000 },
329 { MIPI_CAL_CILA_MIPI_CAL_CONFIG_0, 0x00200000 },
330 { MIPI_CAL_CILB_MIPI_CAL_CONFIG_0, 0x00200000 },
331 { MIPI_CAL_CILC_MIPI_CAL_CONFIG_0, 0x00200000 },
332 { MIPI_CAL_CILD_MIPI_CAL_CONFIG_0, 0x00200000 },
333 { MIPI_CAL_CILE_MIPI_CAL_CONFIG_0, 0x00000000 },
334 { MIPI_CAL_DSIA_MIPI_CAL_CONFIG_0, 0x00200000 },
335 { MIPI_CAL_DSIB_MIPI_CAL_CONFIG_0, 0x00200000 },
336 { MIPI_CAL_DSIC_MIPI_CAL_CONFIG_0, 0x00200000 },
337 { MIPI_CAL_DSID_MIPI_CAL_CONFIG_0, 0x00200000 },
341 if (id != TEGRA_POWERGATE_DISA)
344 spin_lock_irqsave(&tegra11x_powergate_lock, flags);
346 /* mipi cal por restore */
347 for (i = 0; i < ARRAY_SIZE(mipi_cal_por_values); i++) {
348 mipi_cal_write(mipi_cal_por_values[i].por_value,
349 mipi_cal_por_values[i].offset);
352 /* mipi cal status clear */
353 status = mipi_cal_read(MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
354 mipi_cal_write(status, MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
356 /* mipi cal status read - to flush writes */
357 status = mipi_cal_read(MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
359 spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
362 static int tegra11x_powergate_partition_internal(int id,
363 struct powergate_partition_info *pg_info)
367 if (tegra_powergate_is_powered(id)) {
368 ret = is_partition_clk_disabled(pg_info);
371 ret = tegra11x_powergate_partition_with_clk_off(id);
375 ret = tegra_powergate_partition(id);
383 static int tegra11x_unpowergate_partition_internal(int id,
384 struct powergate_partition_info *pg_info)
388 if (!tegra_powergate_is_powered(id)) {
389 ret = is_partition_clk_disabled(pg_info);
392 ret = tegra11x_unpowergate_partition_with_clk_on(id);
396 ret = tegra_unpowergate_partition(id);
405 * Tegra11x has powergate dependencies between partitions.
406 * This function captures the dependencies.
408 static int tegra11x_check_partition_pg_seq(int id,
409 struct powergate_partition_info *pg_info)
413 if (id == TEGRA_POWERGATE_DISA) {
414 ret = tegra11x_powergate_partition_internal(TEGRA_POWERGATE_VENC,
419 ret = tegra11x_powergate_partition_internal(TEGRA_POWERGATE_DISB,
429 * This function captures power-ungate dependencies between tegra11x partitions
431 static int tegra11x_check_partition_pug_seq(int id,
432 struct powergate_partition_info *pg_info)
437 case TEGRA_POWERGATE_DISB:
438 case TEGRA_POWERGATE_VENC:
439 ret = tegra11x_unpowergate_partition_internal(TEGRA_POWERGATE_DISA,
449 int tegra11x_powergate_mc_enable(int id)
454 int tegra11x_powergate_mc_disable(int id)
459 int tegra11x_powergate_mc_flush(int id)
461 u32 idx, rst_ctrl, rst_stat;
462 enum mc_client mcClientBit;
466 for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
468 tegra11x_pg_mc_info[id].hot_reset_clients[idx];
469 if (mcClientBit == MC_CLIENT_LAST)
472 spin_lock_irqsave(&tegra11x_powergate_lock, flags);
473 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
474 rst_ctrl |= (1 << mcClientBit);
475 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
477 spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
482 ret = tegra11x_stable_hotreset_check(&rst_stat);
485 } while (!(rst_stat & (1 << mcClientBit)));
491 int tegra11x_powergate_mc_flush_done(int id)
494 enum mc_client mcClientBit;
497 for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
499 tegra11x_pg_mc_info[id].hot_reset_clients[idx];
500 if (mcClientBit == MC_CLIENT_LAST)
503 spin_lock_irqsave(&tegra11x_powergate_lock, flags);
505 rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
506 rst_ctrl &= ~(1 << mcClientBit);
507 mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
509 spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
517 static int tegra11x_unpowergate(int id,
518 struct powergate_partition_info *pg_info)
522 /* If first clk_ptr is null, fill clk info for the partition */
523 if (!pg_info->clk_info[0].clk_ptr)
524 get_clk_info(pg_info);
526 if (tegra_powergate_is_powered(id))
527 return tegra_powergate_reset_module(pg_info);
529 ret = tegra_powergate_set(id, true);
535 powergate_partition_assert_reset(pg_info);
539 /* Un-Powergating fails if all clks are not enabled */
540 ret = partition_clk_enable(pg_info);
546 ret = tegra_powergate_remove_clamping(id);
552 tegra11x_mipical_calibrate(id);
554 powergate_partition_deassert_reset(pg_info);
558 tegra_powergate_mc_flush_done(id);
562 /* Disable all clks enabled earlier. Drivers should enable clks */
563 partition_clk_disable(pg_info);
568 partition_clk_disable(pg_info);
570 powergate_module(id);
572 WARN(1, "Could not Un-Powergate %d", id);
576 void tegra11x_powergate_dis_partition(void)
578 tegra1xx_powergate(TEGRA_POWERGATE_DISB,
579 &tegra11x_powergate_partition_info[TEGRA_POWERGATE_DISB]);
581 tegra11x_powergate_partition_internal(TEGRA_POWERGATE_VENC,
582 &tegra11x_powergate_partition_info[TEGRA_POWERGATE_DISA]);
584 tegra1xx_powergate(TEGRA_POWERGATE_DISA,
585 &tegra11x_powergate_partition_info[TEGRA_POWERGATE_DISA]);
588 /* The logic manages the ref-count for dis partitions. The dependency between
589 * disa and disb is hided from client. */
590 bool tegra11x_powergate_check_dis_refcount(int id, int op)
592 WARN_ONCE(atomic_read(&ref_count_a) < 0, "dis ref a count underflow");
593 WARN_ONCE(atomic_read(&ref_count_b) < 0, "dis ref b count underflow");
595 if (op && id == TEGRA_POWERGATE_DISA) {
596 if (atomic_inc_return(&ref_count_a) != 1)
598 } else if (op && id == TEGRA_POWERGATE_DISB) {
599 if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISA))
600 atomic_inc(&ref_count_a);
601 if (atomic_inc_return(&ref_count_b) != 1)
603 } else if (!op && id == TEGRA_POWERGATE_DISA) {
604 if (atomic_dec_return(&ref_count_a) != 0)
606 } else if (!op && id == TEGRA_POWERGATE_DISB) {
607 atomic_dec(&ref_count_a);
608 if (atomic_dec_return(&ref_count_b) != 0) {
610 } else if (atomic_read(&ref_count_a) == 0) {
611 tegra11x_powergate_dis_partition();
619 int tegra11x_powergate_partition(int id)
623 if ((id == TEGRA_POWERGATE_DISA || id == TEGRA_POWERGATE_DISB) &&
624 !tegra11x_powergate_check_dis_refcount(id, 0))
627 ret = tegra11x_check_partition_pg_seq(id,
628 &tegra11x_powergate_partition_info[id]);
632 /* call common power-gate API for t1xx */
633 ret = tegra1xx_powergate(id,
634 &tegra11x_powergate_partition_info[id]);
639 int tegra11x_unpowergate_partition(int id)
643 if ((id == TEGRA_POWERGATE_DISA || id == TEGRA_POWERGATE_DISB) &&
644 !tegra11x_powergate_check_dis_refcount(id, 1))
647 ret = tegra11x_check_partition_pug_seq(id,
648 &tegra11x_powergate_partition_info[id]);
652 /* t11x needs to calibrate mipi in un-power-gate sequence
653 * hence it cannot use common un-power-gate api tegra1xx_unpowergate */
654 ret = tegra11x_unpowergate(id,
655 &tegra11x_powergate_partition_info[id]);
660 int tegra11x_powergate_partition_with_clk_off(int id)
662 return tegraxx_powergate_partition_with_clk_off(id,
663 &tegra11x_powergate_partition_info[id]);
666 int tegra11x_unpowergate_partition_with_clk_on(int id)
668 return tegraxx_unpowergate_partition_with_clk_on(id,
669 &tegra11x_powergate_partition_info[id]);
672 const char *tegra11x_get_powergate_domain_name(int id)
674 return tegra11x_powergate_partition_info[id].name;
677 spinlock_t *tegra11x_get_powergate_lock(void)
679 return &tegra11x_powergate_lock;
682 int tegra11x_powergate_init_refcount(void)
684 if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISA))
685 atomic_set(&ref_count_a, 1);
687 atomic_set(&ref_count_a, 0);
689 if (tegra_powergate_is_powered(TEGRA_POWERGATE_DISB))
690 atomic_set(&ref_count_b, 1);
692 atomic_set(&ref_count_b, 0);
696 static struct powergate_ops tegra11x_powergate_ops = {
697 .soc_name = "tegra11x",
699 .num_powerdomains = TEGRA_NUM_POWERGATE,
701 .get_powergate_lock = tegra11x_get_powergate_lock,
702 .get_powergate_domain_name = tegra11x_get_powergate_domain_name,
704 .powergate_partition = tegra11x_powergate_partition,
705 .unpowergate_partition = tegra11x_unpowergate_partition,
707 .powergate_partition_with_clk_off = tegra11x_powergate_partition_with_clk_off,
708 .unpowergate_partition_with_clk_on = tegra11x_unpowergate_partition_with_clk_on,
710 .powergate_mc_enable = tegra11x_powergate_mc_enable,
711 .powergate_mc_disable = tegra11x_powergate_mc_disable,
713 .powergate_mc_flush = tegra11x_powergate_mc_flush,
714 .powergate_mc_flush_done = tegra11x_powergate_mc_flush_done,
716 .powergate_init_refcount = tegra11x_powergate_init_refcount,
717 .powergate_check_clamping = tegra11x_powergate_check_clamping,
720 struct powergate_ops *tegra11x_powergate_init_chip_support(void)
722 return &tegra11x_powergate_ops;