ARM: tegra: powergate: Add hot reset sequence for powergate
Jin Qian [Thu, 21 Apr 2011 19:55:47 +0000 (12:55 -0700)]
Original-Change-Id: I0e37b788c666ae99f46e7e6995c3700b0b23d412
Reviewed-on: http://git-master/r/29901
Tested-by: Jin Qian <jqian@nvidia.com>
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>

Rebase-Id: R24afa8e7f33265722cee139f37ad53774d1dcb96

arch/arm/mach-tegra/powergate.c

index adff436..14bd542 100644 (file)
@@ -50,9 +50,68 @@ static u8 tegra30_cpu_domains[] = {
        TEGRA_POWERGATE_CPU3,
 };
 
+#define MC_CLIENT_HOTRESET_CTRL        0x200
+#define MC_CLIENT_HOTRESET_STAT        0x204
+
+typedef enum {
+       MC_CLIENT_AFI   = 0,
+       MC_CLIENT_AVPC  = 1,
+       MC_CLIENT_DC    = 2,
+       MC_CLIENT_DCB   = 3,
+       MC_CLIENT_EPP   = 4,
+       MC_CLIENT_G2    = 5,
+       MC_CLIENT_HC    = 6,
+       MC_CLIENT_HDA   = 7,
+       MC_CLIENT_ISP   = 8,
+       MC_CLIENT_MPCORE        = 9,
+       MC_CLIENT_MPCORELP      = 10,
+       MC_CLIENT_MPE   = 11,
+       MC_CLIENT_NV    = 12,
+       MC_CLIENT_NV2   = 13,
+       MC_CLIENT_PPCS  = 14,
+       MC_CLIENT_SATA  = 15,
+       MC_CLIENT_VDE   = 16,
+       MC_CLIENT_VI    = 17,
+       MC_CLIENT_LAST  = -1,
+} MC_CLIENT;
+
 static DEFINE_SPINLOCK(tegra_powergate_lock);
 
