soc/tegra: Add generic PM domain support
Implements generic PM domain support on top of the existing Tegra power- gate API. Drivers are thus allowed to move away from the Tegra-specific API and towards using generic power domains directly. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXI3unAAoJEN0jrNd/PrOhdPIP/jOgS5Qf1u8QelFvPbsXouRf xpBy9sa43oow8f+0RVjTrsWDikjniCYcGDPZbfgmXEN5sWYK2xBIZeaMysGezPXS /g9VnxU0HZsQ+5AOWHS/vxYLfB9Q6g3w4P7XDDzKmJ9mFaLeyNR4ulGWwLxpeF/1 au38f/dXgjU9I+NwXJWhTEspQrXAshIwp6lsQkjcpLmAMeFBOjB7c92uTYmVXI9F XtGcpGmIqDwRaieY1lqE0iGUMgox2XG2bKgNuwZ+Eng+ICz2ACuGfyH7hwCeWJpK HByxL83aA9a+G8KIsPZgJYTCuQA7UVYjWp5A5UqpuhsP7Da4EagX3Bdvcwqy/sgk rcLNSTuksbJyHT/yQO5cj9lPrdFckSglChZlSRX1J5FhQN2xHw/1CmfDgPmZ1Htt D+66iLm3lJ+SZlwaZToofU89GYRc+GoZVPQrMBMCwTIBcYf6ACtG7hYIB6JrxHJg xyiSbDKRYyHBWHoL534G2S8xtnTjMHG1RLD2SoI49Qsa9OTmJz50NainElPi2DX5 266zz87Fa+5WePEp0NjBIJ0Hgq4uoMJFpvkG2pzODb2fwHxzW7E/KB6pAV9/+1EZ F+sPDHUnzTSLx76h7vs1v4ye9TXrcuKXXnhU7YBm6eWCgeM3GOf6V8u7z6BQDxxT rAYhbGOeeAiORGdMUo4h =oeBF -----END PGP SIGNATURE----- Merge tag 'tegra-for-4.7-genpd' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into next/drivers Merge "soc/tegra: Add generic PM domain support" from Thierry Reding: Implements generic PM domain support on top of the existing Tegra power- gate API. Drivers are thus allowed to move away from the Tegra-specific API and towards using generic power domains directly. * tag 'tegra-for-4.7-genpd' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux: soc/tegra: pmc: Add generic PM domain support dt-bindings: Add power domain info for NVIDIA PMC
This commit is contained in:
commit
b7dcc6d01f
|
@ -1,5 +1,7 @@
|
|||
NVIDIA Tegra Power Management Controller (PMC)
|
||||
|
||||
== Power Management Controller Node ==
|
||||
|
||||
The PMC block interacts with an external Power Management Unit. The PMC
|
||||
mostly controls the entry and exit of the system from different sleep
|
||||
modes. It provides power-gating controllers for SoC and CPU power-islands.
|
||||
|
@ -70,6 +72,11 @@ Optional properties for hardware-triggered thermal reset (inside 'i2c-thermtrip'
|
|||
Defaults to 0. Valid values are described in section 12.5.2
|
||||
"Pinmux Support" of the Tegra4 Technical Reference Manual.
|
||||
|
||||
Optional nodes:
|
||||
- powergates : This node contains a hierarchy of power domain nodes, which
|
||||
should match the powergates on the Tegra SoC. See "Powergate
|
||||
Nodes" below.
|
||||
|
||||
Example:
|
||||
|
||||
/ SoC dts including file
|
||||
|
@ -115,3 +122,76 @@ pmc@7000f400 {
|
|||
};
|
||||
...
|
||||
};
|
||||
|
||||
|
||||
== Powergate Nodes ==
|
||||
|
||||
Each of the powergate nodes represents a power-domain on the Tegra SoC
|
||||
that can be power-gated by the Tegra PMC. The name of the powergate node
|
||||
should be one of the below. Note that not every powergate is applicable
|
||||
to all Tegra devices and the following list shows which powergates are
|
||||
applicable to which devices. Please refer to the Tegra TRM for more
|
||||
details on the various powergates.
|
||||
|
||||
Name Description Devices Applicable
|
||||
3d 3D Graphics Tegra20/114/124/210
|
||||
3d0 3D Graphics 0 Tegra30
|
||||
3d1 3D Graphics 1 Tegra30
|
||||
aud Audio Tegra210
|
||||
dfd Debug Tegra210
|
||||
dis Display A Tegra114/124/210
|
||||
disb Display B Tegra114/124/210
|
||||
heg 2D Graphics Tegra30/114/124/210
|
||||
iram Internal RAM Tegra124/210
|
||||
mpe MPEG Encode All
|
||||
nvdec NVIDIA Video Decode Engine Tegra210
|
||||
nvjpg NVIDIA JPEG Engine Tegra210
|
||||
pcie PCIE Tegra20/30/124/210
|
||||
sata SATA Tegra30/124/210
|
||||
sor Display interfaces Tegra124/210
|
||||
ve2 Video Encode Engine 2 Tegra210
|
||||
venc Video Encode Engine All
|
||||
vdec Video Decode Engine Tegra20/30/114/124
|
||||
vic Video Imaging Compositor Tegra124/210
|
||||
xusba USB Partition A Tegra114/124/210
|
||||
xusbb USB Partition B Tegra114/124/210
|
||||
xusbc USB Partition C Tegra114/124/210
|
||||
|
||||
Required properties:
|
||||
- clocks: Must contain an entry for each clock required by the PMC for
|
||||
controlling a power-gate. See ../clocks/clock-bindings.txt for details.
|
||||
- resets: Must contain an entry for each reset required by the PMC for
|
||||
controlling a power-gate. See ../reset/reset.txt for details.
|
||||
- #power-domain-cells: Must be 0.
|
||||
|
||||
Example:
|
||||
|
||||
pmc: pmc@7000e400 {
|
||||
compatible = "nvidia,tegra210-pmc";
|
||||
reg = <0x0 0x7000e400 0x0 0x400>;
|
||||
clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
|
||||
clock-names = "pclk", "clk32k_in";
|
||||
|
||||
powergates {
|
||||
pd_audio: aud {
|
||||
clocks = <&tegra_car TEGRA210_CLK_APE>,
|
||||
<&tegra_car TEGRA210_CLK_APB2APE>;
|
||||
resets = <&tegra_car 198>;
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
== Powergate Clients ==
|
||||
|
||||
Hardware blocks belonging to a power domain should contain a "power-domains"
|
||||
property that is a phandle pointing to the corresponding powergate node.
|
||||
|
||||
Example:
|
||||
|
||||
adma: adma@702e2000 {
|
||||
...
|
||||
power-domains = <&pd_audio>;
|
||||
...
|
||||
};
|
||||
|
|
|
@ -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,60 +264,10 @@ static int tegra_powergate_set(unsigned int id, bool new_state)
|
|||
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))
|
||||
return -EINVAL;
|
||||
|
||||
return tegra_powergate_set(id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_powergate_power_off() - power off partition
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_power_off(unsigned int id)
|
||||
{
|
||||
if (!tegra_powergate_is_valid(id))
|
||||
return -EINVAL;
|
||||
|
||||
return tegra_powergate_set(id, false);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_powergate_power_off);
|
||||
|
||||
/**
|
||||
* tegra_powergate_is_powered() - check if partition is powered
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_is_powered(unsigned int id)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!tegra_powergate_is_valid(id))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pmc->powergates_lock);
|
||||
status = tegra_powergate_state(id);
|
||||
mutex_unlock(&pmc->powergates_lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_powergate_remove_clamping() - remove power clamps for partition
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_remove_clamping(unsigned int id)
|
||||
static int __tegra_powergate_remove_clamping(unsigned int id)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
if (!tegra_powergate_is_valid(id))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pmc->powergates_lock);
|
||||
|
||||
/*
|
||||
|
@ -303,6 +299,231 @@ out:
|
|||
|
||||
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_available(id))
|
||||
return -EINVAL;
|
||||
|
||||
return tegra_powergate_set(id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_powergate_power_off() - power off partition
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_power_off(unsigned int id)
|
||||
{
|
||||
if (!tegra_powergate_is_available(id))
|
||||
return -EINVAL;
|
||||
|
||||
return tegra_powergate_set(id, false);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_powergate_power_off);
|
||||
|
||||
/**
|
||||
* tegra_powergate_is_powered() - check if partition is powered
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_is_powered(unsigned int id)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!tegra_powergate_is_valid(id))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pmc->powergates_lock);
|
||||
status = tegra_powergate_state(id);
|
||||
mutex_unlock(&pmc->powergates_lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_powergate_remove_clamping() - remove power clamps for partition
|
||||
* @id: partition ID
|
||||
*/
|
||||
int tegra_powergate_remove_clamping(unsigned int id)
|
||||
{
|
||||
if (!tegra_powergate_is_available(id))
|
||||
return -EINVAL;
|
||||
|
||||
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;
|
||||
struct tegra_powergate pg;
|
||||
int err;
|
||||
|
||||
reset_control_assert(rst);
|
||||
pg.id = id;
|
||||
pg.clks = &clk;
|
||||
pg.num_clks = 1;
|
||||
pg.resets = &rst;
|
||||
pg.num_resets = 1;
|
||||
|
||||
ret = tegra_powergate_power_on(id);
|
||||
if (ret)
|
||||
goto err_power;
|
||||
err = tegra_powergate_power_up(&pg, false);
|
||||
if (err)
|
||||
pr_err("failed to turn on partition %d: %d\n", id, err);
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
usleep_range(10, 20);
|
||||
|
||||
ret = tegra_powergate_remove_clamping(id);
|
||||
if (ret)
|
||||
goto err_clamp;
|
||||
|
||||
usleep_range(10, 20);
|
||||
reset_control_deassert(rst);
|
||||
|
||||
return 0;
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -72,6 +72,7 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid);
|
|||
#define TEGRA_POWERGATE_AUD 27
|
||||
#define TEGRA_POWERGATE_DFD 28
|
||||
#define TEGRA_POWERGATE_VE2 29
|
||||
#define TEGRA_POWERGATE_MAX TEGRA_POWERGATE_VE2
|
||||
|
||||
#define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D
|
||||
|
||||
|
|
Loading…
Reference in New Issue