mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 19:51:31 -04:00
soc/tegra: Changes for v4.13-rc1
This contains an implementation of generic PM domains for Tegra186, based on the BPMP powergate request. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAllDj1wTHHRyZWRpbmdA bnZpZGlhLmNvbQAKCRDdI6zXfz6zofYAD/wKf+1uvTiFCXTHe+ebKBrsxpiF9na+ PDARXtOzjv5dWZOe4IY1NaejyOFL5TAjjB0WSjaj1y/dqd6UmqaJynaUkc7lcGhp wol8NNecQQosex8Bcswy+96HsnN9kTI7LvZqHAL5boGkzDygj6bNvDpB6rdkONlD gkOORyeuQnP3IIEstk1v1r8s+7NlcNYqGoFqXP9qIfD4LVqVA/IXK86riKiP8Bou s9E4cbXvwhZhZ76m5hLNrGU880CpyLf6JCExgqaLUNmqhZ49ZLXY8L4ReewWB3tI WRAzt6kOsj4EzPb0h8wksMWbxHsQ79ms3rA1YtjUb2zJzk6gbv9DNhEwarj/qyz7 16K158Uxhi2iELfnLFICq24U9wtqe5bGw/FLJASSD2Quv8aRih6p/oLdfatZ5kAh unR1HYTSw7z1XFjhGUEwb7x1MRNuS5dpMWar5L8nn4b5bACGvsNUHm+sID5TJlgR CowaYqO1QGxB4EpxMaGMzzEOieWofvy8Bu0WX3tTzuwPALq9LOOfQstmU69I4/Og tkrirL+TkBJEi8bMmn64iGMj0aOq33czpbFBdzaL7BDYHbOaB34bAErrJwQIYHNK gLMyQXD1TaBGliJtEeN+FsFFpHsVdB9wy0wGCkwu9nPCigoYOt52fQAnjrP7w26P ZKLNmUpszxrIVQ== =uyV7 -----END PGP SIGNATURE----- Merge tag 'tegra-for-4.13-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into next/drivers soc/tegra: Changes for v4.13-rc1 This contains an implementation of generic PM domains for Tegra186, based on the BPMP powergate request. * tag 'tegra-for-4.13-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux: soc/tegra: flowctrl: Fix error handling soc/tegra: bpmp: Implement generic PM domains soc/tegra: bpmp: Update ABI header PM / Domains: Allow overriding the ->xlate() callback Signed-off-by: Olof Johansson <[email protected]>
This commit is contained in:
commit
f39b24e0b4
9 changed files with 798 additions and 15 deletions
|
|
@ -115,3 +115,8 @@ config SOC_TEGRA_PMC
|
|||
|
||||
config SOC_TEGRA_PMC_TEGRA186
|
||||
bool
|
||||
|
||||
config SOC_TEGRA_POWERGATE_BPMP
|
||||
def_bool y
|
||||
depends on PM_GENERIC_DOMAINS
|
||||
depends on TEGRA_BPMP
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ obj-y += common.o
|
|||
obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
|
||||
obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
|
||||
obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o
|
||||
obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ static int tegra_flowctrl_probe(struct platform_device *pdev)
|
|||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(tegra_flowctrl_base))
|
||||
return PTR_ERR(base);
|
||||
return PTR_ERR(tegra_flowctrl_base);
|
||||
|
||||
iounmap(base);
|
||||
|
||||
|
|
|
|||
359
drivers/soc/tegra/powergate-bpmp.c
Normal file
359
drivers/soc/tegra/powergate-bpmp.c
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <soc/tegra/bpmp.h>
|
||||
#include <soc/tegra/bpmp-abi.h>
|
||||
|
||||
struct tegra_powergate_info {
|
||||
unsigned int id;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct tegra_powergate {
|
||||
struct generic_pm_domain genpd;
|
||||
struct tegra_bpmp *bpmp;
|
||||
unsigned int id;
|
||||
};
|
||||
|
||||
static inline struct tegra_powergate *
|
||||
to_tegra_powergate(struct generic_pm_domain *genpd)
|
||||
{
|
||||
return container_of(genpd, struct tegra_powergate, genpd);
|
||||
}
|
||||
|
||||
static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp,
|
||||
unsigned int id, u32 state)
|
||||
{
|
||||
struct mrq_pg_request request;
|
||||
struct tegra_bpmp_message msg;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.cmd = CMD_PG_SET_STATE;
|
||||
request.id = id;
|
||||
request.set_state.state = state;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.mrq = MRQ_PG;
|
||||
msg.tx.data = &request;
|
||||
msg.tx.size = sizeof(request);
|
||||
|
||||
return tegra_bpmp_transfer(bpmp, &msg);
|
||||
}
|
||||
|
||||
static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp,
|
||||
unsigned int id)
|
||||
{
|
||||
struct mrq_pg_response response;
|
||||
struct mrq_pg_request request;
|
||||
struct tegra_bpmp_message msg;
|
||||
int err;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.cmd = CMD_PG_GET_STATE;
|
||||
request.id = id;
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.mrq = MRQ_PG;
|
||||
msg.tx.data = &request;
|
||||
msg.tx.size = sizeof(request);
|
||||
msg.rx.data = &response;
|
||||
msg.rx.size = sizeof(response);
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return PG_STATE_OFF;
|
||||
|
||||
return response.get_state.state;
|
||||
}
|
||||
|
||||
static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp)
|
||||
{
|
||||
struct mrq_pg_response response;
|
||||
struct mrq_pg_request request;
|
||||
struct tegra_bpmp_message msg;
|
||||
int err;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.cmd = CMD_PG_GET_MAX_ID;
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.mrq = MRQ_PG;
|
||||
msg.tx.data = &request;
|
||||
msg.tx.size = sizeof(request);
|
||||
msg.rx.data = &response;
|
||||
msg.rx.size = sizeof(response);
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return response.get_max_id.max_id;
|
||||
}
|
||||
|
||||
static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp,
|
||||
unsigned int id)
|
||||
{
|
||||
struct mrq_pg_response response;
|
||||
struct mrq_pg_request request;
|
||||
struct tegra_bpmp_message msg;
|
||||
int err;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.cmd = CMD_PG_GET_NAME;
|
||||
request.id = id;
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.mrq = MRQ_PG;
|
||||
msg.tx.data = &request;
|
||||
msg.tx.size = sizeof(request);
|
||||
msg.rx.data = &response;
|
||||
msg.rx.size = sizeof(response);
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return NULL;
|
||||
|
||||
return kstrdup(response.get_name.name, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp,
|
||||
unsigned int id)
|
||||
{
|
||||
return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF;
|
||||
}
|
||||
|
||||
static int tegra_powergate_power_on(struct generic_pm_domain *domain)
|
||||
{
|
||||
struct tegra_powergate *powergate = to_tegra_powergate(domain);
|
||||
struct tegra_bpmp *bpmp = powergate->bpmp;
|
||||
|
||||
return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
|
||||
PG_STATE_ON);
|
||||
}
|
||||
|
||||
static int tegra_powergate_power_off(struct generic_pm_domain *domain)
|
||||
{
|
||||
struct tegra_powergate *powergate = to_tegra_powergate(domain);
|
||||
struct tegra_bpmp *bpmp = powergate->bpmp;
|
||||
|
||||
return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
|
||||
PG_STATE_OFF);
|
||||
}
|
||||
|
||||
static struct tegra_powergate *
|
||||
tegra_powergate_add(struct tegra_bpmp *bpmp,
|
||||
const struct tegra_powergate_info *info)
|
||||
{
|
||||
struct tegra_powergate *powergate;
|
||||
bool off;
|
||||
int err;
|
||||
|
||||
off = !tegra_bpmp_powergate_is_powered(bpmp, info->id);
|
||||
|
||||
powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL);
|
||||
if (!powergate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
powergate->id = info->id;
|
||||
powergate->bpmp = bpmp;
|
||||
|
||||
powergate->genpd.name = kstrdup(info->name, GFP_KERNEL);
|
||||
powergate->genpd.power_on = tegra_powergate_power_on;
|
||||
powergate->genpd.power_off = tegra_powergate_power_off;
|
||||
|
||||
err = pm_genpd_init(&powergate->genpd, NULL, off);
|
||||
if (err < 0) {
|
||||
kfree(powergate->genpd.name);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return powergate;
|
||||
}
|
||||
|
||||
static void tegra_powergate_remove(struct tegra_powergate *powergate)
|
||||
{
|
||||
struct generic_pm_domain *genpd = &powergate->genpd;
|
||||
struct tegra_bpmp *bpmp = powergate->bpmp;
|
||||
int err;
|
||||
|
||||
err = pm_genpd_remove(genpd);
|
||||
if (err < 0)
|
||||
dev_err(bpmp->dev, "failed to remove power domain %s: %d\n",
|
||||
genpd->name, err);
|
||||
|
||||
kfree(genpd->name);
|
||||
}
|
||||
|
||||
static int
|
||||
tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp,
|
||||
struct tegra_powergate_info **powergatesp)
|
||||
{
|
||||
struct tegra_powergate_info *powergates;
|
||||
unsigned int max_id, id, count = 0;
|
||||
unsigned int num_holes = 0;
|
||||
int err;
|
||||
|
||||
err = tegra_bpmp_powergate_get_max_id(bpmp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
max_id = err;
|
||||
|
||||
dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id);
|
||||
|
||||
powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL);
|
||||
if (!powergates)
|
||||
return -ENOMEM;
|
||||
|
||||
for (id = 0; id <= max_id; id++) {
|
||||
struct tegra_powergate_info *info = &powergates[count];
|
||||
|
||||
info->name = tegra_bpmp_powergate_get_name(bpmp, id);
|
||||
if (!info->name || info->name[0] == '\0') {
|
||||
num_holes++;
|
||||
continue;
|
||||
}
|
||||
|
||||
info->id = id;
|
||||
count++;
|
||||
}
|
||||
|
||||
dev_dbg(bpmp->dev, "holes: %u\n", num_holes);
|
||||
|
||||
*powergatesp = powergates;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp,
|
||||
struct tegra_powergate_info *powergates,
|
||||
unsigned int count)
|
||||
{
|
||||
struct genpd_onecell_data *genpd = &bpmp->genpd;
|
||||
struct generic_pm_domain **domains;
|
||||
struct tegra_powergate *powergate;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
domains = kcalloc(count, sizeof(*domains), GFP_KERNEL);
|
||||
if (!domains)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
powergate = tegra_powergate_add(bpmp, &powergates[i]);
|
||||
if (IS_ERR(powergate)) {
|
||||
err = PTR_ERR(powergate);
|
||||
goto remove;
|
||||
}
|
||||
|
||||
dev_dbg(bpmp->dev, "added power domain %s\n",
|
||||
powergate->genpd.name);
|
||||
domains[i] = &powergate->genpd;
|
||||
}
|
||||
|
||||
genpd->num_domains = count;
|
||||
genpd->domains = domains;
|
||||
|
||||
return 0;
|
||||
|
||||
remove:
|
||||
while (i--) {
|
||||
powergate = to_tegra_powergate(domains[i]);
|
||||
tegra_powergate_remove(powergate);
|
||||
}
|
||||
|
||||
kfree(genpd->domains);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp)
|
||||
{
|
||||
struct genpd_onecell_data *genpd = &bpmp->genpd;
|
||||
unsigned int i = genpd->num_domains;
|
||||
struct tegra_powergate *powergate;
|
||||
|
||||
while (i--) {
|
||||
dev_dbg(bpmp->dev, "removing power domain %s\n",
|
||||
genpd->domains[i]->name);
|
||||
powergate = to_tegra_powergate(genpd->domains[i]);
|
||||
tegra_powergate_remove(powergate);
|
||||
}
|
||||
}
|
||||
|
||||
static struct generic_pm_domain *
|
||||
tegra_powergate_xlate(struct of_phandle_args *spec, void *data)
|
||||
{
|
||||
struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
|
||||
struct genpd_onecell_data *genpd = data;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < genpd->num_domains; i++) {
|
||||
struct tegra_powergate *powergate;
|
||||
|
||||
powergate = to_tegra_powergate(genpd->domains[i]);
|
||||
if (powergate->id == spec->args[0]) {
|
||||
domain = &powergate->genpd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
|
||||
{
|
||||
struct device_node *np = bpmp->dev->of_node;
|
||||
struct tegra_powergate_info *powergates;
|
||||
struct device *dev = bpmp->dev;
|
||||
unsigned int count, i;
|
||||
int err;
|
||||
|
||||
err = tegra_bpmp_probe_powergates(bpmp, &powergates);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
count = err;
|
||||
|
||||
dev_dbg(dev, "%u power domains probed\n", count);
|
||||
|
||||
err = tegra_bpmp_add_powergates(bpmp, powergates, count);
|
||||
if (err < 0)
|
||||
goto free;
|
||||
|
||||
bpmp->genpd.xlate = tegra_powergate_xlate;
|
||||
|
||||
err = of_genpd_add_provider_onecell(np, &bpmp->genpd);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to add power domain provider: %d\n", err);
|
||||
tegra_bpmp_remove_powergates(bpmp);
|
||||
}
|
||||
|
||||
free:
|
||||
for (i = 0; i < count; i++)
|
||||
kfree(powergates[i].name);
|
||||
|
||||
kfree(powergates);
|
||||
return err;
|
||||
}
|
||||
Loading…
Reference in a new issue