|
@@ -31,10 +31,13 @@
|
|
|
#include <linux/iopoll.h>
|
|
|
#include <linux/of.h>
|
|
|
#include <linux/of_address.h>
|
|
|
+#include <linux/of_platform.h>
|
|
|
#include <linux/platform_device.h>
|
|
|
+#include <linux/pm_domain.h>
|
|
|
#include <linux/reboot.h>
|
|
|
#include <linux/reset.h>
|
|
|
#include <linux/seq_file.h>
|
|
|
+#include <linux/slab.h>
|
|
|
#include <linux/spinlock.h>
|
|
|
|
|
|
#include <soc/tegra/common.h>
|
|
@@ -102,6 +105,16 @@
|
|
|
|
|
|
#define GPU_RG_CNTRL 0x2d4
|
|
|
|
|
|
+struct tegra_powergate {
|
|
|
+ struct generic_pm_domain genpd;
|
|
|
+ struct tegra_pmc *pmc;
|
|
|
+ unsigned int id;
|
|
|
+ struct clk **clks;
|
|
|
+ unsigned int num_clks;
|
|
|
+ struct reset_control **resets;
|
|
|
+ unsigned int num_resets;
|
|
|
+};
|
|
|
+
|
|
|
struct tegra_pmc_soc {
|
|
|
unsigned int num_powergates;
|
|
|
const char *const *powergates;
|
|
@@ -132,6 +145,7 @@ struct tegra_pmc_soc {
|
|
|
* @cpu_pwr_good_en: CPU power good signal is enabled
|
|
|
* @lp0_vec_phys: physical base address of the LP0 warm boot code
|
|
|
* @lp0_vec_size: size of the LP0 warm boot code
|
|
|
+ * @powergates_available: Bitmap of available power gates
|
|
|
* @powergates_lock: mutex for power gate register access
|
|
|
*/
|
|
|
struct tegra_pmc {
|
|
@@ -156,6 +170,7 @@ struct tegra_pmc {
|
|
|
bool cpu_pwr_good_en;
|
|
|
u32 lp0_vec_phys;
|
|
|
u32 lp0_vec_size;
|
|
|
+ DECLARE_BITMAP(powergates_available, TEGRA_POWERGATE_MAX);
|
|
|
|
|
|
struct mutex powergates_lock;
|
|
|
};
|
|
@@ -165,6 +180,12 @@ static struct tegra_pmc *pmc = &(struct tegra_pmc) {
|
|
|
.suspend_mode = TEGRA_SUSPEND_NONE,
|
|
|
};
|
|
|
|
|
|
+static inline struct tegra_powergate *
|
|
|
+to_powergate(struct generic_pm_domain *domain)
|
|
|
+{
|
|
|
+ return container_of(domain, struct tegra_powergate, genpd);
|
|
|
+}
|
|
|
+
|
|
|
static u32 tegra_pmc_readl(unsigned long offset)
|
|
|
{
|
|
|
return readl(pmc->base + offset);
|
|
@@ -188,6 +209,31 @@ static inline bool tegra_powergate_is_valid(int id)
|
|
|
return (pmc->soc && pmc->soc->powergates[id]);
|
|
|
}
|
|
|
|
|
|
+static inline bool tegra_powergate_is_available(int id)
|
|
|
+{
|
|
|
+ return test_bit(id, pmc->powergates_available);
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_powergate_lookup(struct tegra_pmc *pmc, const char *name)
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ if (!pmc || !pmc->soc || !name)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ for (i = 0; i < pmc->soc->num_powergates; i++) {
|
|
|
+ if (!tegra_powergate_is_valid(i))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!strcmp(name, pmc->soc->powergates[i]))
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_err(pmc->dev, "powergate %s not found\n", name);
|
|
|
+
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* tegra_powergate_set() - set the state of a partition
|
|
|
* @id: partition ID
|
|
@@ -218,13 +264,219 @@ static int tegra_powergate_set(unsigned int id, bool new_state)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int __tegra_powergate_remove_clamping(unsigned int id)
|
|
|
+{
|
|
|
+ u32 mask;
|
|
|
+
|
|
|
+ mutex_lock(&pmc->powergates_lock);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * On Tegra124 and later, the clamps for the GPU are controlled by a
|
|
|
+ * separate register (with different semantics).
|
|
|
+ */
|
|
|
+ if (id == TEGRA_POWERGATE_3D) {
|
|
|
+ if (pmc->soc->has_gpu_clamps) {
|
|
|
+ tegra_pmc_writel(0, GPU_RG_CNTRL);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Tegra 2 has a bug where PCIE and VDE clamping masks are
|
|
|
+ * swapped relatively to the 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);
|
|
|
+
|
|
|
+ tegra_pmc_writel(mask, REMOVE_CLAMPING);
|
|
|
+
|
|
|
+out:
|
|
|
+ mutex_unlock(&pmc->powergates_lock);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void tegra_powergate_disable_clocks(struct tegra_powergate *pg)
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ for (i = 0; i < pg->num_clks; i++)
|
|
|
+ clk_disable_unprepare(pg->clks[i]);
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_powergate_enable_clocks(struct tegra_powergate *pg)
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ for (i = 0; i < pg->num_clks; i++) {
|
|
|
+ err = clk_prepare_enable(pg->clks[i]);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out:
|
|
|
+ while (i--)
|
|
|
+ clk_disable_unprepare(pg->clks[i]);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_powergate_reset_assert(struct tegra_powergate *pg)
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ for (i = 0; i < pg->num_resets; i++) {
|
|
|
+ err = reset_control_assert(pg->resets[i]);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_powergate_reset_deassert(struct tegra_powergate *pg)
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ for (i = 0; i < pg->num_resets; i++) {
|
|
|
+ err = reset_control_deassert(pg->resets[i]);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_powergate_power_up(struct tegra_powergate *pg,
|
|
|
+ bool disable_clocks)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = tegra_powergate_reset_assert(pg);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ usleep_range(10, 20);
|
|
|
+
|
|
|
+ err = tegra_powergate_set(pg->id, true);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ usleep_range(10, 20);
|
|
|
+
|
|
|
+ err = tegra_powergate_enable_clocks(pg);
|
|
|
+ if (err)
|
|
|
+ goto disable_clks;
|
|
|
+
|
|
|
+ usleep_range(10, 20);
|
|
|
+
|
|
|
+ err = __tegra_powergate_remove_clamping(pg->id);
|
|
|
+ if (err)
|
|
|
+ goto disable_clks;
|
|
|
+
|
|
|
+ usleep_range(10, 20);
|
|
|
+
|
|
|
+ err = tegra_powergate_reset_deassert(pg);
|
|
|
+ if (err)
|
|
|
+ goto powergate_off;
|
|
|
+
|
|
|
+ usleep_range(10, 20);
|
|
|
+
|
|
|
+ if (disable_clocks)
|
|
|
+ tegra_powergate_disable_clocks(pg);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+disable_clks:
|
|
|
+ tegra_powergate_disable_clocks(pg);
|
|
|
+ usleep_range(10, 20);
|
|
|
+powergate_off:
|
|
|
+ tegra_powergate_set(pg->id, false);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_powergate_power_down(struct tegra_powergate *pg)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = tegra_powergate_enable_clocks(pg);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ usleep_range(10, 20);
|
|
|
+
|
|
|
+ err = tegra_powergate_reset_assert(pg);
|
|
|
+ if (err)
|
|
|
+ goto disable_clks;
|
|
|
+
|
|
|
+ usleep_range(10, 20);
|
|
|
+
|
|
|
+ tegra_powergate_disable_clocks(pg);
|
|
|
+
|
|
|
+ usleep_range(10, 20);
|
|
|
+
|
|
|
+ err = tegra_powergate_set(pg->id, false);
|
|
|
+ if (err)
|
|
|
+ goto assert_resets;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+assert_resets:
|
|
|
+ tegra_powergate_enable_clocks(pg);
|
|
|
+ usleep_range(10, 20);
|
|
|
+ tegra_powergate_reset_deassert(pg);
|
|
|
+ usleep_range(10, 20);
|
|
|
+disable_clks:
|
|
|
+ tegra_powergate_disable_clocks(pg);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_genpd_power_on(struct generic_pm_domain *domain)
|
|
|
+{
|
|
|
+ struct tegra_powergate *pg = to_powergate(domain);
|
|
|
+ struct tegra_pmc *pmc = pg->pmc;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = tegra_powergate_power_up(pg, true);
|
|
|
+ if (err)
|
|
|
+ dev_err(pmc->dev, "failed to turn on PM domain %s: %d\n",
|
|
|
+ pg->genpd.name, err);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_genpd_power_off(struct generic_pm_domain *domain)
|
|
|
+{
|
|
|
+ struct tegra_powergate *pg = to_powergate(domain);
|
|
|
+ struct tegra_pmc *pmc = pg->pmc;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = tegra_powergate_power_down(pg);
|
|
|
+ if (err)
|
|
|
+ dev_err(pmc->dev, "failed to turn off PM domain %s: %d\n",
|
|
|
+ pg->genpd.name, err);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* tegra_powergate_power_on() - power on partition
|
|
|
* @id: partition ID
|
|
|
*/
|
|
|
int tegra_powergate_power_on(unsigned int id)
|
|
|
{
|
|
|
- if (!tegra_powergate_is_valid(id))
|
|
|
+ if (!tegra_powergate_is_available(id))
|
|
|
return -EINVAL;
|
|
|
|
|
|
return tegra_powergate_set(id, true);
|
|
@@ -236,7 +488,7 @@ int tegra_powergate_power_on(unsigned int id)
|
|
|
*/
|
|
|
int tegra_powergate_power_off(unsigned int id)
|
|
|
{
|
|
|
- if (!tegra_powergate_is_valid(id))
|
|
|
+ if (!tegra_powergate_is_available(id))
|
|
|
return -EINVAL;
|
|
|
|
|
|
return tegra_powergate_set(id, false);
|
|
@@ -267,41 +519,10 @@ int tegra_powergate_is_powered(unsigned int id)
|
|
|
*/
|
|
|
int tegra_powergate_remove_clamping(unsigned int id)
|
|
|
{
|
|
|
- u32 mask;
|
|
|
-
|
|
|
- if (!tegra_powergate_is_valid(id))
|
|
|
+ if (!tegra_powergate_is_available(id))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- mutex_lock(&pmc->powergates_lock);
|
|
|
-
|
|
|
- /*
|
|
|
- * On Tegra124 and later, the clamps for the GPU are controlled by a
|
|
|
- * separate register (with different semantics).
|
|
|
- */
|
|
|
- if (id == TEGRA_POWERGATE_3D) {
|
|
|
- if (pmc->soc->has_gpu_clamps) {
|
|
|
- tegra_pmc_writel(0, GPU_RG_CNTRL);
|
|
|
- goto out;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Tegra 2 has a bug where PCIE and VDE clamping masks are
|
|
|
- * swapped relatively to the 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);
|
|
|
-
|
|
|
- tegra_pmc_writel(mask, REMOVE_CLAMPING);
|
|
|
-
|
|
|
-out:
|
|
|
- mutex_unlock(&pmc->powergates_lock);
|
|
|
-
|
|
|
- return 0;
|
|
|
+ return __tegra_powergate_remove_clamping(id);
|
|
|
}
|
|
|
EXPORT_SYMBOL(tegra_powergate_remove_clamping);
|
|
|
|
|
@@ -316,35 +537,20 @@ EXPORT_SYMBOL(tegra_powergate_remove_clamping);
|
|
|
int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
|
|
|
struct reset_control *rst)
|
|
|
{
|
|
|
- int ret;
|
|
|
-
|
|
|
- reset_control_assert(rst);
|
|
|
-
|
|
|
- ret = tegra_powergate_power_on(id);
|
|
|
- if (ret)
|
|
|
- goto err_power;
|
|
|
-
|
|
|
- ret = clk_prepare_enable(clk);
|
|
|
- if (ret)
|
|
|
- goto err_clk;
|
|
|
-
|
|
|
- usleep_range(10, 20);
|
|
|
+ struct tegra_powergate pg;
|
|
|
+ int err;
|
|
|
|
|
|
- ret = tegra_powergate_remove_clamping(id);
|
|
|
- if (ret)
|
|
|
- goto err_clamp;
|
|
|
+ pg.id = id;
|
|
|
+ pg.clks = &clk;
|
|
|
+ pg.num_clks = 1;
|
|
|
+ pg.resets = &rst;
|
|
|
+ pg.num_resets = 1;
|
|
|
|
|
|
- usleep_range(10, 20);
|
|
|
- reset_control_deassert(rst);
|
|
|
-
|
|
|
- return 0;
|
|
|
+ err = tegra_powergate_power_up(&pg, false);
|
|
|
+ if (err)
|
|
|
+ pr_err("failed to turn on partition %d: %d\n", id, err);
|
|
|
|
|
|
-err_clamp:
|
|
|
- clk_disable_unprepare(clk);
|
|
|
-err_clk:
|
|
|
- tegra_powergate_power_off(id);
|
|
|
-err_power:
|
|
|
- return ret;
|
|
|
+ return err;
|
|
|
}
|
|
|
EXPORT_SYMBOL(tegra_powergate_sequence_power_up);
|
|
|
|
|
@@ -486,6 +692,155 @@ static int tegra_powergate_debugfs_init(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int tegra_powergate_of_get_clks(struct tegra_powergate *pg,
|
|
|
+ struct device_node *np)
|
|
|
+{
|
|
|
+ struct clk *clk;
|
|
|
+ unsigned int i, count;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ count = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
|
|
+ if (count == 0)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ pg->clks = kcalloc(count, sizeof(clk), GFP_KERNEL);
|
|
|
+ if (!pg->clks)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ pg->clks[i] = of_clk_get(np, i);
|
|
|
+ if (IS_ERR(pg->clks[i])) {
|
|
|
+ err = PTR_ERR(pg->clks[i]);
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pg->num_clks = count;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err:
|
|
|
+ while (i--)
|
|
|
+ clk_put(pg->clks[i]);
|
|
|
+ kfree(pg->clks);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
|
|
|
+ struct device_node *np)
|
|
|
+{
|
|
|
+ struct reset_control *rst;
|
|
|
+ unsigned int i, count;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ count = of_count_phandle_with_args(np, "resets", "#reset-cells");
|
|
|
+ if (count == 0)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ pg->resets = kcalloc(count, sizeof(rst), GFP_KERNEL);
|
|
|
+ if (!pg->resets)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ pg->resets[i] = of_reset_control_get_by_index(np, i);
|
|
|
+ if (IS_ERR(pg->resets[i])) {
|
|
|
+ err = PTR_ERR(pg->resets[i]);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pg->num_resets = count;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ while (i--)
|
|
|
+ reset_control_put(pg->resets[i]);
|
|
|
+ kfree(pg->resets);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
|
|
|
+{
|
|
|
+ struct tegra_powergate *pg;
|
|
|
+ bool off;
|
|
|
+ int id;
|
|
|
+
|
|
|
+ pg = kzalloc(sizeof(*pg), GFP_KERNEL);
|
|
|
+ if (!pg)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ id = tegra_powergate_lookup(pmc, np->name);
|
|
|
+ if (id < 0)
|
|
|
+ goto free_mem;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Clear the bit for this powergate so it cannot be managed
|
|
|
+ * directly via the legacy APIs for controlling powergates.
|
|
|
+ */
|
|
|
+ clear_bit(id, pmc->powergates_available);
|
|
|
+
|
|
|
+ pg->id = id;
|
|
|
+ pg->genpd.name = np->name;
|
|
|
+ pg->genpd.power_off = tegra_genpd_power_off;
|
|
|
+ pg->genpd.power_on = tegra_genpd_power_on;
|
|
|
+ pg->pmc = pmc;
|
|
|
+
|
|
|
+ if (tegra_powergate_of_get_clks(pg, np))
|
|
|
+ goto set_available;
|
|
|
+
|
|
|
+ if (tegra_powergate_of_get_resets(pg, np))
|
|
|
+ goto remove_clks;
|
|
|
+
|
|
|
+ off = !tegra_powergate_is_powered(pg->id);
|
|
|
+
|
|
|
+ pm_genpd_init(&pg->genpd, NULL, off);
|
|
|
+
|
|
|
+ if (of_genpd_add_provider_simple(np, &pg->genpd))
|
|
|
+ goto remove_resets;
|
|
|
+
|
|
|
+ dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name);
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+remove_resets:
|
|
|
+ while (pg->num_resets--)
|
|
|
+ reset_control_put(pg->resets[pg->num_resets]);
|
|
|
+ kfree(pg->resets);
|
|
|
+
|
|
|
+remove_clks:
|
|
|
+ while (pg->num_clks--)
|
|
|
+ clk_put(pg->clks[pg->num_clks]);
|
|
|
+ kfree(pg->clks);
|
|
|
+
|
|
|
+set_available:
|
|
|
+ set_bit(id, pmc->powergates_available);
|
|
|
+
|
|
|
+free_mem:
|
|
|
+ kfree(pg);
|
|
|
+
|
|
|
+error:
|
|
|
+ dev_err(pmc->dev, "failed to create power domain for %s\n", np->name);
|
|
|
+}
|
|
|
+
|
|
|
+static void tegra_powergate_init(struct tegra_pmc *pmc)
|
|
|
+{
|
|
|
+ struct device_node *np, *child;
|
|
|
+
|
|
|
+ np = of_get_child_by_name(pmc->dev->of_node, "powergates");
|
|
|
+ if (!np)
|
|
|
+ return;
|
|
|
+
|
|
|
+ for_each_child_of_node(np, child) {
|
|
|
+ tegra_powergate_add(pmc, child);
|
|
|
+ of_node_put(child);
|
|
|
+ }
|
|
|
+
|
|
|
+ of_node_put(np);
|
|
|
+}
|
|
|
+
|
|
|
static int tegra_io_rail_prepare(unsigned int id, unsigned long *request,
|
|
|
unsigned long *status, unsigned int *bit)
|
|
|
{
|
|
@@ -887,6 +1242,8 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+ tegra_powergate_init(pmc);
|
|
|
+
|
|
|
mutex_lock(&pmc->powergates_lock);
|
|
|
iounmap(pmc->base);
|
|
|
pmc->base = base;
|
|
@@ -1120,6 +1477,7 @@ static int __init tegra_pmc_early_init(void)
|
|
|
const struct of_device_id *match;
|
|
|
struct device_node *np;
|
|
|
struct resource regs;
|
|
|
+ unsigned int i;
|
|
|
bool invert;
|
|
|
u32 value;
|
|
|
|
|
@@ -1169,6 +1527,11 @@ static int __init tegra_pmc_early_init(void)
|
|
|
return -ENXIO;
|
|
|
}
|
|
|
|
|
|
+ /* Create a bit-map of the available and valid partitions */
|
|
|
+ for (i = 0; i < pmc->soc->num_powergates; i++)
|
|
|
+ if (pmc->soc->powergates[i])
|
|
|
+ set_bit(i, pmc->powergates_available);
|
|
|
+
|
|
|
mutex_init(&pmc->powergates_lock);
|
|
|
|
|
|
/*
|