tegrakernel/kernel/kernel-4.9/drivers/platform/tegra/pm_domains.c

154 lines
3.7 KiB
C
Raw Normal View History

2022-02-16 09:13:02 -06:00
/*
* drivers/platform/tegra/pm_domains.c
*
* Copyright (c) 2012-2018, 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/module.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/tegra_pm_domains.h>
#include <linux/tegra-powergate.h>
#include <linux/slab.h>
typedef int (*of_tegra_pd_init_cb_t)(struct generic_pm_domain *);
static int tegra_legacy_power_off(struct generic_pm_domain *genpd)
{
struct tegra_pm_domain *pd = to_tegra_pd(genpd);
return tegra_powergate_partition(pd->partition_id);
}
static int tegra_legacy_power_on(struct generic_pm_domain *genpd)
{
struct tegra_pm_domain *pd = to_tegra_pd(genpd);
return tegra_unpowergate_partition(pd->partition_id);
}
static int __init tegra_init_legacy_ops(struct generic_pm_domain *pd)
{
struct tegra_pm_domain *tpd = to_tegra_pd(pd);
if (tpd->partition_id == -1)
return -EINVAL;
pd->power_off = tegra_legacy_power_off;
pd->power_on = tegra_legacy_power_on;
return 0;
}
/* Do not add to this list */
static const struct of_device_id tegra_pd_match[] __initconst = {
{.compatible = "nvidia,tegra210-ape-pd", .data = NULL},
{.compatible = "nvidia,tegra210-pcie-pd", .data = tegra_init_legacy_ops},
{},
};
/* node: device tree node of the child power domain */
static int attach_subdomain(struct device_node *node)
{
int ret;
struct of_phandle_args master_phandle, child_phandle;
child_phandle.np = node;
child_phandle.args_count = 0;
ret = of_parse_phandle_with_args(node, "power-domains",
"#power-domain-cells", 0,
&master_phandle);
if (ret < 0)
return ret;
pr_info("Adding domain %s to PM domain %s\n",
node->name, master_phandle.np->name);
of_genpd_add_subdomain(&master_phandle, &child_phandle);
return 0;
}
static int __init tegra_init_pd(struct device_node *np)
{
struct tegra_pm_domain *tpd;
struct generic_pm_domain *gpd;
of_tegra_pd_init_cb_t tpd_init_cb;
const struct of_device_id *match = of_match_node(tegra_pd_match, np);
bool is_off = false;
tpd = (struct tegra_pm_domain *)kzalloc
(sizeof(struct tegra_pm_domain), GFP_KERNEL);
if (!tpd) {
pr_err("Failed to allocate memory for %s domain\n",
of_node_full_name(np));
return -ENOMEM;
}
gpd = &tpd->gpd;
gpd->name = (char *)np->name;
tpd_init_cb = match->data;
if (tpd_init_cb)
tpd_init_cb(gpd);
if (of_property_read_u32(np, "partition-id", &tpd->partition_id))
tpd->partition_id = -1;
is_off = !tegra_powergate_is_powered(tpd->partition_id);
pm_genpd_init(gpd, &simple_qos_governor, is_off);
of_genpd_add_provider_simple(np, gpd);
attach_subdomain(np);
return 0;
}
static int __init tegra_init_pm_domain(void)
{
struct device_node *np;
int ret = 0;
for_each_matching_node(np, tegra_pd_match) {
ret = tegra_init_pd(np);
if (ret)
return ret;
}
return ret;
}
core_initcall(tegra_init_pm_domain);
int tegra_pd_get_powergate_id(const struct of_device_id *dev_id)
{
struct device_node *dn = NULL;
u32 partition_id;
int ret = -EINVAL;
for_each_matching_node(dn, dev_id) {
ret = of_property_read_u32(dn, "partition-id", &partition_id);
break;
}
if (ret) {
pr_err("Reading powergate-id failed\n");
return ret;
}
return partition_id;
}
EXPORT_SYMBOL(tegra_pd_get_powergate_id);