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