+#define MAX_HOTRESET_CLIENT_NUM                3
+
+typedef struct {
+       const char * name;
+       MC_CLIENT hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
+       /* add clocks for each partition*/
+} powergate_partition;
+
+static powergate_partition powergate_partition_info[] = {
+       [TEGRA_POWERGATE_CPU]   = { "cpu0",     {MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_L2]    = { "l2",       {MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_3D]    = { "3d0",
+                                               {MC_CLIENT_NV, MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_PCIE]  = { "pcie",
+                                               {MC_CLIENT_AFI, MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_VDEC]  = { "vde",
+                                               {MC_CLIENT_VDE, MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_MPE]   = { "mpe",
+                                               {MC_CLIENT_MPE, MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_VENC]  = { "ve",
+                                               {MC_CLIENT_ISP, MC_CLIENT_VI, MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_CPU1]  = { "cpu1",     {MC_CLIENT_LAST}},
+       [TEGRA_POWERGATE_CPU2]  = { "cpu2",     {MC_CLIENT_LAST}},
+       [TEGRA_POWERGATE_CPU3]  = { "cpu3",     {MC_CLIENT_LAST}},
+       [TEGRA_POWERGATE_A9LP]  = { "a9lp",     {MC_CLIENT_LAST}},
+       [TEGRA_POWERGATE_SATA]  = { "sata",
+                                               {MC_CLIENT_SATA, MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_3D1]   = { "3d1",
+                                               {MC_CLIENT_NV2, MC_CLIENT_LAST} },
+       [TEGRA_POWERGATE_HEG]   = { "heg",
+                                               {MC_CLIENT_G2, MC_CLIENT_EPP, MC_CLIENT_HC} },
+};
+
 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
 
 static u32 pmc_read(unsigned long reg)
 {
@@ -64,6 +123,74 @@ static void pmc_write(u32 val, unsigned long reg)
        writel(val, pmc + reg);
 }
 
+static u32 mc_read(unsigned long reg)
+{
+       return readl(mc + reg);
+}
+
+static void mc_write(u32 val, unsigned long reg)
+{
+       writel(val, mc + reg);
+}
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+static void mc_flush(int id)
+{
+       u32 idx, rst_ctrl, rst_stat;
+       MC_CLIENT mcClientBit;
+       unsigned long flags;
+
+       BUG_ON(id < 0 || id >= tegra_num_powerdomains);
+
+       for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+               mcClientBit = powergate_partition_info[id].hot_reset_clients[idx];
+               if (mcClientBit == MC_CLIENT_LAST)
+                       break;
+
+               spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+               rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
+               rst_ctrl |= (1 << mcClientBit);
+               mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
+
+               spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+
+               do {
+                       udelay(10);
+                       rst_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
+               } while (!(rst_stat & (1 << mcClientBit)));
+       }
+}
+
+static void mc_flush_done(int id)
+{
+       u32 idx, rst_ctrl;
+       MC_CLIENT mcClientBit;
+       unsigned long flags;
+
+       BUG_ON(id < 0 || id >= tegra_num_powerdomains);
+
+       for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+               mcClientBit = powergate_partition_info[id].hot_reset_clients[idx];
+               if (mcClientBit == MC_CLIENT_LAST)
+                       break;
+
+               spin_lock_irqsave(&tegra_powergate_lock, flags);
+
+               rst_ctrl = mc_read(MC_CLIENT_HOTRESET_CTRL);
+               rst_ctrl &= ~(1 << mcClientBit);
+               mc_write(rst_ctrl, MC_CLIENT_HOTRESET_CTRL);
+
+               spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+       }
+
+       wmb();
+}
+#else
+static void mc_flush(int id) {}
+static void mc_flush_done(int id) {}
+#endif
+
 static int tegra_powergate_set(int id, bool new_state)
 {
        bool status;
@@ -98,6 +225,8 @@ int tegra_powergate_power_off(int id)
        if (id < 0 || id >= tegra_num_powerdomains)
                return -EINVAL;
 
+       mc_flush(id);
+
        return tegra_powergate_set(id, false);
 }
 
@@ -182,6 +311,8 @@ int tegra_powergate_sequence_power_up(int id, struct clk *clk)
        udelay(10);
        tegra_periph_reset_deassert(clk);
 
+       mc_flush_done(id);
+
        return 0;
 
 err_clamp:
@@ -205,13 +336,11 @@ int __init tegra_powergate_init(void)
        switch (tegra_chip_id) {
        case TEGRA20:
                tegra_num_powerdomains = 7;
-               powergate_name = powergate_name_t20;
                break;
        case TEGRA30:
                tegra_num_powerdomains = 14;
                tegra_num_cpu_domains = 4;
                tegra_cpu_domains = tegra30_cpu_domains;
-               powergate_name = powergate_name_t30;
                break;
        default:
                /* Unknown Tegra variant. Disable powergating */
@@ -222,41 +351,12 @@ int __init tegra_powergate_init(void)
        return 0;
 }
 
-static const char * const *powergate_name;
-
-static const char * const powergate_name_t20[] = {
-       [TEGRA_POWERGATE_CPU]   = "cpu",
-       [TEGRA_POWERGATE_3D]    = "3d",
-       [TEGRA_POWERGATE_VENC]  = "venc",
-       [TEGRA_POWERGATE_VDEC]  = "vdec",
-       [TEGRA_POWERGATE_PCIE]  = "pcie",
-       [TEGRA_POWERGATE_L2]    = "l2",
-       [TEGRA_POWERGATE_MPE]   = "mpe",
-};
-
-static const char * const powergate_name_t30[] = {
-       [TEGRA_POWERGATE_CPU]   = "cpu0",
-       [TEGRA_POWERGATE_3D]    = "3d0",
-       [TEGRA_POWERGATE_VENC]  = "venc",
-       [TEGRA_POWERGATE_VDEC]  = "vdec",
-       [TEGRA_POWERGATE_PCIE]  = "pcie",
-       [TEGRA_POWERGATE_L2]    = "l2",
-       [TEGRA_POWERGATE_MPE]   = "mpe",
-       [TEGRA_POWERGATE_HEG]   = "heg",
-       [TEGRA_POWERGATE_SATA]  = "sata",
-       [TEGRA_POWERGATE_CPU1]  = "cpu1",
-       [TEGRA_POWERGATE_CPU2]  = "cpu2",
-       [TEGRA_POWERGATE_CPU3]  = "cpu3",
-       [TEGRA_POWERGATE_CELP]  = "celp",
-       [TEGRA_POWERGATE_3D1]   = "3d1",
-};
-
 const char* tegra_powergate_get_name(int id)
 {
-       if (id < 0 || id >= TEGRA_NUM_POWERGATE)
+       if (id < 0 || id >= tegra_num_powerdomains)
                return "invalid";
 
-       return powergate_name[id];
+       return powergate_partition_info[id].name;
 }
 
 #ifdef CONFIG_DEBUG_FS
@@ -269,7 +369,7 @@ static int powergate_show(struct seq_file *s, void *data)
        seq_printf(s, "------------------\n");
 
        for (i = 0; i < tegra_num_powerdomains; i++)
-               seq_printf(s, " %9s %7s\n", powergate_name[i],
+               seq_printf(s, " %9s %7s\n", powergate_partition_info[i].name,
                        tegra_powergate_is_powered(i) ? "yes" : "no");
        return 0;
 }