123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved
- */
- #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;
- int err;
- 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);
- err = tegra_bpmp_transfer(bpmp, &msg);
- if (err < 0)
- return err;
- else if (msg.rx.ret < 0)
- return -EINVAL;
- return 0;
- }
- 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;
- else if (msg.rx.ret < 0)
- return -EINVAL;
- 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;
- else if (msg.rx.ret < 0)
- return -EINVAL;
- 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 || msg.rx.ret < 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;
- }
|