linux/drivers/soc/tegra/powergate-bpmp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved
   4 */
   5
   6#include <linux/of.h>
   7#include <linux/platform_device.h>
   8#include <linux/pm_domain.h>
   9#include <linux/slab.h>
  10
  11#include <soc/tegra/bpmp.h>
  12#include <soc/tegra/bpmp-abi.h>
  13
  14struct tegra_powergate_info {
  15        unsigned int id;
  16        char *name;
  17};
  18
  19struct tegra_powergate {
  20        struct generic_pm_domain genpd;
  21        struct tegra_bpmp *bpmp;
  22        unsigned int id;
  23};
  24
  25static inline struct tegra_powergate *
  26to_tegra_powergate(struct generic_pm_domain *genpd)
  27{
  28        return container_of(genpd, struct tegra_powergate, genpd);
  29}
  30
  31static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp,
  32                                          unsigned int id, u32 state)
  33{
  34        struct mrq_pg_request request;
  35        struct tegra_bpmp_message msg;
  36        int err;
  37
  38        memset(&request, 0, sizeof(request));
  39        request.cmd = CMD_PG_SET_STATE;
  40        request.id = id;
  41        request.set_state.state = state;
  42
  43        memset(&msg, 0, sizeof(msg));
  44        msg.mrq = MRQ_PG;
  45        msg.tx.data = &request;
  46        msg.tx.size = sizeof(request);
  47
  48        err = tegra_bpmp_transfer(bpmp, &msg);
  49        if (err < 0)
  50                return err;
  51        else if (msg.rx.ret < 0)
  52                return -EINVAL;
  53
  54        return 0;
  55}
  56
  57static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp,
  58                                          unsigned int id)
  59{
  60        struct mrq_pg_response response;
  61        struct mrq_pg_request request;
  62        struct tegra_bpmp_message msg;
  63        int err;
  64
  65        memset(&request, 0, sizeof(request));
  66        request.cmd = CMD_PG_GET_STATE;
  67        request.id = id;
  68
  69        memset(&response, 0, sizeof(response));
  70
  71        memset(&msg, 0, sizeof(msg));
  72        msg.mrq = MRQ_PG;
  73        msg.tx.data = &request;
  74        msg.tx.size = sizeof(request);
  75        msg.rx.data = &response;
  76        msg.rx.size = sizeof(response);
  77
  78        err = tegra_bpmp_transfer(bpmp, &msg);
  79        if (err < 0)
  80                return PG_STATE_OFF;
  81        else if (msg.rx.ret < 0)
  82                return -EINVAL;
  83
  84        return response.get_state.state;
  85}
  86
  87static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp)
  88{
  89        struct mrq_pg_response response;
  90        struct mrq_pg_request request;
  91        struct tegra_bpmp_message msg;
  92        int err;
  93
  94        memset(&request, 0, sizeof(request));
  95        request.cmd = CMD_PG_GET_MAX_ID;
  96
  97        memset(&response, 0, sizeof(response));
  98
  99        memset(&msg, 0, sizeof(msg));
 100        msg.mrq = MRQ_PG;
 101        msg.tx.data = &request;
 102        msg.tx.size = sizeof(request);
 103        msg.rx.data = &response;
 104        msg.rx.size = sizeof(response);
 105
 106        err = tegra_bpmp_transfer(bpmp, &msg);
 107        if (err < 0)
 108                return err;
 109        else if (msg.rx.ret < 0)
 110                return -EINVAL;
 111
 112        return response.get_max_id.max_id;
 113}
 114
 115static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp,
 116                                           unsigned int id)
 117{
 118        struct mrq_pg_response response;
 119        struct mrq_pg_request request;
 120        struct tegra_bpmp_message msg;
 121        int err;
 122
 123        memset(&request, 0, sizeof(request));
 124        request.cmd = CMD_PG_GET_NAME;
 125        request.id = id;
 126
 127        memset(&response, 0, sizeof(response));
 128
 129        memset(&msg, 0, sizeof(msg));
 130        msg.mrq = MRQ_PG;
 131        msg.tx.data = &request;
 132        msg.tx.size = sizeof(request);
 133        msg.rx.data = &response;
 134        msg.rx.size = sizeof(response);
 135
 136        err = tegra_bpmp_transfer(bpmp, &msg);
 137        if (err < 0 || msg.rx.ret < 0)
 138                return NULL;
 139
 140        return kstrdup(response.get_name.name, GFP_KERNEL);
 141}
 142
 143static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp,
 144                                                   unsigned int id)
 145{
 146        return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF;
 147}
 148
 149static int tegra_powergate_power_on(struct generic_pm_domain *domain)
 150{
 151        struct tegra_powergate *powergate = to_tegra_powergate(domain);
 152        struct tegra_bpmp *bpmp = powergate->bpmp;
 153
 154        return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
 155                                              PG_STATE_ON);
 156}
 157
 158static int tegra_powergate_power_off(struct generic_pm_domain *domain)
 159{
 160        struct tegra_powergate *powergate = to_tegra_powergate(domain);
 161        struct tegra_bpmp *bpmp = powergate->bpmp;
 162
 163        return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
 164                                              PG_STATE_OFF);
 165}
 166
 167static struct tegra_powergate *
 168tegra_powergate_add(struct tegra_bpmp *bpmp,
 169                    const struct tegra_powergate_info *info)
 170{
 171        struct tegra_powergate *powergate;
 172        bool off;
 173        int err;
 174
 175        off = !tegra_bpmp_powergate_is_powered(bpmp, info->id);
 176
 177        powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL);
 178        if (!powergate)
 179                return ERR_PTR(-ENOMEM);
 180
 181        powergate->id = info->id;
 182        powergate->bpmp = bpmp;
 183
 184        powergate->genpd.name = kstrdup(info->name, GFP_KERNEL);
 185        powergate->genpd.power_on = tegra_powergate_power_on;
 186        powergate->genpd.power_off = tegra_powergate_power_off;
 187
 188        err = pm_genpd_init(&powergate->genpd, NULL, off);
 189        if (err < 0) {
 190                kfree(powergate->genpd.name);
 191                return ERR_PTR(err);
 192        }
 193
 194        return powergate;
 195}
 196
 197static void tegra_powergate_remove(struct tegra_powergate *powergate)
 198{
 199        struct generic_pm_domain *genpd = &powergate->genpd;
 200        struct tegra_bpmp *bpmp = powergate->bpmp;
 201        int err;
 202
 203        err = pm_genpd_remove(genpd);
 204        if (err < 0)
 205                dev_err(bpmp->dev, "failed to remove power domain %s: %d\n",
 206                        genpd->name, err);
 207
 208        kfree(genpd->name);
 209}
 210
 211static int
 212tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp,
 213                            struct tegra_powergate_info **powergatesp)
 214{
 215        struct tegra_powergate_info *powergates;
 216        unsigned int max_id, id, count = 0;
 217        unsigned int num_holes = 0;
 218        int err;
 219
 220        err = tegra_bpmp_powergate_get_max_id(bpmp);
 221        if (err < 0)
 222                return err;
 223
 224        max_id = err;
 225
 226        dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id);
 227
 228        powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL);
 229        if (!powergates)
 230                return -ENOMEM;
 231
 232        for (id = 0; id <= max_id; id++) {
 233                struct tegra_powergate_info *info = &powergates[count];
 234
 235                info->name = tegra_bpmp_powergate_get_name(bpmp, id);
 236                if (!info->name || info->name[0] == '\0') {
 237                        num_holes++;
 238                        continue;
 239                }
 240
 241                info->id = id;
 242                count++;
 243        }
 244
 245        dev_dbg(bpmp->dev, "holes: %u\n", num_holes);
 246
 247        *powergatesp = powergates;
 248
 249        return count;
 250}
 251
 252static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp,
 253                                     struct tegra_powergate_info *powergates,
 254                                     unsigned int count)
 255{
 256        struct genpd_onecell_data *genpd = &bpmp->genpd;
 257        struct generic_pm_domain **domains;
 258        struct tegra_powergate *powergate;
 259        unsigned int i;
 260        int err;
 261
 262        domains = kcalloc(count, sizeof(*domains), GFP_KERNEL);
 263        if (!domains)
 264                return -ENOMEM;
 265
 266        for (i = 0; i < count; i++) {
 267                powergate = tegra_powergate_add(bpmp, &powergates[i]);
 268                if (IS_ERR(powergate)) {
 269                        err = PTR_ERR(powergate);
 270                        goto remove;
 271                }
 272
 273                dev_dbg(bpmp->dev, "added power domain %s\n",
 274                        powergate->genpd.name);
 275                domains[i] = &powergate->genpd;
 276        }
 277
 278        genpd->num_domains = count;
 279        genpd->domains = domains;
 280
 281        return 0;
 282
 283remove:
 284        while (i--) {
 285                powergate = to_tegra_powergate(domains[i]);
 286                tegra_powergate_remove(powergate);
 287        }
 288
 289        kfree(genpd->domains);
 290        return err;
 291}
 292
 293static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp)
 294{
 295        struct genpd_onecell_data *genpd = &bpmp->genpd;
 296        unsigned int i = genpd->num_domains;
 297        struct tegra_powergate *powergate;
 298
 299        while (i--) {
 300                dev_dbg(bpmp->dev, "removing power domain %s\n",
 301                        genpd->domains[i]->name);
 302                powergate = to_tegra_powergate(genpd->domains[i]);
 303                tegra_powergate_remove(powergate);
 304        }
 305}
 306
 307static struct generic_pm_domain *
 308tegra_powergate_xlate(struct of_phandle_args *spec, void *data)
 309{
 310        struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
 311        struct genpd_onecell_data *genpd = data;
 312        unsigned int i;
 313
 314        for (i = 0; i < genpd->num_domains; i++) {
 315                struct tegra_powergate *powergate;
 316
 317                powergate = to_tegra_powergate(genpd->domains[i]);
 318                if (powergate->id == spec->args[0]) {
 319                        domain = &powergate->genpd;
 320                        break;
 321                }
 322        }
 323
 324        return domain;
 325}
 326
 327int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
 328{
 329        struct device_node *np = bpmp->dev->of_node;
 330        struct tegra_powergate_info *powergates;
 331        struct device *dev = bpmp->dev;
 332        unsigned int count, i;
 333        int err;
 334
 335        err = tegra_bpmp_probe_powergates(bpmp, &powergates);
 336        if (err < 0)
 337                return err;
 338
 339        count = err;
 340
 341        dev_dbg(dev, "%u power domains probed\n", count);
 342
 343        err = tegra_bpmp_add_powergates(bpmp, powergates, count);
 344        if (err < 0)
 345                goto free;
 346
 347        bpmp->genpd.xlate = tegra_powergate_xlate;
 348
 349        err = of_genpd_add_provider_onecell(np, &bpmp->genpd);
 350        if (err < 0) {
 351                dev_err(dev, "failed to add power domain provider: %d\n", err);
 352                tegra_bpmp_remove_powergates(bpmp);
 353        }
 354
 355free:
 356        for (i = 0; i < count; i++)
 357                kfree(powergates[i].name);
 358
 359        kfree(powergates);
 360        return err;
 361}
 362