summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorMayuresh Kulkarni <mkulkarni@nvidia.com>2012-11-16 19:28:17 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:49:30 -0700
commitf3d71c369c0c86088abf3d89bc588ed4d196a859 (patch)
treea5b6faf512d81939bbfe147e1368b3d9b111ae61 /arch
parent40e6d10a2e1a50f706404775811b3d884fe1ca36 (diff)
arm: tegra: refactor powergate code per-SoC
- split the current power-gate code into common and SoC specific code - every SoC now exports a set of ops to the common APIs - following is the new file structure: powergate.c - common powergate APIs powergate-ops-txx.c - t20/t30 specific common implementation powergate-ops-t1xx.c - t114 specific common implementation. this will also take care of t148 and t124 (in future) powergate-t20 - t20 specific structures and ids powergate-t30 - t30 specific strcutures and ids powergate-t11x - t11x specific structures and ids powergate-t14x - t14x specific structures and ids (stub in this commit. it will be populated in a separate commit as a part of bug 1190194) - powergate.c also provides a low level common APIs for all SoCs - each SoC now has 2 arrays: 1 for power partition info and other for mc client list info. amongst these 2 arrays, mc client list array is static Bug 1180197 Change-Id: I62b2d894c683fe9a18b82c7f9d87c08ce5b3864f Signed-off-by: Mayuresh Kulkarni <mkulkarni@nvidia.com> Reviewed-on: http://git-master/r/145591 Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com> GVS: Gerrit_Virtual_Submit
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/Makefile8
-rw-r--r--arch/arm/mach-tegra/powergate-ops-t1xx.c109
-rw-r--r--arch/arm/mach-tegra/powergate-ops-t1xx.h22
-rw-r--r--arch/arm/mach-tegra/powergate-ops-txx.c149
-rw-r--r--arch/arm/mach-tegra/powergate-ops-txx.h31
-rw-r--r--arch/arm/mach-tegra/powergate-priv.h154
-rw-r--r--arch/arm/mach-tegra/powergate-t11x.c666
-rw-r--r--arch/arm/mach-tegra/powergate-t14x.c35
-rw-r--r--arch/arm/mach-tegra/powergate-t20.c348
-rw-r--r--arch/arm/mach-tegra/powergate-t30.c358
-rw-r--r--arch/arm/mach-tegra/powergate.c1661
11 files changed, 2171 insertions, 1370 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 7dcd49a3cc31..e2ed380b0ac0 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -59,7 +59,15 @@ AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec)
obj-y += tegra_fuse.o
obj-y += kfuse.o
obj-y += csi.o
+
obj-y += powergate.o
+obj-y += powergate-ops-txx.o
+obj-y += powergate-ops-t1xx.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += powergate-t20.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += powergate-t30.o
+obj-$(CONFIG_ARCH_TEGRA_11x_SOC) += powergate-t11x.o
+obj-$(CONFIG_ARCH_TEGRA_14x_SOC) += powergate-t14x.o
+
obj-y += apbio.o
obj-y += mc.o
ifeq ($(CONFIG_USB_SUPPORT),y)
diff --git a/arch/arm/mach-tegra/powergate-ops-t1xx.c b/arch/arm/mach-tegra/powergate-ops-t1xx.c
new file mode 100644
index 000000000000..dd2d0aba9630
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-ops-t1xx.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+
+#include <mach/powergate.h>
+
+#include "powergate-priv.h"
+
+int tegra1xx_powergate(int id, struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ /* If first clk_ptr is null, fill clk info for the partition */
+ if (!pg_info->clk_info[0].clk_ptr)
+ get_clk_info(pg_info);
+
+ ret = partition_clk_enable(pg_info);
+ if (ret)
+ WARN(1, "Couldn't enable clock");
+
+ udelay(10);
+
+ tegra_powergate_mc_flush(id);
+
+ udelay(10);
+
+ powergate_partition_assert_reset(pg_info);
+
+ udelay(10);
+
+ /* Powergating is done only if refcnt of all clks is 0 */
+ partition_clk_disable(pg_info);
+
+ udelay(10);
+
+ ret = tegra_powergate_set(id, false);
+ if (ret)
+ goto err_power_off;
+
+ return 0;
+
+err_power_off:
+ WARN(1, "Could not Powergate Partition %d", id);
+ return ret;
+}
+
+int tegra1xx_unpowergate(int id, struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ /* If first clk_ptr is null, fill clk info for the partition */
+ if (!pg_info->clk_info[0].clk_ptr)
+ get_clk_info(pg_info);
+
+ if (tegra_powergate_is_powered(id))
+ return tegra_powergate_reset_module(pg_info);
+
+ ret = tegra_powergate_set(id, true);
+ if (ret)
+ goto err_power;
+
+ udelay(10);
+
+ /* Un-Powergating fails if all clks are not enabled */
+ ret = partition_clk_enable(pg_info);
+ if (ret)
+ goto err_clk_on;
+
+ udelay(10);
+
+ ret = tegra_powergate_remove_clamping(id);
+ if (ret)
+ goto err_clamp;
+
+ udelay(10);
+
+ powergate_partition_deassert_reset(pg_info);
+
+ udelay(10);
+
+ tegra_powergate_mc_flush_done(id);
+
+ udelay(10);
+
+ /* Disable all clks enabled earlier. Drivers should enable clks */
+ partition_clk_disable(pg_info);
+
+ return 0;
+
+err_clamp:
+ partition_clk_disable(pg_info);
+err_clk_on:
+ powergate_module(id);
+err_power:
+ WARN(1, "Could not Un-Powergate %d", id);
+ return ret;
+}
diff --git a/arch/arm/mach-tegra/powergate-ops-t1xx.h b/arch/arm/mach-tegra/powergate-ops-t1xx.h
new file mode 100644
index 000000000000..4b9528e7374a
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-ops-t1xx.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __POWERGATE_OPS_T1XX_H__
+#define __POWERGATE_OPS_T1XX_H__
+
+/* Common APIs for tegra1xx SoCs */
+int tegra1xx_powergate(int id, struct powergate_partition_info *pg_info);
+int tegra1xx_unpowergate(int id, struct powergate_partition_info *pg_info);
+
+#endif /* __POWERGATE_OPS_T1XX_H__ */
diff --git a/arch/arm/mach-tegra/powergate-ops-txx.c b/arch/arm/mach-tegra/powergate-ops-txx.c
new file mode 100644
index 000000000000..75246c610577
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-ops-txx.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+
+#include <mach/powergate.h>
+
+#include "powergate-priv.h"
+
+int tegraxx_powergate_partition(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ /* If first clk_ptr is null, fill clk info for the partition */
+ if (pg_info->clk_info[0].clk_ptr)
+ get_clk_info(pg_info);
+
+ powergate_partition_assert_reset(pg_info);
+
+ /* Powergating is done only if refcnt of all clks is 0 */
+ ret = is_partition_clk_disabled(pg_info);
+ if (ret)
+ goto err_clk_off;
+
+ ret = powergate_module(id);
+ if (ret)
+ goto err_power_off;
+
+ return 0;
+
+err_power_off:
+ WARN(1, "Could not Powergate Partition %d", id);
+err_clk_off:
+ WARN(1, "Could not Powergate Partition %d, all clks not disabled", id);
+ return ret;
+}
+
+int tegraxx_unpowergate_partition(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ /* If first clk_ptr is null, fill clk info for the partition */
+ if (!pg_info->clk_info[0].clk_ptr)
+ get_clk_info(pg_info);
+
+ if (tegra_powergate_is_powered(id))
+ return tegra_powergate_reset_module(pg_info);
+
+ ret = unpowergate_module(id);
+ if (ret)
+ goto err_power;
+
+ powergate_partition_assert_reset(pg_info);
+
+ /* Un-Powergating fails if all clks are not enabled */
+ ret = partition_clk_enable(pg_info);
+ if (ret)
+ goto err_clk_on;
+
+ udelay(10);
+
+ ret = tegra_powergate_remove_clamping(id);
+ if (ret)
+ goto err_clamp;
+
+ udelay(10);
+
+ powergate_partition_deassert_reset(pg_info);
+
+ tegra_powergate_mc_flush_done(id);
+
+ /* Disable all clks enabled earlier. Drivers should enable clks */
+ partition_clk_disable(pg_info);
+
+ return 0;
+
+err_clamp:
+ partition_clk_disable(pg_info);
+err_clk_on:
+ powergate_module(id);
+err_power:
+ WARN(1, "Could not Un-Powergate %d", id);
+
+ return ret;
+}
+
+int tegraxx_powergate_partition_with_clk_off(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret = 0;
+
+ /* Disable clks for the partition */
+ partition_clk_disable(pg_info);
+
+ ret = is_partition_clk_disabled(pg_info);
+ if (ret)
+ goto err_powergate_clk;
+
+ ret = tegra_powergate_partition(id);
+ if (ret)
+ goto err_powergating;
+
+ return ret;
+
+err_powergate_clk:
+ WARN(1, "Could not Powergate Partition %d, all clks not disabled", id);
+err_powergating:
+ partition_clk_enable(pg_info);
+ WARN(1, "Could not Powergate Partition %d", id);
+ return ret;
+}
+
+int tegraxx_unpowergate_partition_with_clk_on(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret = 0;
+
+ ret = tegra_unpowergate_partition(id);
+ if (ret)
+ goto err_unpowergating;
+
+ /* Enable clks for the partition */
+ ret = partition_clk_enable(pg_info);
+ if (ret)
+ goto err_unpowergate_clk;
+
+ return ret;
+
+err_unpowergate_clk:
+ tegra_powergate_partition(id);
+ WARN(1, "Could not Un-Powergate %d, err in enabling clk", id);
+err_unpowergating:
+ WARN(1, "Could not Un-Powergate %d", id);
+
+ return ret;
+}
diff --git a/arch/arm/mach-tegra/powergate-ops-txx.h b/arch/arm/mach-tegra/powergate-ops-txx.h
new file mode 100644
index 000000000000..7acbd06efed4
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-ops-txx.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __POWERGATE_OPS_TXX_H__
+#define __POWERGATE_OPS_TXX_H__
+
+/* Common APIs for tegra2 and tegra3 SoCs */
+int tegraxx_powergate_partition(int id,
+ struct powergate_partition_info *pg_info);
+
+int tegraxx_unpowergate_partition(int id,
+ struct powergate_partition_info *pg_info);
+
+int tegraxx_powergate_partition_with_clk_off(int id,
+ struct powergate_partition_info *pg_info);
+
+int tegraxx_unpowergate_partition_with_clk_on(int id,
+ struct powergate_partition_info *pg_info);
+
+#endif /* __POWERGATE_OPS_TXX_H__ */
diff --git a/arch/arm/mach-tegra/powergate-priv.h b/arch/arm/mach-tegra/powergate-priv.h
new file mode 100644
index 000000000000..a4f7acb518a9
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-priv.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __POWERGATE_PRIV_H__
+#define __POWERGATE_PRIV_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/powergate.h>
+
+#include "clock.h"
+#include "fuse.h"
+
+#define MAX_CLK_EN_NUM 9
+#define MAX_HOTRESET_CLIENT_NUM 4
+
+#define PWRGATE_TOGGLE 0x30
+#define PWRGATE_TOGGLE_START (1 << 8)
+#define REMOVE_CLAMPING 0x34
+#define PWRGATE_STATUS 0x38
+
+/* MC register read/write */
+static void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
+static inline u32 mc_read(unsigned long reg)
+{
+ return readl(mc + reg);
+}
+
+static inline void mc_write(u32 val, unsigned long reg)
+{
+ writel_relaxed(val, mc + reg);
+}
+
+/* PMC register read/write */
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static inline u32 pmc_read(unsigned long reg)
+{
+ return readl(pmc + reg);
+}
+
+static inline void pmc_write(u32 val, unsigned long reg)
+{
+ writel_relaxed(val, pmc + reg);
+}
+
+enum clk_type {
+ CLK_AND_RST,
+ RST_ONLY,
+ CLK_ONLY,
+};
+
+struct partition_clk_info {
+ const char *clk_name;
+ enum clk_type clk_type;
+ struct clk *clk_ptr;
+};
+
+struct powergate_partition_info {
+ const char *name;
+ struct partition_clk_info clk_info[MAX_CLK_EN_NUM];
+};
+
+struct powergate_ops {
+ const char *soc_name;
+
+ int num_powerdomains;
+ int num_cpu_domains;
+ u8 *cpu_domains;
+
+ spinlock_t *(*get_powergate_lock)(void);
+
+ const char *(*get_powergate_domain_name)(int id);
+
+ int (*powergate_partition)(int);
+ int (*unpowergate_partition)(int id);
+
+ int (*powergate_partition_with_clk_off)(int);
+ int (*unpowergate_partition_with_clk_on)(int);
+
+ int (*powergate_mc_enable)(int id);
+ int (*powergate_mc_disable)(int id);
+
+ int (*powergate_mc_flush)(int id);
+ int (*powergate_mc_flush_done)(int id);
+};
+
+void get_clk_info(struct powergate_partition_info *pg_info);
+int tegra_powergate_remove_clamping(int id);
+int partition_clk_enable(struct powergate_partition_info *pg_info);
+void partition_clk_disable(struct powergate_partition_info *pg_info);
+int is_partition_clk_disabled(struct powergate_partition_info *pg_info);
+void powergate_partition_deassert_reset(struct powergate_partition_info *pg_info);
+void powergate_partition_assert_reset(struct powergate_partition_info *pg_info);
+int tegra_powergate_reset_module(struct powergate_partition_info *pg_info);
+int powergate_module(int id);
+int unpowergate_module(int id);
+int tegra_powergate_set(int id, bool new_state);
+
+/* INIT APIs: New SoC needs to add its support here */
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+struct powergate_ops *tegra2_powergate_init_chip_support(void);
+#else
+static inline struct powergate_ops *tegra2_powergate_init_chip_support(void)
+{
+ return NULL;
+}
+#endif
+
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+struct powergate_ops *tegra3_powergate_init_chip_support(void);
+#else
+static inline struct powergate_ops *tegra3_powergate_init_chip_support(void)
+{
+ return NULL;
+}
+#endif
+
+#if defined(CONFIG_ARCH_TEGRA_11x_SOC)
+struct powergate_ops *tegra11x_powergate_init_chip_support(void);
+#else
+static inline struct powergate_ops *tegra11x_powergate_init_chip_support(void)
+{
+ return NULL;
+}
+#endif
+
+#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
+struct powergate_ops *tegra14x_powergate_init_chip_support(void);
+#else
+static inline struct powergate_ops *tegra14x_powergate_init_chip_support(void)
+{
+ return NULL;
+}
+#endif
+
+#endif /* __POWERGATE_PRIV_H__ */
diff --git a/arch/arm/mach-tegra/powergate-t11x.c b/arch/arm/mach-tegra/powergate-t11x.c
new file mode 100644
index 000000000000..1fba772c0e66
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-t11x.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+
+#include <mach/powergate.h>
+
+#include "powergate-priv.h"
+#include "powergate-ops-txx.h"
+#include "powergate-ops-t1xx.h"
+
+enum mc_client {
+ 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_MSENC = 11,
+ MC_CLIENT_NV = 12,
+ MC_CLIENT_PPCS = 14,
+ MC_CLIENT_VDE = 16,
+ MC_CLIENT_VI = 17,
+ MC_CLIENT_XUSB_HOST = 19,
+ MC_CLIENT_XUSB_DEV = 20,
+ MC_CLIENT_EMUCIF = 21,
+ MC_CLIENT_TSEC = 22,
+ MC_CLIENT_LAST = -1,
+ MC_CLIENT_AFI = MC_CLIENT_LAST,
+ MC_CLIENT_MPE = MC_CLIENT_LAST,
+ MC_CLIENT_NV2 = MC_CLIENT_LAST,
+ MC_CLIENT_SATA = MC_CLIENT_LAST,
+};
+
+struct tegra11x_powergate_mc_client_info {
+ enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
+};
+
+static struct tegra11x_powergate_mc_client_info tegra11x_pg_mc_info[] = {
+ [TEGRA_POWERGATE_3D] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_NV,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_VDEC] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_VDE,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_MSENC,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_VENC] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_ISP,
+ [1] = MC_CLIENT_VI,
+ [2] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_HEG] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_G2,
+ [1] = MC_CLIENT_EPP,
+ [2] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_DISA] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_DC,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_DISB] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_DCB,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_XUSBA] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_XUSBB] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_XUSB_DEV,
+ [1] = MC_CLIENT_LAST
+ },
+ },
+ [TEGRA_POWERGATE_XUSBC] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_XUSB_HOST,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+};
+
+static struct powergate_partition_info tegra11x_powergate_partition_info[] = {
+ [TEGRA_POWERGATE_3D] = {
+ .name = "3d",
+ .clk_info = {
+ [0] = { .clk_name = "3d", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_VDEC] = {
+ .name = "vde",
+ .clk_info = {
+ [0] = { .clk_name = "vde", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .name = "mpe",
+ .clk_info = {
+ [0] = { .clk_name = "msenc.cbus", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_VENC] = {
+ .name = "ve",
+ .clk_info = {
+ [0] = { .clk_name = "isp", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "vi", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_HEG] = {
+ .name = "heg",
+ .clk_info = {
+ [0] = { .clk_name = "2d.cbus", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "epp.cbus", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_DISA] = {
+ .name = "disa",
+ .clk_info = {
+ [0] = { .clk_name = "disp1", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "dsia", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "dsib", .clk_type = CLK_AND_RST },
+ [3] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
+ [4] = { .clk_name = "mipi-cal", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_DISB] = {
+ .name = "disb",
+ .clk_info = {
+ [0] = { .clk_name = "disp2", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "hdmi", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_XUSBA] = {
+ .name = "xusba",
+ .clk_info = {
+ [0] = { .clk_name = "xusb_ss", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_XUSBB] = {
+ .name = "xusbb",
+ .clk_info = {
+ [0] = { .clk_name = "xusb_dev", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_XUSBC] = {
+ .name = "xusbc",
+ .clk_info = {
+ [0] = { .clk_name = "xusb_host", .clk_type = CLK_AND_RST },
+ },
+ },
+};
+
+static atomic_t ref_count_a = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISA */
+static atomic_t ref_count_b = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISB */
+
+static void __iomem *mipi_cal = IO_ADDRESS(TEGRA_MIPI_CAL_BASE);
+static u32 mipi_cal_read(unsigned long reg)
+{
+ return readl(mipi_cal + reg);
+}
+
+static void mipi_cal_write(u32 val, unsigned long reg)
+{
+ writel_relaxed(val, mipi_cal + reg);
+}
+
+#define MC_CLIENT_HOTRESET_CTRL 0x200
+#define MC_CLIENT_HOTRESET_STAT 0x204
+
+static DEFINE_SPINLOCK(tegra11x_powergate_lock);
+
+/* Forward Declarations */
+int tegra11x_powergate_mc_flush(int id);
+int tegra11x_powergate_mc_flush_done(int id);
+int tegra11x_unpowergate_partition_with_clk_on(int id);
+int tegra11x_powergate_partition_with_clk_off(int id);
+
+static bool skip_pg_check(int id, bool is_unpowergate)
+{
+ /*
+ * FIXME: need to stress test partition power gating before
+ * enabling power gating for T11x
+ * List of T11x partition id which skip power gating
+ */
+ static int skip_pg_t11x_list[] = {
+ /*
+ * CPU and 3D partitions enable/disable
+ * is managed by respective modules
+ */
+ TEGRA_POWERGATE_DISA,
+ TEGRA_POWERGATE_DISB
+ };
+ int i;
+
+ /*
+ * skip unnecessary multiple calls e.g. powergate call when
+ * partition is already powered-off or vice-versa
+ */
+ if ((tegra_powergate_is_powered(id) && is_unpowergate) ||
+ (!(tegra_powergate_is_powered(id)) && (!is_unpowergate))) {
+
+ pr_err("Partition %s already powered-%s and %spowergate skipped\n",
+ tegra_powergate_get_name(id),
+ (tegra_powergate_is_powered(id)) ?
+ "on" : "off",
+ (is_unpowergate) ? "un" : "");
+
+ return true;
+ }
+
+ /* unpowergate is allowed for all partitions */
+ if (!tegra_powergate_is_powered(id) && is_unpowergate)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(skip_pg_t11x_list); i++) {
+ if (id == skip_pg_t11x_list[i]) {
+ pr_err("Partition %s %spowergate skipped\n",
+ tegra_powergate_get_name(id),
+ (is_unpowergate) ? "un" : "");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#define HOTRESET_READ_COUNT 5
+static bool tegra11x_stable_hotreset_check(u32 *stat)
+{
+ int i;
+ u32 cur_stat;
+ u32 prv_stat;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tegra11x_powergate_lock, flags);
+ prv_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
+ for (i = 0; i < HOTRESET_READ_COUNT; i++) {
+ cur_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
+ if (cur_stat != prv_stat) {
+ spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
+ return false;
+ }
+ }
+ *stat = cur_stat;
+ spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
+ return true;
+}
+
+/*
+ * FIXME: sw war for mipi-cal calibration when unpowergating DISA partition
+ */
+static void tegra11x_mipical_calibrate(int id)
+{
+ struct reg_offset_val {
+ u32 offset;
+ u32 por_value;
+ };
+ u32 status;
+ unsigned long flags;
+
+#define MIPI_CAL_MIPI_CAL_CTRL_0 0x0
+#define MIPI_CAL_CIL_MIPI_CAL_STATUS_0 0x8
+#define MIPI_CAL_CILA_MIPI_CAL_CONFIG_0 0x14
+#define MIPI_CAL_CILB_MIPI_CAL_CONFIG_0 0x18
+#define MIPI_CAL_CILC_MIPI_CAL_CONFIG_0 0x1c
+#define MIPI_CAL_CILD_MIPI_CAL_CONFIG_0 0x20
+#define MIPI_CAL_CILE_MIPI_CAL_CONFIG_0 0x24
+#define MIPI_CAL_DSIA_MIPI_CAL_CONFIG_0 0x38
+#define MIPI_CAL_DSIB_MIPI_CAL_CONFIG_0 0x3c
+#define MIPI_CAL_DSIC_MIPI_CAL_CONFIG_0 0x40
+#define MIPI_CAL_DSID_MIPI_CAL_CONFIG_0 0x44
+
+ static struct reg_offset_val mipi_cal_por_values[] = {
+ { MIPI_CAL_MIPI_CAL_CTRL_0, 0x2a000000 },
+ { MIPI_CAL_CILA_MIPI_CAL_CONFIG_0, 0x00200000 },
+ { MIPI_CAL_CILB_MIPI_CAL_CONFIG_0, 0x00200000 },
+ { MIPI_CAL_CILC_MIPI_CAL_CONFIG_0, 0x00200000 },
+ { MIPI_CAL_CILD_MIPI_CAL_CONFIG_0, 0x00200000 },
+ { MIPI_CAL_CILE_MIPI_CAL_CONFIG_0, 0x00000000 },
+ { MIPI_CAL_DSIA_MIPI_CAL_CONFIG_0, 0x00200000 },
+ { MIPI_CAL_DSIB_MIPI_CAL_CONFIG_0, 0x00200000 },
+ { MIPI_CAL_DSIC_MIPI_CAL_CONFIG_0, 0x00200000 },
+ { MIPI_CAL_DSID_MIPI_CAL_CONFIG_0, 0x00200000 },
+ };
+ int i;
+
+ if (id != TEGRA_POWERGATE_DISA)
+ return;
+
+ spin_lock_irqsave(&tegra11x_powergate_lock, flags);
+
+ /* mipi cal por restore */
+ for (i = 0; i < ARRAY_SIZE(mipi_cal_por_values); i++) {
+ mipi_cal_write(mipi_cal_por_values[i].por_value,
+ mipi_cal_por_values[i].offset);
+ }
+
+ /* mipi cal status clear */
+ status = mipi_cal_read(MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
+ mipi_cal_write(status, MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
+
+ /* mipi cal status read - to flush writes */
+ status = mipi_cal_read(MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
+
+ spin_unlock_irqrestore(&tegra11x_powergate_lock, flags);
+}
+
+static int tegra11x_powergate_partition_internal(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ if (tegra_powergate_is_powered(id)) {
+ ret = is_partition_clk_disabled(pg_info);
+ if (ret < 0) {
+ /* clock enabled */
+ ret = tegra11x_powergate_partition_with_clk_off(id);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = tegra_powergate_partition(id);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int tegra11x_unpowergate_partition_internal(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ if (!tegra_powergate_is_powered(id)) {
+ ret = is_partition_clk_disabled(pg_info);
+ if (ret) {
+ /* clock disabled */
+ ret = tegra11x_unpowergate_partition_with_clk_on(id);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = tegra_unpowergate_partition(id);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Tegra11x has powergate dependencies between partitions.
+ * This function captures the dependencies.
+ */
+static int tegra11x_check_partition_pg_seq(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ if (id == TEGRA_POWERGATE_DISA) {
+ ret = tegra11x_powergate_partition_internal(TEGRA_POWERGATE_VENC,
+ pg_info);
+ if (ret < 0)
+ return ret;
+
+ ret = tegra11x_powergate_partition_internal(TEGRA_POWERGATE_DISB,
+ pg_info);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * This function captures power-ungate dependencies between tegra11x partitions
+ */
+static int tegra11x_check_partition_pug_seq(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ switch (id) {
+ case TEGRA_POWERGATE_DISB:
+ case TEGRA_POWERGATE_VENC:
+ ret = tegra11x_unpowergate_partition_internal(TEGRA_POWERGATE_DISA,
+ pg_info);
+ if (ret < 0)
+ return ret;
+
+ break;
+ }
+ return 0;
+}
+
+int tegra11x_powergate_mc_enable(int id)
+{
+ return 0;
+}
+
+int tegra11x_powergate_mc_disable(int id)
+{
+ return 0;
+}
+
+int tegra11x_powergate_mc_flush(int id)
+{
+ u32 idx, rst_ctrl, rst_stat;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+ bool ret;
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ tegra11x_pg_mc_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra11x_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(&tegra11x_powergate_lock, flags);
+
+ do {
+ udelay(10);
+ rst_stat = 0;
+ ret = tegra11x_stable_hotreset_check(&rst_stat);
+ if (!ret)
+ continue;
+ } while (!(rst_stat & (1 << mcClientBit)));
+ }
+
+ return 0;
+}
+
+int tegra11x_powergate_mc_flush_done(int id)
+{
+ u32 idx, rst_ctrl;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ tegra11x_pg_mc_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra11x_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(&tegra11x_powergate_lock, flags);
+ }
+
+ wmb();
+
+ return 0;
+}
+
+static int tegra11x_unpowergate(int id,
+ struct powergate_partition_info *pg_info)
+{
+ int ret;
+
+ /* If first clk_ptr is null, fill clk info for the partition */
+ if (!pg_info->clk_info[0].clk_ptr)
+ get_clk_info(pg_info);
+
+ if (tegra_powergate_is_powered(id))
+ return tegra_powergate_reset_module(pg_info);
+
+ ret = tegra_powergate_set(id, true);
+ if (ret)
+ goto err_power;
+
+ udelay(10);
+
+ /* Un-Powergating fails if all clks are not enabled */
+ ret = partition_clk_enable(pg_info);
+ if (ret)
+ goto err_clk_on;
+
+ udelay(10);
+
+ ret = tegra_powergate_remove_clamping(id);
+ if (ret)
+ goto err_clamp;
+
+ udelay(10);
+
+ tegra11x_mipical_calibrate(id);
+
+ powergate_partition_deassert_reset(pg_info);
+
+ udelay(10);
+
+ tegra_powergate_mc_flush_done(id);
+
+ udelay(10);
+
+ /* Disable all clks enabled earlier. Drivers should enable clks */
+ partition_clk_disable(pg_info);
+
+ return 0;
+
+err_clamp:
+ partition_clk_disable(pg_info);
+err_clk_on:
+ powergate_module(id);
+err_power:
+ WARN(1, "Could not Un-Powergate %d", id);
+ return ret;
+}
+
+int tegra11x_powergate_partition(int id)
+{
+ int ret;
+
+ WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
+ WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
+
+ if (id == TEGRA_POWERGATE_DISA && atomic_dec_return(&ref_count_a) != 0)
+ return 0;
+ else if (id == TEGRA_POWERGATE_DISB &&
+ atomic_dec_return(&ref_count_b) != 0)
+ return 0;
+
+ if (skip_pg_check(id, false))
+ return 0;
+
+ ret = tegra11x_check_partition_pg_seq(id,
+ &tegra11x_powergate_partition_info[id]);
+ if (ret)
+ return ret;
+
+ /* call common power-gate API for t1xx */
+ ret = tegra1xx_powergate(id,
+ &tegra11x_powergate_partition_info[id]);
+
+ return ret;
+}
+
+int tegra11x_unpowergate_partition(int id)
+{
+ int ret;
+
+ WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
+ WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
+
+ if (id == TEGRA_POWERGATE_DISA && atomic_inc_return(&ref_count_a) != 1)
+ return 0;
+ else if (id == TEGRA_POWERGATE_DISB &&
+ atomic_inc_return(&ref_count_b) != 1)
+ return 0;
+
+ if (skip_pg_check(id, true))
+ return 0;
+
+ ret = tegra11x_check_partition_pug_seq(id,
+ &tegra11x_powergate_partition_info[id]);
+ if (ret)
+ return ret;
+
+ /* t11x needs to calibrate mipi in un-power-gate sequence
+ * hence it cannot use common un-power-gate api tegra1xx_unpowergate */
+ ret = tegra11x_unpowergate(id,
+ &tegra11x_powergate_partition_info[id]);
+
+ return ret;
+}
+
+int tegra11x_powergate_partition_with_clk_off(int id)
+{
+ if (skip_pg_check(id, false))
+ return 0;
+
+ return tegraxx_powergate_partition_with_clk_off(id,
+ &tegra11x_powergate_partition_info[id]);
+}
+
+int tegra11x_unpowergate_partition_with_clk_on(int id)
+{
+ if (skip_pg_check(id, true))
+ return 0;
+
+ return tegraxx_unpowergate_partition_with_clk_on(id,
+ &tegra11x_powergate_partition_info[id]);
+}
+
+const char *tegra11x_get_powergate_domain_name(int id)
+{
+ return tegra11x_powergate_partition_info[id].name;
+}
+
+spinlock_t *tegra11x_get_powergate_lock(void)
+{
+ return &tegra11x_powergate_lock;
+}
+
+static struct powergate_ops tegra11x_powergate_ops = {
+ .soc_name = "tegra11x",
+
+ .num_powerdomains = TEGRA_NUM_POWERGATE,
+
+ .get_powergate_lock = tegra11x_get_powergate_lock,
+ .get_powergate_domain_name = tegra11x_get_powergate_domain_name,
+
+ .powergate_partition = tegra11x_powergate_partition,
+ .unpowergate_partition = tegra11x_unpowergate_partition,
+
+ .powergate_partition_with_clk_off = tegra11x_powergate_partition_with_clk_off,
+ .unpowergate_partition_with_clk_on = tegra11x_unpowergate_partition_with_clk_on,
+
+ .powergate_mc_enable = tegra11x_powergate_mc_enable,
+ .powergate_mc_disable = tegra11x_powergate_mc_disable,
+
+ .powergate_mc_flush = tegra11x_powergate_mc_flush,
+ .powergate_mc_flush_done = tegra11x_powergate_mc_flush_done,
+};
+
+struct powergate_ops *tegra11x_powergate_init_chip_support(void)
+{
+ return &tegra11x_powergate_ops;
+}
diff --git a/arch/arm/mach-tegra/powergate-t14x.c b/arch/arm/mach-tegra/powergate-t14x.c
new file mode 100644
index 000000000000..a6a46dff41f7
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-t14x.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+
+#include <mach/powergate.h>
+
+#include "powergate-priv.h"
+#include "powergate-ops-txx.h"
+#include "powergate-ops-t1xx.h"
+
+static struct powergate_ops tegra14x_powergate_ops = {
+ .soc_name = "tegra14x",
+
+ .num_powerdomains = TEGRA_NUM_POWERGATE,
+};
+
+struct powergate_ops *tegra11x_powergate_init_chip_support(void)
+{
+ return NULL;
+}
diff --git a/arch/arm/mach-tegra/powergate-t20.c b/arch/arm/mach-tegra/powergate-t20.c
new file mode 100644
index 000000000000..32a48e1a9084
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-t20.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+#include <mach/powergate.h>
+
+#include "powergate-priv.h"
+#include "powergate-ops-txx.h"
+
+enum mc_client {
+ MC_CLIENT_AVPC = 0,
+ MC_CLIENT_DC = 1,
+ MC_CLIENT_DCB = 2,
+ MC_CLIENT_EPP = 3,
+ MC_CLIENT_G2 = 4,
+ MC_CLIENT_HC = 5,
+ MC_CLIENT_ISP = 6,
+ MC_CLIENT_MPCORE = 7,
+ MC_CLIENT_MPEA = 8,
+ MC_CLIENT_MPEB = 9,
+ MC_CLIENT_MPEC = 10,
+ MC_CLIENT_NV = 11,
+ MC_CLIENT_PPCS = 12,
+ MC_CLIENT_VDE = 13,
+ MC_CLIENT_VI = 14,
+ MC_CLIENT_LAST = -1,
+ MC_CLIENT_AFI = MC_CLIENT_LAST,
+};
+
+struct tegra2_powergate_mc_client_info {
+ enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
+};
+
+static struct tegra2_powergate_mc_client_info tegra2_pg_mc_info[] = {
+ [TEGRA_POWERGATE_CPU] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_L2] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_3D] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_NV,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+#ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
+ [TEGRA_POWERGATE_PCIE] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_AFI,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+#endif
+ [TEGRA_POWERGATE_VDEC] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_VDE,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_MPEA,
+ [1] = MC_CLIENT_MPEB,
+ [2] = MC_CLIENT_MPEC,
+ [3] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_VENC] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_ISP,
+ [1] = MC_CLIENT_VI,
+ [2] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_HEG] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_G2,
+ [1] = MC_CLIENT_EPP,
+ [2] = MC_CLIENT_HC,
+ [3] = MC_CLIENT_LAST,
+ },
+ },
+};
+
+static struct powergate_partition_info tegra2_powergate_partition_info[] = {
+ [TEGRA_POWERGATE_CPU] = { .name = "cpu0" },
+ [TEGRA_POWERGATE_L2] = { .name = "l2" },
+ [TEGRA_POWERGATE_3D] = {
+ .name = "3d0",
+ .clk_info = {
+ [0] = { .clk_name = "3d", .clk_type = CLK_AND_RST },
+ },
+ },
+#ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
+ [TEGRA_POWERGATE_PCIE] = {
+ .name = "pcie",
+ .clk_info = {
+ [0] = { .clk_name = "afi", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "pcie", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "pciex", .clk_type = RST_ONLY },
+ },
+ },
+#endif
+ [TEGRA_POWERGATE_VDEC] = {
+ .name = "vde",
+ .clk_info = {
+ [0] = { .clk_name = "vde", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .name = "mpe",
+ .clk_info = {
+ [0] = { .clk_name = "mpe", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_VENC] = {
+ .name = "ve",
+ .clk_info = {
+ [0] = { .clk_name = "isp", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "vi", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_HEG] = {
+ .name = "heg",
+ .clk_info = {
+ [0] = { .clk_name = "2d", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "epp", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "host1x", .clk_type = CLK_AND_RST },
+ },
+ },
+};
+
+#define MC_CLIENT_CTRL 0x100
+#define MC_CLIENT_HOTRESETN 0x104
+#define MC_CLIENT_ORRC_BASE 0x140
+
+static DEFINE_SPINLOCK(tegra2_powergate_lock);
+
+int tegra2_powergate_partition(int id)
+{
+ return tegraxx_powergate_partition(id,
+ &tegra2_powergate_partition_info[id]);
+}
+
+int tegra2_unpowergate_partition(int id)
+{
+ return tegraxx_unpowergate_partition(id,
+ &tegra2_powergate_partition_info[id]);
+}
+
+int tegra2_powergate_partition_with_clk_off(int id)
+{
+ /* Restrict functions use to selected partitions */
+ if (id != TEGRA_POWERGATE_PCIE) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return tegraxx_powergate_partition_with_clk_off(id,
+ &tegra2_powergate_partition_info[id]);
+}
+
+int tegra2_unpowergate_partition_with_clk_on(int id)
+{
+ /* Restrict this functions use to few partitions */
+ if (id != TEGRA_POWERGATE_PCIE) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return tegraxx_unpowergate_partition_with_clk_on(id,
+ &tegra2_powergate_partition_info[id]);
+}
+
+int tegra2_powergate_mc_enable(int id)
+{
+ u32 idx, clt_ctrl;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ tegra2_pg_mc_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra2_powergate_lock, flags);
+
+ /* enable client */
+ clt_ctrl = mc_read(MC_CLIENT_CTRL);
+ clt_ctrl |= (1 << mcClientBit);
+ mc_write(clt_ctrl, MC_CLIENT_CTRL);
+
+ /* read back to flush write */
+ clt_ctrl = mc_read(MC_CLIENT_CTRL);
+
+ spin_unlock_irqrestore(&tegra2_powergate_lock, flags);
+ }
+
+ return 0;
+}
+
+int tegra2_powergate_mc_disable(int id)
+{
+ u32 idx, clt_ctrl, orrc_reg;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ tegra2_pg_mc_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra2_powergate_lock, flags);
+
+ /* clear client enable bit */
+ clt_ctrl = mc_read(MC_CLIENT_CTRL);
+ clt_ctrl &= ~(1 << mcClientBit);
+ mc_write(clt_ctrl, MC_CLIENT_CTRL);
+
+ /* read back to flush write */
+ clt_ctrl = mc_read(MC_CLIENT_CTRL);
+
+ spin_unlock_irqrestore(&tegra2_powergate_lock, flags);
+
+ /* wait for outstanding requests to reach 0 */
+ orrc_reg = MC_CLIENT_ORRC_BASE + (mcClientBit * 4);
+ while (mc_read(orrc_reg) != 0)
+ udelay(10);
+ }
+
+ return 0;
+}
+
+int tegra2_powergate_mc_flush(int id)
+{
+ u32 idx, hot_rstn;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ tegra2_pg_mc_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra2_powergate_lock, flags);
+
+ /* assert hotreset (client module is currently in reset) */
+ hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+ hot_rstn &= ~(1 << mcClientBit);
+ mc_write(hot_rstn, MC_CLIENT_HOTRESETN);
+
+ /* read back to flush write */
+ hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+
+ spin_unlock_irqrestore(&tegra2_powergate_lock, flags);
+ }
+
+ return 0;
+}
+
+int tegra2_powergate_mc_flush_done(int id)
+{
+ u32 idx, hot_rstn;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ tegra2_pg_mc_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra2_powergate_lock, flags);
+
+ /* deassert hotreset */
+ hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+ hot_rstn |= (1 << mcClientBit);
+ mc_write(hot_rstn, MC_CLIENT_HOTRESETN);
+
+ /* read back to flush write */
+ hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+
+ spin_unlock_irqrestore(&tegra2_powergate_lock, flags);
+ }
+
+ return 0;
+}
+
+const char *tegra2_get_powergate_domain_name(int id)
+{
+ return tegra2_powergate_partition_info[id].name;
+}
+
+spinlock_t *tegra2_get_powergate_lock(void)
+{
+ return &tegra2_powergate_lock;
+}
+
+static struct powergate_ops tegra2_powergate_ops = {
+ .soc_name = "tegra2",
+
+ .num_powerdomains = TEGRA_NUM_POWERGATE,
+ .num_cpu_domains = 0,
+ .cpu_domains = NULL,
+
+ .get_powergate_lock = tegra2_get_powergate_lock,
+
+ .get_powergate_domain_name = tegra2_get_powergate_domain_name,
+
+ .powergate_partition = tegra2_powergate_partition,
+ .unpowergate_partition = tegra2_unpowergate_partition,
+
+ .powergate_partition_with_clk_off = tegra2_powergate_partition_with_clk_off,
+ .unpowergate_partition_with_clk_on = tegra2_unpowergate_partition_with_clk_on,
+
+ .powergate_mc_enable = tegra2_powergate_mc_enable,
+ .powergate_mc_disable = tegra2_powergate_mc_disable,
+
+ .powergate_mc_flush = tegra2_powergate_mc_flush,
+ .powergate_mc_flush_done = tegra2_powergate_mc_flush_done,
+};
+
+struct powergate_ops *tegra2_powergate_init_chip_support(void)
+{
+ return &tegra2_powergate_ops;
+}
diff --git a/arch/arm/mach-tegra/powergate-t30.c b/arch/arm/mach-tegra/powergate-t30.c
new file mode 100644
index 000000000000..a645f364ab69
--- /dev/null
+++ b/arch/arm/mach-tegra/powergate-t30.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+#include <mach/powergate.h>
+
+#include "powergate-priv.h"
+#include "powergate-ops-txx.h"
+
+enum mc_client {
+ 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,
+};
+
+struct tegra3_powergate_mc_client_info {
+ enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
+};
+
+static struct tegra3_powergate_mc_client_info tegra3_pg_mc_info[] = {
+ [TEGRA_POWERGATE_CPU] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_L2] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_3D] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_NV,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+#ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
+ [TEGRA_POWERGATE_PCIE] = {
+ .hot_reset_clients ={
+ [0] = MC_CLIENT_AFI,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+#endif
+ [TEGRA_POWERGATE_VDEC] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_VDE,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_MPE,
+ [1] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_VENC] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_ISP,
+ [1] = MC_CLIENT_VI,
+ [2] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_CPU1] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_CPU2] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_CPU3] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+ [TEGRA_POWERGATE_CELP] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_LAST,
+ },
+ },
+#ifdef CONFIG_ARCH_TEGRA_HAS_SATA
+ [TEGRA_POWERGATE_SATA] = {
+ .hot_reset_clients ={
+ [0] = MC_CLIENT_SATA,
+ [1] = MC_CLIENT_LAST
+ },
+ },
+#endif
+#ifdef CONFIG_ARCH_TEGRA_HAS_DUAL_3D
+ [TEGRA_POWERGATE_3D1] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_NV2,
+ [1] = MC_CLIENT_LAST
+ },
+ },
+#endif
+ [TEGRA_POWERGATE_HEG] = {
+ .hot_reset_clients = {
+ [0] = MC_CLIENT_G2,
+ [1] = MC_CLIENT_EPP,
+ [2] = MC_CLIENT_HC,
+ [3] = MC_CLIENT_LAST
+ },
+ },
+};
+
+static struct powergate_partition_info tegra3_powergate_partition_info[] = {
+ [TEGRA_POWERGATE_CPU] = { .name = "cpu0" },
+ [TEGRA_POWERGATE_L2] = { .name = "l2" },
+ [TEGRA_POWERGATE_3D] = {
+ .name = "3d0",
+ .clk_info = {
+ [0] = { .clk_name = "3d", .clk_type = CLK_AND_RST },
+ },
+ },
+#ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
+ [TEGRA_POWERGATE_PCIE] = {
+ .name = "pcie",
+ .clk_info = {
+ [0] = { .clk_name = "afi", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "pcie", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "cml0", .clk_type = CLK_ONLY },
+ [3] = { .clk_name = "pciex", .clk_type = RST_ONLY },
+ },
+ },
+#endif
+ [TEGRA_POWERGATE_VDEC] = {
+ .name = "vde",
+ .clk_info = {
+ [0] = { .clk_name = "vde", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .name = "mpe",
+ .clk_info = {
+ [0] = { .clk_name = "mpe.cbus", CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_VENC] = {
+ .name = "ve",
+ .clk_info = {
+ [0] = { .clk_name = "isp", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "vi", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "csi", .clk_type = CLK_AND_RST },
+ },
+ },
+ [TEGRA_POWERGATE_CPU1] = { .name = "cpu1" },
+ [TEGRA_POWERGATE_CPU2] = { .name = "cpu2" },
+ [TEGRA_POWERGATE_CPU3] = { .name = "cpu3" },
+ [TEGRA_POWERGATE_CELP] = { .name = "celp" },
+#ifdef CONFIG_ARCH_TEGRA_HAS_SATA
+ [TEGRA_POWERGATE_SATA] = {
+ .name = "sata",
+ .clk_info = {
+ [0] = { .clk_name = "sata", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "sata_oob", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "cml1", .clk_type = CLK_ONLY },
+ [3] = { .clk_name = "sata_cold", .clk_type = RST_ONLY },
+ },
+ },
+#endif
+#ifdef CONFIG_ARCH_TEGRA_HAS_DUAL_3D
+ [TEGRA_POWERGATE_3D1] = {
+ .name = "3d1",
+ .clk_info = {
+ [0] = { .clk_name = "3d2", .clk_type = CLK_AND_RST },
+ },
+ },
+#endif
+ [TEGRA_POWERGATE_HEG] = {
+ .name = "heg",
+ .clk_info = {
+ [0] = { .clk_name = "2d.cbus", .clk_type = CLK_AND_RST },
+ [1] = { .clk_name = "epp.cbus", .clk_type = CLK_AND_RST },
+ [2] = { .clk_name = "host1x.cbus", .clk_type = CLK_AND_RST },
+ },
+ },
+};
+
+static u8 tegra3_quad_cpu_domains[] = {
+ TEGRA_POWERGATE_CPU0,
+ TEGRA_POWERGATE_CPU1,
+ TEGRA_POWERGATE_CPU2,
+ TEGRA_POWERGATE_CPU3,
+};
+
+#define MC_CLIENT_HOTRESET_CTRL 0x200
+#define MC_CLIENT_HOTRESET_STAT 0x204
+
+static DEFINE_SPINLOCK(tegra3_powergate_lock);
+
+int tegra3_powergate_partition(int id)
+{
+ return tegraxx_powergate_partition(id,
+ &tegra3_powergate_partition_info[id]);
+}
+
+int tegra3_unpowergate_partition(int id)
+{
+ return tegraxx_unpowergate_partition(id,
+ &tegra3_powergate_partition_info[id]);
+}
+
+int tegra3_powergate_partition_with_clk_off(int id)
+{
+ if (id != TEGRA_POWERGATE_PCIE && id != TEGRA_POWERGATE_SATA) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return tegraxx_powergate_partition_with_clk_off(id,
+ &tegra3_powergate_partition_info[id]);
+}
+
+int tegra3_unpowergate_partition_with_clk_on(int id)
+{
+ if (id != TEGRA_POWERGATE_SATA && id != TEGRA_POWERGATE_PCIE) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return tegraxx_unpowergate_partition_with_clk_on(id,
+ &tegra3_powergate_partition_info[id]);
+}
+
+int tegra3_powergate_mc_enable(int id)
+{
+ return 0;
+}
+
+int tegra3_powergate_mc_disable(int id)
+{
+ return 0;
+}
+
+int tegra3_powergate_mc_flush(int id)
+{
+ u32 idx, rst_ctrl, rst_stat;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ tegra3_pg_mc_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra3_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(&tegra3_powergate_lock, flags);
+
+ do {
+ udelay(10);
+ rst_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
+ } while (!(rst_stat & (1 << mcClientBit)));
+ }
+
+ return 0;
+}
+
+int tegra3_powergate_mc_flush_done(int id)
+{
+ u32 idx, rst_ctrl;
+ enum mc_client mcClientBit;
+ unsigned long flags;
+
+ for (idx = 0; idx < MAX_HOTRESET_CLIENT_NUM; idx++) {
+ mcClientBit =
+ tegra3_pg_mc_info[id].hot_reset_clients[idx];
+ if (mcClientBit == MC_CLIENT_LAST)
+ break;
+
+ spin_lock_irqsave(&tegra3_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(&tegra3_powergate_lock, flags);
+ }
+
+ wmb();
+
+ return 0;
+}
+
+const char *tegra3_get_powergate_domain_name(int id)
+{
+ return tegra3_powergate_partition_info[id].name;
+}
+
+spinlock_t *tegra3_get_powergate_lock(void)
+{
+ return &tegra3_powergate_lock;
+}
+
+static struct powergate_ops tegra3_powergate_ops = {
+ .soc_name = "tegra3",
+
+ .num_powerdomains = TEGRA_NUM_POWERGATE,
+ .num_cpu_domains = 4,
+ .cpu_domains = tegra3_quad_cpu_domains,
+
+ .get_powergate_lock = tegra3_get_powergate_lock,
+
+ .get_powergate_domain_name = tegra3_get_powergate_domain_name,
+
+ .powergate_partition = tegra3_powergate_partition,
+ .unpowergate_partition = tegra3_unpowergate_partition,
+
+ .powergate_partition_with_clk_off = tegra3_powergate_partition_with_clk_off,
+ .unpowergate_partition_with_clk_on = tegra3_unpowergate_partition_with_clk_on,
+
+ .powergate_mc_enable = tegra3_powergate_mc_enable,
+ .powergate_mc_disable = tegra3_powergate_mc_disable,
+
+ .powergate_mc_flush = tegra3_powergate_mc_flush,
+ .powergate_mc_flush_done = tegra3_powergate_mc_flush_done,
+};
+
+struct powergate_ops *tegra3_powergate_init_chip_support(void)
+{
+ return &tegra3_powergate_ops;
+}
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
index 21d9d4bf2c79..de8af12df3a3 100644
--- a/arch/arm/mach-tegra/powergate.c
+++ b/arch/arm/mach-tegra/powergate.c
@@ -1,8 +1,8 @@
/*
- * drivers/powergate/tegra-powergate.c
+ * arch/arm/mach-tegra/powergate.c
*
* Copyright (c) 2010 Google, Inc
- * Copyright (C) 2011-2012 NVIDIA Corporation.
+ * Copyright (c) 2011 - 2012, NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Colin Cross <ccross@google.com>
@@ -38,620 +38,40 @@
#include "clock.h"
#include "fuse.h"
#include "iomap.h"
+#include "powergate-priv.h"
-#if defined(DEBUG_T11x_POWERGATE)
-static void test_powergate_parts(void);
-#endif
-#if defined(DEBUG_T11x_POWERUNGATE)
-static void test_powerungate_parts(void);
-#endif
-#if defined(DEBUG_T11x_POWERGATE_CLK_OFF)
-static void test_powergate_clk_off_parts(void);
-#endif
-#if defined(DEBUG_T11x_POWERUNGATE_CLK_OFF)
-static void test_unpowergate_clk_on_parts(void);
-#endif
-
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-static int tegra11x_check_partition_pug_seq(int id);
-static int tegra11x_unpowergate(int id);
-#endif
-
-#define PWRGATE_TOGGLE 0x30
-#define PWRGATE_TOGGLE_START (1 << 8)
-
-#define REMOVE_CLAMPING 0x34
-
-#define PWRGATE_STATUS 0x38
-
-#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
-enum mc_client {
- 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,
-};
-#elif defined(CONFIG_ARCH_TEGRA_2x_SOC)
-enum mc_client {
- MC_CLIENT_AVPC = 0,
- MC_CLIENT_DC = 1,
- MC_CLIENT_DCB = 2,
- MC_CLIENT_EPP = 3,
- MC_CLIENT_G2 = 4,
- MC_CLIENT_HC = 5,
- MC_CLIENT_ISP = 6,
- MC_CLIENT_MPCORE = 7,
- MC_CLIENT_MPEA = 8,
- MC_CLIENT_MPEB = 9,
- MC_CLIENT_MPEC = 10,
- MC_CLIENT_NV = 11,
- MC_CLIENT_PPCS = 12,
- MC_CLIENT_VDE = 13,
- MC_CLIENT_VI = 14,
- MC_CLIENT_LAST = -1,
- MC_CLIENT_AFI = MC_CLIENT_LAST,
-};
-#else
-/* bit positions are specific to chip */
-enum mc_client {
- 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_MSENC = 11,
- MC_CLIENT_NV = 12,
- MC_CLIENT_PPCS = 14,
- MC_CLIENT_VDE = 16,
- MC_CLIENT_VI = 17,
- MC_CLIENT_XUSB_HOST = 19,
- MC_CLIENT_XUSB_DEV = 20,
- MC_CLIENT_EMUCIF = 21,
- MC_CLIENT_TSEC = 22,
- MC_CLIENT_LAST = -1,
- MC_CLIENT_AFI = MC_CLIENT_LAST,
- MC_CLIENT_MPE = MC_CLIENT_LAST,
- MC_CLIENT_NV2 = MC_CLIENT_LAST,
- MC_CLIENT_SATA = MC_CLIENT_LAST,
-};
-#endif
-
-#define MAX_CLK_EN_NUM 9
-
-static int tegra_num_powerdomains;
-static int tegra_num_cpu_domains;
-static u8 *tegra_cpu_domains;
-static u8 tegra_quad_cpu_domains[] = {
- TEGRA_POWERGATE_CPU0,
- TEGRA_POWERGATE_CPU1,
- TEGRA_POWERGATE_CPU2,
- TEGRA_POWERGATE_CPU3,
-};
-
-static DEFINE_SPINLOCK(tegra_powergate_lock);
-
-#define MAX_HOTRESET_CLIENT_NUM 4
-
-enum clk_type {
- CLK_AND_RST,
- RST_ONLY,
- CLK_ONLY,
-};
-
-struct partition_clk_info {
- const char *clk_name;
- enum clk_type clk_type;
- /* true if clk is only used in assert/deassert reset and not while enable-den*/
- struct clk *clk_ptr;
-};
-
-struct powergate_partition {
- const char *name;
- enum mc_client hot_reset_clients[MAX_HOTRESET_CLIENT_NUM];
- struct partition_clk_info clk_info[MAX_CLK_EN_NUM];
-};
-
-static struct powergate_partition powergate_partition_info[TEGRA_NUM_POWERGATE] = {
- [TEGRA_POWERGATE_CPU] = { "cpu0", {MC_CLIENT_LAST}, },
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
- [TEGRA_POWERGATE_L2] = { "l2", {MC_CLIENT_LAST}, },
- [TEGRA_POWERGATE_3D] = { "3d0",
-#else
- [TEGRA_POWERGATE_3D] = { "3d",
-#endif
- {MC_CLIENT_NV, MC_CLIENT_LAST},
- {{"3d", CLK_AND_RST} }, },
-/* T11x does not have pcie */
-#if !defined(CONFIG_ARCH_TEGRA_11x_SOC)
-#ifdef CONFIG_ARCH_TEGRA_HAS_PCIE
- [TEGRA_POWERGATE_PCIE] = { "pcie",
- {MC_CLIENT_AFI, MC_CLIENT_LAST},
- {{"afi", CLK_AND_RST},
- {"pcie", CLK_AND_RST},
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
- {"cml0", CLK_ONLY},
-#endif
- {"pciex", RST_ONLY} }, },
-#endif
-#endif
- [TEGRA_POWERGATE_VDEC] = { "vde",
- {MC_CLIENT_VDE, MC_CLIENT_LAST},
- {{"vde", CLK_AND_RST} }, },
- [TEGRA_POWERGATE_MPE] = { "mpe",
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
- {MC_CLIENT_MPE, MC_CLIENT_LAST},
- {{"mpe.cbus", CLK_AND_RST}, },
-#elif defined(CONFIG_ARCH_TEGRA_2x_SOC)
- {MC_CLIENT_MPEA, MC_CLIENT_MPEB,
- MC_CLIENT_MPEC, MC_CLIENT_LAST},
- {{"mpe", CLK_AND_RST}, },
-#else
- {MC_CLIENT_MSENC,
- MC_CLIENT_LAST},
- {{"msenc.cbus", CLK_AND_RST}, },
-#endif
- },
- [TEGRA_POWERGATE_VENC] = { "ve",
- {
- MC_CLIENT_ISP,
- MC_CLIENT_VI,
- MC_CLIENT_LAST
- },
- {
- {"isp", CLK_AND_RST},
- {"vi", CLK_AND_RST},
- {"csi", CLK_AND_RST}
- },
- },
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
- [TEGRA_POWERGATE_CPU1] = { "cpu1", {MC_CLIENT_LAST}, },
- [TEGRA_POWERGATE_CPU2] = { "cpu2", {MC_CLIENT_LAST}, },
- [TEGRA_POWERGATE_CPU3] = { "cpu3", {MC_CLIENT_LAST}, },
- [TEGRA_POWERGATE_CELP] = { "celp", {MC_CLIENT_LAST}, },
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
-#ifdef CONFIG_ARCH_TEGRA_HAS_SATA
- [TEGRA_POWERGATE_SATA] = { "sata", {MC_CLIENT_SATA, MC_CLIENT_LAST},
- {{"sata", CLK_AND_RST},
- {"sata_oob", CLK_AND_RST},
- {"cml1", CLK_ONLY},
- {"sata_cold", RST_ONLY} }, },
-#endif
-#ifdef CONFIG_ARCH_TEGRA_HAS_DUAL_3D
- [TEGRA_POWERGATE_3D1] = { "3d1",
- {MC_CLIENT_NV2, MC_CLIENT_LAST},
- {{"3d2", CLK_AND_RST} }, },
-#endif
-#endif
- [TEGRA_POWERGATE_HEG] = { "heg",
- {
- MC_CLIENT_G2,
- MC_CLIENT_EPP,
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
- MC_CLIENT_HC,
-#endif
- MC_CLIENT_LAST
- },
- {
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
- {"2d", CLK_AND_RST},
- {"epp", CLK_AND_RST},
- {"host1x", CLK_AND_RST},
-#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
- {"2d.cbus", CLK_AND_RST},
- {"epp.cbus", CLK_AND_RST},
- {"host1x.cbus", CLK_AND_RST},
-#else
- {"2d.cbus", CLK_AND_RST},
- {"epp.cbus", CLK_AND_RST},
-#endif
- },
- },
-#endif
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- [TEGRA_POWERGATE_CRAIL] = { "crail", {MC_CLIENT_LAST}, },
- [TEGRA_POWERGATE_C0NC] = { "c0nc", {MC_CLIENT_LAST}, },
- [TEGRA_POWERGATE_C1NC] = { "c1nc", {MC_CLIENT_LAST}, },
- [TEGRA_POWERGATE_DISA] = { "disa",
- {
- MC_CLIENT_DC,
- MC_CLIENT_LAST
- },
- {
- {"disp1", CLK_AND_RST},
- {"dsia", CLK_AND_RST},
- {"dsib", CLK_AND_RST},
- {"csi", CLK_AND_RST},
- {"mipi-cal", CLK_AND_RST}
- },
- },
- [TEGRA_POWERGATE_DISB] = { "disb",
- {
- MC_CLIENT_DCB,
- MC_CLIENT_LAST
- },
- {
- {"disp2", CLK_AND_RST},
- {"hdmi", CLK_AND_RST}
- },
- },
- [TEGRA_POWERGATE_XUSBA] = { "xusba",
- { MC_CLIENT_LAST },
- {
- {"xusb_ss", CLK_AND_RST}
- },
- },
- [TEGRA_POWERGATE_XUSBB] = { "xusbb",
- {
- MC_CLIENT_XUSB_DEV,
- MC_CLIENT_LAST
- },
- {
- {"xusb_dev", CLK_AND_RST},
- },
- },
- [TEGRA_POWERGATE_XUSBC] = { "xusbc",
- {
- MC_CLIENT_XUSB_HOST,
- MC_CLIENT_LAST
- },
- {
- {"xusb_host", CLK_AND_RST},
- },
- },
-#endif
-
-};
-
-static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
-
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-static void __iomem *mipi_cal = IO_ADDRESS(TEGRA_MIPI_CAL_BASE);
-
-static u32 mipi_cal_read(unsigned long reg)
-{
- return readl(mipi_cal + reg);
-}
-
-static void mipi_cal_write(u32 val, unsigned long reg)
-{
- writel_relaxed(val, mipi_cal + reg);
-}
-
-static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
-
-static u32 clk_rst_read(unsigned long reg)
-{
- return readl(clk_rst + reg);
-}
-#endif
-
-static u32 pmc_read(unsigned long reg)
-{
- return readl(pmc + reg);
-}
-
-static void pmc_write(u32 val, unsigned long reg)
-{
- writel_relaxed(val, pmc + reg);
-}
-
-static void __iomem *mc = IO_ADDRESS(TEGRA_MC_BASE);
-
-static u32 mc_read(unsigned long reg)
-{
- return readl(mc + reg);
-}
-
-static void mc_write(u32 val, unsigned long reg)
-{
- writel_relaxed(val, mc + reg);
-}
-
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && \
- !defined(CONFIG_TEGRA_SIMULATION_PLATFORM)
-
-#define MC_CLIENT_HOTRESET_CTRL 0x200
-#define MC_CLIENT_HOTRESET_STAT 0x204
-
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && \
- !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-/* FIXME: this is sw workaround for unstable hotreset status
- * for T11x.
- */
-#define HOTRESET_READ_COUNT 5
-static bool tegra11x_stable_hotreset_check(u32 *stat)
-{
- int i;
- u32 cur_stat;
- u32 prv_stat;
- unsigned long flags;
-
- spin_lock_irqsave(&tegra_powergate_lock, flags);
- prv_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
- for (i = 0; i < HOTRESET_READ_COUNT; i++) {
- cur_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
- if (cur_stat != prv_stat) {
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
- return false;
- }
- }
- *stat = cur_stat;
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
- return true;
-}
-#endif
-
-static void mc_flush(int id)
-{
- u32 idx, rst_ctrl, rst_stat;
- enum mc_client mcClientBit;
- unsigned long flags;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && \
- !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool ret;
-#endif
-
- BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
-
- 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);
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
- rst_stat = mc_read(MC_CLIENT_HOTRESET_STAT);
-#else
- rst_stat = 0;
- ret = tegra11x_stable_hotreset_check(&rst_stat);
- if (!ret)
- continue;
-#endif
- } while (!(rst_stat & (1 << mcClientBit)));
- }
-}
-
-static void mc_flush_done(int id)
-{
- u32 idx, rst_ctrl;
- enum mc_client mcClientBit;
- unsigned long flags;
-
- BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
-
- 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();
-}
-
-int tegra_powergate_mc_flush(int id)
-{
- if (id < 0 || id >= TEGRA_NUM_POWERGATE)
- return -EINVAL;
- mc_flush(id);
- return 0;
-}
-
-int tegra_powergate_mc_flush_done(int id)
-{
- if (id < 0 || id >= TEGRA_NUM_POWERGATE)
- return -EINVAL;
- mc_flush_done(id);
- return 0;
-}
-
-int tegra_powergate_mc_disable(int id)
-{
- return 0;
-}
-
-int tegra_powergate_mc_enable(int id)
-{
- return 0;
-}
-
-#else
-
-#define MC_CLIENT_CTRL 0x100
-#define MC_CLIENT_HOTRESETN 0x104
-#define MC_CLIENT_ORRC_BASE 0x140
-
-int tegra_powergate_mc_disable(int id)
-{
- u32 idx, clt_ctrl, orrc_reg;
- enum mc_client mcClientBit;
- unsigned long flags;
-
- if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
- WARN_ON(1);
- return -EINVAL;
- }
-
- 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);
-
- /* clear client enable bit */
- clt_ctrl = mc_read(MC_CLIENT_CTRL);
- clt_ctrl &= ~(1 << mcClientBit);
- mc_write(clt_ctrl, MC_CLIENT_CTRL);
-
- /* read back to flush write */
- clt_ctrl = mc_read(MC_CLIENT_CTRL);
-
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
-
- /* wait for outstanding requests to reach 0 */
- orrc_reg = MC_CLIENT_ORRC_BASE + (mcClientBit * 4);
- while (mc_read(orrc_reg) != 0)
- udelay(10);
- }
- return 0;
-}
-
-int tegra_powergate_mc_flush(int id)
-{
- u32 idx, hot_rstn;
- enum mc_client mcClientBit;
- unsigned long flags;
-
- if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
- WARN_ON(1);
- return -EINVAL;
- }
-
- 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);
-
- /* assert hotreset (client module is currently in reset) */
- hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
- hot_rstn &= ~(1 << mcClientBit);
- mc_write(hot_rstn, MC_CLIENT_HOTRESETN);
-
- /* read back to flush write */
- hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
-
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
- }
- return 0;
-}
-
-int tegra_powergate_mc_flush_done(int id)
-{
- u32 idx, hot_rstn;
- enum mc_client mcClientBit;
- unsigned long flags;
-
- if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
- WARN_ON(1);
- return -EINVAL;
- }
-
- 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);
-
- /* deassert hotreset */
- hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
- hot_rstn |= (1 << mcClientBit);
- mc_write(hot_rstn, MC_CLIENT_HOTRESETN);
-
- /* read back to flush write */
- hot_rstn = mc_read(MC_CLIENT_HOTRESETN);
+static struct powergate_ops *pg_ops;
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
- }
- return 0;
-}
-
-int tegra_powergate_mc_enable(int id)
+static spinlock_t *tegra_get_powergate_lock(void)
{
- u32 idx, clt_ctrl;
- enum mc_client mcClientBit;
- unsigned long flags;
-
- if (id < 0 || id >= TEGRA_NUM_POWERGATE) {
- WARN_ON(1);
- return -EINVAL;
- }
-
- 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);
-
- /* enable client */
- clt_ctrl = mc_read(MC_CLIENT_CTRL);
- clt_ctrl |= (1 << mcClientBit);
- mc_write(clt_ctrl, MC_CLIENT_CTRL);
-
- /* read back to flush write */
- clt_ctrl = mc_read(MC_CLIENT_CTRL);
+ if (pg_ops && pg_ops->get_powergate_lock)
+ return pg_ops->get_powergate_lock();
+ else
+ WARN_ON_ONCE("This SOC does not export powergate lock");
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
- }
- return 0;
+ return NULL;
}
-static void mc_flush(int id) {}
-static void mc_flush_done(int id) {}
-#endif
-
-static int tegra_powergate_set(int id, bool new_state)
+int tegra_powergate_set(int id, bool new_state)
{
#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
bool status;
unsigned long flags;
+ spinlock_t *lock = tegra_get_powergate_lock();
+
/* 10us timeout for toggle operation if it takes affect*/
int toggle_timeout = 10;
+
/* 100 * 10 = 1000us timeout for toggle command to take affect in case
of contention with h/w initiated CPU power gating */
int contention_timeout = 100;
- spin_lock_irqsave(&tegra_powergate_lock, flags);
+ spin_lock_irqsave(lock, flags);
status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
if (status == new_state) {
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ spin_unlock_irqrestore(lock, flags);
return 0;
}
@@ -659,12 +79,12 @@ static int tegra_powergate_set(int id, bool new_state)
/* CPU ungated in s/w only during boot/resume with outer
waiting loop and no contention from other CPUs */
pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ spin_unlock_irqrestore(lock, flags);
return 0;
}
+ pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
do {
- pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
do {
udelay(1);
status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
@@ -675,202 +95,82 @@ static int tegra_powergate_set(int id, bool new_state)
contention_timeout--;
} while ((status != new_state) && (contention_timeout > 0));
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+ spin_unlock_irqrestore(lock, flags);
if (status != new_state) {
WARN(1, "Could not set powergate %d to %d", id, new_state);
return -EBUSY;
}
- trace_power_domain_target(powergate_partition_info[id].name, new_state,
+ trace_power_domain_target(tegra_powergate_get_name(id), new_state,
smp_processor_id());
#endif
return 0;
}
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-static bool tegra11x_check_plld_plld2_disable(void)
-{
- /* FIXME:
- * add check for plld and plld2 disable
- */
-#define CLK_RST_CONTROLLER_PLLD_BASE_0 0xd0
-#define CLK_RST_CONTROLLER_PLLD_BASE_0_PLLD_ENABLE_LSB 30
-#define CLK_RST_CONTROLLER_PLLD2_BASE_0 0x4b8
-#define CLK_RST_CONTROLLER_PLLD2_BASE_0_PLLD2_ENABLE_LSB 30
- u32 status;
- status = clk_rst_read(CLK_RST_CONTROLLER_PLLD_BASE_0);
- if (status & (1 << CLK_RST_CONTROLLER_PLLD_BASE_0_PLLD_ENABLE_LSB))
- return false;
- status = clk_rst_read(CLK_RST_CONTROLLER_PLLD2_BASE_0);
- if (status & (1 << CLK_RST_CONTROLLER_PLLD2_BASE_0_PLLD2_ENABLE_LSB))
- return false;
- return true;
-}
-
-static bool tegra11x_pg_sw_war_missing(int id)
+int is_partition_clk_disabled(struct powergate_partition_info *pg_info)
{
- bool ret;
-
- switch (id) {
- case TEGRA_POWERGATE_DISA:
- /* FIXME:
- * [SW WAR bug 954988]:
- * Disable PLLD and PLLD2 by clearing bits:
-a. CLK_RST_CONTROLLER_PLLD_BASE_0_PLLD_ENABLE
-b. CLK_RST_CONTROLLER_PLLD2_BASE_0_PLLD2_ENABLE
- * We should not need to disable PLLD and PLLD2
- * for linux/android implementation
- * adding check in case PLLD or PLLD2 is/are ON
- */
- ret = tegra11x_check_plld_plld2_disable();
- if (!ret)
- return true;
-
- break;
- }
- return false;
-}
-#endif
+ u32 idx;
+ struct clk *clk;
+ struct partition_clk_info *clk_info;
+ int ret = 0;
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
-static int unpowergate_module(int id)
-{
- if (id < 0 || id >= tegra_num_powerdomains)
- return -EINVAL;
- return tegra_powergate_set(id, true);
-}
-#endif
+ for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
+ clk_info = &pg_info->clk_info[idx];
+ clk = clk_info->clk_ptr;
-static int powergate_module(int id)
-{
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool need_sw_war;
-#endif
- if (id < 0 || id >= tegra_num_powerdomains)
- return -EINVAL;
+ if (!clk)
+ break;
- mc_flush(id);
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- need_sw_war = tegra11x_pg_sw_war_missing(id);
- if (need_sw_war) {
- pr_err("Error: missing powergate sw war in file: %s, func: %s, line=%d\n",
- __FILE__, __func__, __LINE__);
- return -1;
+ if (clk_info->clk_type != RST_ONLY) {
+ if (tegra_is_clk_enabled(clk)) {
+ ret = -1;
+ break;
+ }
+ }
}
-#endif
- return tegra_powergate_set(id, false);
-}
-
-bool tegra_powergate_is_powered(int id)
-{
- u32 status;
- if (id < 0 || id >= tegra_num_powerdomains)
- return -EINVAL;
-
- status = pmc_read(PWRGATE_STATUS) & (1 << id);
- return !!status;
+ return ret;
}
-EXPORT_SYMBOL(tegra_powergate_is_powered);
-int tegra_powergate_remove_clamping(int id)
+int powergate_module(int id)
{
- u32 mask;
- int contention_timeout = 100;
-
- if (id < 0 || id >= tegra_num_powerdomains)
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
return -EINVAL;
+ }
- /*
- * PCIE and VDE clamping masks are swapped with respect to their
- * partition ids
- */
- if (id == TEGRA_POWERGATE_VDEC)
- mask = (1 << TEGRA_POWERGATE_PCIE);
- else if (id == TEGRA_POWERGATE_PCIE)
- mask = (1 << TEGRA_POWERGATE_VDEC);
- else
- mask = (1 << id);
-
- pmc_write(mask, REMOVE_CLAMPING);
- /* Wait until clamp is removed */
- do {
- udelay(1);
- contention_timeout--;
- } while ((contention_timeout > 0)
- && (pmc_read(REMOVE_CLAMPING) & mask));
+ if (id < 0 || id >= pg_ops->num_powerdomains)
+ return -EINVAL;
- WARN(contention_timeout <= 0, "Couldn't remove clamping");
+ tegra_powergate_mc_flush(id);
- return 0;
+ return tegra_powergate_set(id, false);
}
-static void get_clk_info(int id)
+int unpowergate_module(int id)
{
- int idx;
-
- for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
- if (!powergate_partition_info[id].clk_info[idx].clk_name)
- break;
- powergate_partition_info[id].
- clk_info[idx].clk_ptr =
- tegra_get_clock_by_name(
- powergate_partition_info[id].clk_info[idx].clk_name);
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
}
-}
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-static bool tegra11x_pug_clk_n_rst_skip(int id, u32 idx)
-{
- switch (id) {
- case TEGRA_POWERGATE_VENC:
- if ((powergate_partition_info[id].clk_info[idx].clk_name) &&
- (!(strncmp("csi",
- powergate_partition_info[id].clk_info[idx].clk_name,
- 3)))) {
- /* DIS powered ON then do clk enable CSI */
- if (!tegra_powergate_is_powered(
- TEGRA_POWERGATE_DISA))
- return true;
- }
- break;
- case TEGRA_POWERGATE_DISA:
- if ((powergate_partition_info[id].clk_info[idx].clk_name) &&
- (!(strncmp("csi",
- powergate_partition_info[id].clk_info[idx].clk_name,
- 3)))) {
- /* DIS powered ON then do clk enable CSI */
- if (!tegra_powergate_is_powered(
- TEGRA_POWERGATE_VENC))
- return true;
- }
- break;
- }
- return false;
+ if (id < 0 || id >= pg_ops->num_powerdomains)
+ return -EINVAL;
+
+ return tegra_powergate_set(id, true);
}
-#endif
-static int partition_clk_enable(int id)
+int partition_clk_enable(struct powergate_partition_info *pg_info)
{
int ret;
u32 idx;
struct clk *clk;
struct partition_clk_info *clk_info;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool skip_enable;
-#endif
-
- BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- skip_enable = tegra11x_pug_clk_n_rst_skip(id, idx);
- if (skip_enable)
- continue;
-#endif
- clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk_info = &pg_info->clk_info[idx];
clk = clk_info->clk_ptr;
if (!clk)
break;
@@ -887,7 +187,7 @@ static int partition_clk_enable(int id)
err_clk_en:
WARN(1, "Could not enable clk %s, error %d", clk->name, ret);
while (idx--) {
- clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk_info = &pg_info->clk_info[idx];
if (clk_info->clk_type != RST_ONLY)
tegra_clk_disable_unprepare(clk_info->clk_ptr);
}
@@ -895,773 +195,389 @@ err_clk_en:
return ret;
}
-static int is_partition_clk_disabled(int id)
+void partition_clk_disable(struct powergate_partition_info *pg_info)
{
u32 idx;
struct clk *clk;
struct partition_clk_info *clk_info;
- int ret = 0;
-
- BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
- clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk_info = &pg_info->clk_info[idx];
clk = clk_info->clk_ptr;
+
if (!clk)
break;
- if (clk_info->clk_type != RST_ONLY) {
- if (tegra_is_clk_enabled(clk)) {
- ret = -1;
- break;
- }
- }
+ if (clk_info->clk_type != RST_ONLY)
+ tegra_clk_disable_unprepare(clk);
}
-
- return ret;
}
-static void partition_clk_disable(int id)
+void get_clk_info(struct powergate_partition_info *pg_info)
{
- u32 idx;
- struct clk *clk;
- struct partition_clk_info *clk_info;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool skip_disable;
-#endif
-
- BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
+ int idx;
for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- if (id == TEGRA_POWERGATE_DISA) {
- skip_disable = tegra11x_pug_clk_n_rst_skip(id, idx);
- if (skip_disable)
- continue;
- }
-#endif
- clk_info = &powergate_partition_info[id].clk_info[idx];
- clk = clk_info->clk_ptr;
- if (!clk)
+ if (!pg_info->clk_info[idx].clk_name)
break;
- if (clk_info->clk_type != RST_ONLY)
- tegra_clk_disable_unprepare(clk);
+ pg_info->clk_info[idx].clk_ptr = tegra_get_clock_by_name(
+ pg_info->clk_info[idx].clk_name);
}
}
-static void powergate_partition_assert_reset(int id)
+void powergate_partition_assert_reset(struct powergate_partition_info *pg_info)
{
u32 idx;
struct clk *clk_ptr;
struct partition_clk_info *clk_info;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool skip_reset;
-#endif
-
- BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- if (id == TEGRA_POWERGATE_DISA) {
- skip_reset = tegra11x_pug_clk_n_rst_skip(id, idx);
- if (skip_reset)
- continue;
- }
-#endif
- clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk_info = &pg_info->clk_info[idx];
clk_ptr = clk_info->clk_ptr;
+
if (!clk_ptr)
break;
+
if (clk_info->clk_type != CLK_ONLY)
tegra_periph_reset_assert(clk_ptr);
}
}
-static void powergate_partition_deassert_reset(int id)
+void powergate_partition_deassert_reset(struct powergate_partition_info *pg_info)
{
u32 idx;
struct clk *clk_ptr;
struct partition_clk_info *clk_info;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool skip_reset;
-#endif
-
- BUG_ON(id < 0 || id >= TEGRA_NUM_POWERGATE);
for (idx = 0; idx < MAX_CLK_EN_NUM; idx++) {
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- skip_reset = tegra11x_pug_clk_n_rst_skip(id, idx);
- if (skip_reset)
- continue;
-#endif
- clk_info = &powergate_partition_info[id].clk_info[idx];
+ clk_info = &pg_info->clk_info[idx];
clk_ptr = clk_info->clk_ptr;
+
if (!clk_ptr)
break;
+
if (clk_info->clk_type != CLK_ONLY)
tegra_periph_reset_deassert(clk_ptr);
}
}
-/* Must be called with clk disabled, and returns with clk disabled */
-static int tegra_powergate_reset_module(int id)
+int tegra_powergate_reset_module(struct powergate_partition_info *pg_info)
{
int ret;
- powergate_partition_assert_reset(id);
+ powergate_partition_assert_reset(pg_info);
udelay(10);
- ret = partition_clk_enable(id);
+ ret = partition_clk_enable(pg_info);
if (ret)
return ret;
udelay(10);
- powergate_partition_deassert_reset(id);
+ powergate_partition_deassert_reset(pg_info);
- partition_clk_disable(id);
+ partition_clk_disable(pg_info);
return 0;
}
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-/*
- * FIXME: sw war for mipi-cal calibration when unpowergating DISA partition
- */
-static void tegra11x_mipical_calibrate(int id)
+int tegra_powergate_remove_clamping(int id)
{
- struct reg_offset_val {
- u32 offset;
- u32 por_value;
- };
- u32 status;
- unsigned long flags;
-#define MIPI_CAL_MIPI_CAL_CTRL_0 0x0
-#define MIPI_CAL_CIL_MIPI_CAL_STATUS_0 0x8
-#define MIPI_CAL_CILA_MIPI_CAL_CONFIG_0 0x14
-#define MIPI_CAL_CILB_MIPI_CAL_CONFIG_0 0x18
-#define MIPI_CAL_CILC_MIPI_CAL_CONFIG_0 0x1c
-#define MIPI_CAL_CILD_MIPI_CAL_CONFIG_0 0x20
-#define MIPI_CAL_CILE_MIPI_CAL_CONFIG_0 0x24
-#define MIPI_CAL_DSIA_MIPI_CAL_CONFIG_0 0x38
-#define MIPI_CAL_DSIB_MIPI_CAL_CONFIG_0 0x3c
-#define MIPI_CAL_DSIC_MIPI_CAL_CONFIG_0 0x40
-#define MIPI_CAL_DSID_MIPI_CAL_CONFIG_0 0x44
- static struct reg_offset_val mipi_cal_por_values[] = {
- { MIPI_CAL_MIPI_CAL_CTRL_0, 0x2a000000 },
- { MIPI_CAL_CILA_MIPI_CAL_CONFIG_0, 0x00200000 },
- { MIPI_CAL_CILB_MIPI_CAL_CONFIG_0, 0x00200000 },
- { MIPI_CAL_CILC_MIPI_CAL_CONFIG_0, 0x00200000 },
- { MIPI_CAL_CILD_MIPI_CAL_CONFIG_0, 0x00200000 },
- { MIPI_CAL_CILE_MIPI_CAL_CONFIG_0, 0x00000000 },
- { MIPI_CAL_DSIA_MIPI_CAL_CONFIG_0, 0x00200000 },
- { MIPI_CAL_DSIB_MIPI_CAL_CONFIG_0, 0x00200000 },
- { MIPI_CAL_DSIC_MIPI_CAL_CONFIG_0, 0x00200000 },
- { MIPI_CAL_DSID_MIPI_CAL_CONFIG_0, 0x00200000 },
- };
- int i;
+ u32 mask;
+ int contention_timeout = 100;
- if (id != TEGRA_POWERGATE_DISA)
- return;
- spin_lock_irqsave(&tegra_powergate_lock, flags);
- /* mipi cal por restore */
- for (i = 0; i < ARRAY_SIZE(mipi_cal_por_values); i++) {
- mipi_cal_write(mipi_cal_por_values[i].por_value,
- mipi_cal_por_values[i].offset);
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
}
- /* mipi cal status clear */
- status = mipi_cal_read(MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
- mipi_cal_write(status, MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
- /* mipi cal status read - to flush writes */
- status = mipi_cal_read(MIPI_CAL_CIL_MIPI_CAL_STATUS_0);
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
-}
-#endif
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-static bool skip_pg_check(int id, bool is_unpowergate)
-{
- /*
- * FIXME: need to stress test partition power gating before
- * enabling power gating for T11x
- * List of T11x partition id which skip power gating
- */
- static int skip_pg_t11x_list[] = {
- /*
- * CPU and 3D partitions enable/disable
- * is managed by respective modules
- */
- };
- int i;
+ if (id < 0 || id >= pg_ops->num_powerdomains)
+ return -EINVAL;
/*
- * skip unnecessary multiple calls e.g. powergate call when
- * partition is already powered-off or vice-versa
+ * PCIE and VDE clamping masks are swapped with respect to their
+ * partition ids
*/
- if ((tegra_powergate_is_powered(id) &&
- is_unpowergate) ||
- (!(tegra_powergate_is_powered(id)) &&
- (!is_unpowergate))) {
- pr_err("Partition %s already powered-%s and %spowergate skipped\n",
- tegra_powergate_get_name(id),
- (tegra_powergate_is_powered(id)) ?
- "on" : "off",
- (is_unpowergate) ? "un" : "");
- return true;
- }
- /* unpowergate is allowed for all partitions */
- if (!tegra_powergate_is_powered(id) &&
- is_unpowergate)
- return false;
- for (i = 0; i < ARRAY_SIZE(skip_pg_t11x_list); i++) {
- if (id == skip_pg_t11x_list[i]) {
- pr_err("Partition %s %spowergate skipped\n",
- tegra_powergate_get_name(id),
- (is_unpowergate) ? "un" : "");
- return true;
- }
- }
-
- return false;
-}
-#endif
-
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-static atomic_t ref_count_a = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISA */
-static atomic_t ref_count_b = ATOMIC_INIT(1); /* for TEGRA_POWERGATE_DISB */
-#endif
-
-/*
- * Must be called with clk disabled, and returns with clk disabled
- * Drivers should enable clks for partition. Unpowergates only the
- * partition.
- */
-int tegra_unpowergate_partition(int id)
-{
- int ret;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool is_pg_skip;
-
- WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
- WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
- if (id == TEGRA_POWERGATE_DISA && atomic_inc_return(&ref_count_a) != 1)
- return 0;
- else if (id == TEGRA_POWERGATE_DISB &&
- atomic_inc_return(&ref_count_b) != 1)
- return 0;
-
- is_pg_skip = skip_pg_check(id, true);
- if (is_pg_skip)
- return 0;
- ret = tegra11x_check_partition_pug_seq(id);
- if (ret)
- return ret;
-
- ret = tegra11x_unpowergate(id);
- return ret;
-#else
- /* FIXME: not changing previous chip's power-ungate implementation */
-
- /* If first clk_ptr is null, fill clk info for the partition */
- if (!powergate_partition_info[id].clk_info[0].clk_ptr)
- get_clk_info(id);
-
- if (tegra_powergate_is_powered(id))
- return tegra_powergate_reset_module(id);
-
- ret = unpowergate_module(id);
- if (ret)
- goto err_power;
-
- powergate_partition_assert_reset(id);
+ if (id == TEGRA_POWERGATE_VDEC)
+ mask = (1 << TEGRA_POWERGATE_PCIE);
+ else if (id == TEGRA_POWERGATE_PCIE)
+ mask = (1 << TEGRA_POWERGATE_VDEC);
+ else
+ mask = (1 << id);
- /* Un-Powergating fails if all clks are not enabled */
- ret = partition_clk_enable(id);
- if (ret)
- goto err_clk_on;
+ pmc_write(mask, REMOVE_CLAMPING);
+ /* Wait until clamp is removed */
+ do {
+ udelay(1);
+ contention_timeout--;
+ } while ((contention_timeout > 0)
+ && (pmc_read(REMOVE_CLAMPING) & mask));
- udelay(10);
+ WARN(contention_timeout <= 0, "Couldn't remove clamping");
- ret = tegra_powergate_remove_clamping(id);
- if (ret)
- goto err_clamp;
+ return 0;
+}
- udelay(10);
+/* EXTERNALY VISIBLE APIS */
- powergate_partition_deassert_reset(id);
+bool tegra_powergate_is_powered(int id)
+{
+ u32 status;
- mc_flush_done(id);
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
- /* Disable all clks enabled earlier. Drivers should enable clks */
- partition_clk_disable(id);
+ if (id < 0 || id >= pg_ops->num_powerdomains)
+ return -EINVAL;
- return 0;
+ status = pmc_read(PWRGATE_STATUS) & (1 << id);
-err_clamp:
- partition_clk_disable(id);
-err_clk_on:
- powergate_module(id);
-err_power:
- WARN(1, "Could not Un-Powergate %d", id);
- return ret;
-#endif
+ return !!status;
}
-EXPORT_SYMBOL(tegra_unpowergate_partition);
+EXPORT_SYMBOL(tegra_powergate_is_powered);
int tegra_cpu_powergate_id(int cpuid)
{
- if (cpuid > 0 && cpuid < tegra_num_cpu_domains)
- return tegra_cpu_domains[cpuid];
-
- return -EINVAL;
-}
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
-int __init tegra_powergate_init(void)
-{
- switch (tegra_chip_id) {
- case TEGRA20:
- tegra_num_powerdomains = 7;
- break;
- case TEGRA30:
- tegra_num_powerdomains = 14;
- tegra_num_cpu_domains = 4;
- tegra_cpu_domains = tegra_quad_cpu_domains;
- break;
- case TEGRA11X:
- tegra_num_powerdomains = 23;
- tegra_num_cpu_domains = 4;
- tegra_cpu_domains = tegra_quad_cpu_domains;
- break;
- case TEGRA14X:
- tegra_num_powerdomains = 25;
- tegra_num_cpu_domains = 4;
- tegra_cpu_domains = tegra_quad_cpu_domains;
- break;
- default:
- /* Unknown Tegra variant. Disable powergating */
- tegra_num_powerdomains = 0;
- break;
+ if (cpuid < 0 || cpuid >= pg_ops->num_cpu_domains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
}
-#if defined(DEBUG_T11x_POWERGATE)
- test_powergate_parts();
-#endif
-#if defined(DEBUG_T11x_POWERUNGATE)
- test_unpowergate_parts();
-#endif
-#if defined(DEBUG_T11x_POWERGATE_CLK_OFF)
- test_powergate_clk_off_parts();
-#endif
-#if defined(DEBUG_T11x_POWERUNGATE_CLK_ON)
- test_unpowergate_clk_on_parts();
-#endif
- return 0;
+ if (pg_ops->cpu_domains)
+ return pg_ops->cpu_domains[cpuid];
+ else
+ WARN_ON_ONCE("This SOC does not support CPU powergate\n");
+
+ return -EINVAL;
}
+EXPORT_SYMBOL(tegra_cpu_powergate_id);
-/*
- * Must be called with clk disabled, and returns with clk enabled
- * Unpowergates the partition and enables all required clks.
- */
-int tegra_unpowergate_partition_with_clk_on(int id)
+int tegra_powergate_partition(int id)
{
- int ret = 0;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool is_pg_skip;
-
- is_pg_skip = skip_pg_check(id, true);
- if (is_pg_skip)
- return 0;
-#endif
-#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
- /* Restrict this functions use to few partitions */
- BUG_ON(id != TEGRA_POWERGATE_SATA && id != TEGRA_POWERGATE_PCIE);
-#elif defined(CONFIG_ARCH_TEGRA_2x_SOC)
- /* Restrict this functions use to few partitions */
- BUG_ON(id != TEGRA_POWERGATE_PCIE);
-#endif
-
- ret = tegra_unpowergate_partition(id);
- if (ret)
- goto err_unpowergating;
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
- /* Enable clks for the partition */
- ret = partition_clk_enable(id);
- if (ret)
- goto err_unpowergate_clk;
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
+ }
- return ret;
+ if (pg_ops->powergate_partition)
+ return pg_ops->powergate_partition(id);
+ else
+ WARN_ON_ONCE("This SOC doesn't support powergating");
-err_unpowergate_clk:
- tegra_powergate_partition(id);
- WARN(1, "Could not Un-Powergate %d, err in enabling clk", id);
-err_unpowergating:
- WARN(1, "Could not Un-Powergate %d", id);
- return ret;
+ return -EINVAL;
}
+EXPORT_SYMBOL(tegra_powergate_partition);
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
-static int tegra11x_powergate_set(int id, bool new_state)
+int tegra_unpowergate_partition(int id)
{
-#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
- bool status;
- unsigned long flags;
- /* 10us timeout for toggle operation if it takes affect*/
- int toggle_timeout = 10;
- /* 100 * 10 = 1000us timeout for toggle command to take affect in case
- of contention with h/w initiated CPU power gating */
- int contention_timeout = 100;
-
- spin_lock_irqsave(&tegra_powergate_lock, flags);
-
- status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
-
- if (status == new_state) {
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
- return 0;
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
}
- pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
- do {
- do {
- udelay(1);
- status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
-
- toggle_timeout--;
- } while ((status != new_state) && (toggle_timeout > 0));
-
- contention_timeout--;
- } while ((status != new_state) && (contention_timeout > 0));
-
- spin_unlock_irqrestore(&tegra_powergate_lock, flags);
-
- if (status != new_state) {
- WARN(1, "Could not set powergate %d to %d", id, new_state);
- return -EBUSY;
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
}
- trace_power_domain_target(powergate_partition_info[id].name, new_state,
- smp_processor_id());
-#endif
+ if (pg_ops->unpowergate_partition)
+ return pg_ops->unpowergate_partition(id);
+ else
+ WARN_ON_ONCE("This SOC doesn't support un-powergating");
- return 0;
+ return -EINVAL;
}
+EXPORT_SYMBOL(tegra_unpowergate_partition);
-static int tegra11x_powergate(int id)
+int tegra_powergate_partition_with_clk_off(int id)
{
- int ret;
-
- /* If first clk_ptr is null, fill clk info for the partition */
- if (powergate_partition_info[id].clk_info[0].clk_ptr)
- get_clk_info(id);
-
- ret = partition_clk_enable(id);
- if (ret)
- WARN(1, "Couldn't enable clock");
-
- udelay(10);
-
- mc_flush(id);
-
- udelay(10);
-
- powergate_partition_assert_reset(id);
-
- udelay(10);
-
- /* Powergating is done only if refcnt of all clks is 0 */
- partition_clk_disable(id);
-
- udelay(10);
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
- ret = tegra11x_powergate_set(id, false);
- if (ret)
- goto err_power_off;
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
+ }
- return 0;
+ if (pg_ops->powergate_partition_with_clk_off)
+ return pg_ops->powergate_partition_with_clk_off(id);
+ else
+ WARN_ON_ONCE("This SOC doesn't support powergating with clk off");
-err_power_off:
- WARN(1, "Could not Powergate Partition %d", id);
- return ret;
+ return -EINVAL;
}
+EXPORT_SYMBOL(tegra_powergate_partition_with_clk_off);
-static int tegra11x_unpowergate(int id)
+int tegra_unpowergate_partition_with_clk_on(int id)
{
- int ret;
- /* If first clk_ptr is null, fill clk info for the partition */
- if (!powergate_partition_info[id].clk_info[0].clk_ptr)
- get_clk_info(id);
-
- if (tegra_powergate_is_powered(id))
- return tegra_powergate_reset_module(id);
-
- ret = tegra11x_powergate_set(id, true);
- if (ret)
- goto err_power;
-
- udelay(10);
-
- /* Un-Powergating fails if all clks are not enabled */
- ret = partition_clk_enable(id);
- if (ret)
- goto err_clk_on;
-
- udelay(10);
-
- ret = tegra_powergate_remove_clamping(id);
- if (ret)
- goto err_clamp;
-
- udelay(10);
-
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- tegra11x_mipical_calibrate(id);
-#endif
- powergate_partition_deassert_reset(id);
-
- udelay(10);
-
- mc_flush_done(id);
-
- udelay(10);
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
- /* Disable all clks enabled earlier. Drivers should enable clks */
- partition_clk_disable(id);
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
+ }
- return 0;
+ if (pg_ops->unpowergate_partition_with_clk_on)
+ return pg_ops->unpowergate_partition_with_clk_on(id);
+ else
+ WARN_ON_ONCE("This SOC doesn't support power un-gating with clk on");
-err_clamp:
- partition_clk_disable(id);
-err_clk_on:
- powergate_module(id);
-err_power:
- WARN(1, "Could not Un-Powergate %d", id);
- return ret;
+ return -EINVAL;
}
+EXPORT_SYMBOL(tegra_unpowergate_partition_with_clk_on);
-static int tegra11x_powergate_partition(int id)
+int tegra_powergate_mc_enable(int id)
{
- int ret;
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
- if (tegra_powergate_is_powered(id)) {
- ret = is_partition_clk_disabled(id);
- if (ret < 0) {
- /* clock enabled */
- ret = tegra_powergate_partition_with_clk_off(id);
- if (ret < 0)
- return ret;
- } else {
- ret = tegra_powergate_partition(id);
- if (ret < 0)
- return ret;
- }
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
}
- return 0;
-}
-static int tegra11x_unpowergate_partition(int id)
-{
- int ret;
+ if (pg_ops->powergate_mc_enable)
+ return pg_ops->powergate_mc_enable(id);
+ else
+ WARN_ON_ONCE("This SOC does not support powergate mc enable");
- if (!tegra_powergate_is_powered(id)) {
- ret = is_partition_clk_disabled(id);
- if (ret) {
- /* clock disabled */
- ret = tegra_unpowergate_partition_with_clk_on(id);
- if (ret < 0)
- return ret;
- } else {
- ret = tegra_unpowergate_partition(id);
- if (ret < 0)
- return ret;
- }
- }
- return 0;
+ return -EINVAL;
}
+EXPORT_SYMBOL(tegra_powergate_mc_enable);
-/*
- * Tegra11x has powergate dependencies between partitions.
- * This function captures the dependencies.
- */
-static int tegra11x_check_partition_pg_seq(int id)
+int tegra_powergate_mc_disable(int id)
{
- int ret;
-
- switch (id) {
- case TEGRA_POWERGATE_DISA:
- ret = tegra11x_powergate_partition(TEGRA_POWERGATE_VENC);
- if (ret < 0)
- return ret;
- ret = tegra11x_powergate_partition(TEGRA_POWERGATE_DISB);
- if (ret < 0)
- return ret;
- break;
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
}
- return 0;
-}
-/*
- * This function captures power-ungate dependencies between tegra11x partitions
- */
-static int tegra11x_check_partition_pug_seq(int id)
-{
- int ret;
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
+ }
- switch (id) {
- case TEGRA_POWERGATE_DISB:
- case TEGRA_POWERGATE_VENC:
- ret = tegra11x_unpowergate_partition(TEGRA_POWERGATE_DISA);
- if (ret < 0)
- return ret;
+ if (pg_ops->powergate_mc_disable)
+ return pg_ops->powergate_mc_disable(id);
+ else
+ WARN_ON_ONCE("This SOC does not support powergate mc disable");
- break;
- }
- return 0;
+ return -EINVAL;
}
-#endif
+EXPORT_SYMBOL(tegra_powergate_mc_disable);
-/*
- * Must be called with clk disabled. Powergates the partition only
- */
-int tegra_powergate_partition(int id)
+int tegra_powergate_mc_flush(int id)
{
- int ret;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool is_pg_skip;
-
- WARN_ONCE(atomic_read(&ref_count_a) < 0, "ref count A underflow");
- WARN_ONCE(atomic_read(&ref_count_b) < 0, "ref count B underflow");
- if (id == TEGRA_POWERGATE_DISA && atomic_dec_return(&ref_count_a) != 0)
- return 0;
- else if (id == TEGRA_POWERGATE_DISB &&
- atomic_dec_return(&ref_count_b) != 0)
- return 0;
-
- is_pg_skip = skip_pg_check(id, false);
- if (is_pg_skip)
- return 0;
-
- ret = tegra11x_check_partition_pg_seq(id);
- if (ret)
- return ret;
-
- /* All Tegra11x partition powergate */
- ret = tegra11x_powergate(id);
- return ret;
-#else
- /* FIXME: not changing previous chip's powergate implementation */
-
- /* If first clk_ptr is null, fill clk info for the partition */
- if (powergate_partition_info[id].clk_info[0].clk_ptr)
- get_clk_info(id);
-
- powergate_partition_assert_reset(id);
-
- /* Powergating is done only if refcnt of all clks is 0 */
- ret = is_partition_clk_disabled(id);
- if (ret)
- goto err_clk_off;
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
- ret = powergate_module(id);
- if (ret)
- goto err_power_off;
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
+ }
- return 0;
+ if (pg_ops->powergate_mc_flush)
+ return pg_ops->powergate_mc_flush(id);
+ else
+ WARN_ON_ONCE("This SOC does not support powergate mc flush");
-err_power_off:
- WARN(1, "Could not Powergate Partition %d", id);
-err_clk_off:
- WARN(1, "Could not Powergate Partition %d, all clks not disabled", id);
- return ret;
-#endif
+ return -EINVAL;
}
-EXPORT_SYMBOL(tegra_powergate_partition);
+EXPORT_SYMBOL(tegra_powergate_mc_flush);
-int tegra_powergate_partition_with_clk_off(int id)
+int tegra_powergate_mc_flush_done(int id)
{
- int ret = 0;
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
- bool is_pg_skip;
-
- is_pg_skip = skip_pg_check(id, false);
- if (is_pg_skip)
- return 0;
-#endif
-#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
- /* Restrict functions use to selected partitions */
- BUG_ON(id != TEGRA_POWERGATE_PCIE && id != TEGRA_POWERGATE_SATA);
-#elif defined(CONFIG_ARCH_TEGRA_2x_SOC)
- /* Restrict functions use to selected partitions */
- BUG_ON(id != TEGRA_POWERGATE_PCIE);
-#endif
- /* Disable clks for the partition */
- partition_clk_disable(id);
-
- ret = is_partition_clk_disabled(id);
- if (ret)
- goto err_powergate_clk;
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
- ret = tegra_powergate_partition(id);
- if (ret)
- goto err_powergating;
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("%s: invalid powergate id\n", __func__);
+ return -EINVAL;
+ }
- return ret;
+ if (pg_ops->powergate_mc_flush_done)
+ return pg_ops->powergate_mc_flush_done(id);
+ else
+ WARN_ON_ONCE("This SOC does not support powergate mc flush done");
-err_powergate_clk:
- WARN(1, "Could not Powergate Partition %d, all clks not disabled", id);
-err_powergating:
- partition_clk_enable(id);
- WARN(1, "Could not Powergate Partition %d", id);
- return ret;
+ return -EINVAL;
}
+EXPORT_SYMBOL(tegra_powergate_mc_flush_done);
-#if defined(DEBUG_T11x_POWERGATE)
-static void test_powergate_parts(void)
+const char *tegra_powergate_get_name(int id)
{
- int i;
+ if (!pg_ops) {
+ pr_info("This SOC doesn't support powergating\n");
+ return NULL;
+ }
- for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
- tegra_powergate_partition(i);
-}
-#endif
+ if (id < 0 || id >= pg_ops->num_powerdomains) {
+ pr_info("invalid powergate id\n");
+ return "invalid";
+ }
-#if defined(DEBUG_T11x_POWERUNGATE)
-static void test_powerungate_parts(void)
-{
- int i;
+ if (pg_ops->get_powergate_domain_name)
+ return pg_ops->get_powergate_domain_name(id);
+ else
+ WARN_ON_ONCE("This SOC does not support CPU powergate");
- for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
- tegra_unpowergate_partition(i);
+ return "invalid";
}
-#endif
+EXPORT_SYMBOL(tegra_powergate_get_name);
-#if defined(DEBUG_T11x_POWERGATE_CLK_OFF)
-static void test_powergate_clk_off_parts(void)
+int __init tegra_powergate_init(void)
{
- int i;
+ switch (tegra_chip_id) {
+ case TEGRA20:
+ pg_ops = tegra2_powergate_init_chip_support();
+ break;
- for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
- tegra_powergate_partition_with_clk_off(i);
-}
-#endif
+ case TEGRA30:
+ pg_ops = tegra3_powergate_init_chip_support();
+ break;
-#if defined(DEBUG_T11x_POWERUNGATE_CLK_OFF)
-static void test_unpowergate_clk_on_parts(void)
-{
- int i;
+ case TEGRA11X:
+ pg_ops = tegra11x_powergate_init_chip_support();
+ break;
- for (i = 0; i < TEGRA_NUM_POWERGATE; i++)
- tegra_unpowergate_partition_with_clk_on(i);
-}
-#endif
+ case TEGRA14X:
+ pg_ops = tegra14x_powergate_init_chip_support();
+ break;
-const char *tegra_powergate_get_name(int id)
-{
- if (id < 0 || id >= TEGRA_NUM_POWERGATE)
- return "invalid";
+ default:
+ pg_ops = NULL;
+ pr_info("%s: Unknown Tegra variant. Disabling powergate\n", __func__);
+ break;
+ }
+
+ pr_info("%s: DONE\n", __func__);
- return powergate_partition_info[id].name;
+ return (pg_ops ? 0 : -EINVAL);
}
#ifdef CONFIG_DEBUG_FS
@@ -1671,10 +587,15 @@ static int powergate_show(struct seq_file *s, void *data)
int i;
const char *name;
+ if (!pg_ops) {
+ seq_printf(s, "This SOC doesn't support powergating\n");
+ return -EINVAL;
+ }
+
seq_printf(s, " powergate powered\n");
seq_printf(s, "------------------\n");
- for (i = 0; i < tegra_num_powerdomains; i++) {
+ for (i = 0; i < pg_ops->num_powerdomains; i++) {
name = tegra_powergate_get_name(i);
if (name)
seq_printf(s, " %9s %7s\n", name,