linux/drivers/soc/renesas/rmobile-sysc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * rmobile power management support
   4 *
   5 * Copyright (C) 2012  Renesas Solutions Corp.
   6 * Copyright (C) 2012  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
   7 * Copyright (C) 2014  Glider bvba
   8 *
   9 * based on pm-sh7372.c
  10 *  Copyright (C) 2011 Magnus Damm
  11 */
  12#include <linux/clk/renesas.h>
  13#include <linux/console.h>
  14#include <linux/delay.h>
  15#include <linux/of.h>
  16#include <linux/of_address.h>
  17#include <linux/of_platform.h>
  18#include <linux/platform_device.h>
  19#include <linux/pm.h>
  20#include <linux/pm_clock.h>
  21#include <linux/pm_domain.h>
  22#include <linux/slab.h>
  23
  24#include <asm/io.h>
  25
  26/* SYSC */
  27#define SPDCR           0x08    /* SYS Power Down Control Register */
  28#define SWUCR           0x14    /* SYS Wakeup Control Register */
  29#define PSTR            0x80    /* Power Status Register */
  30
  31#define PSTR_RETRIES    100
  32#define PSTR_DELAY_US   10
  33
  34struct rmobile_pm_domain {
  35        struct generic_pm_domain genpd;
  36        struct dev_power_governor *gov;
  37        int (*suspend)(void);
  38        void __iomem *base;
  39        unsigned int bit_shift;
  40};
  41
  42static inline
  43struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d)
  44{
  45        return container_of(d, struct rmobile_pm_domain, genpd);
  46}
  47
  48static int rmobile_pd_power_down(struct generic_pm_domain *genpd)
  49{
  50        struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd);
  51        unsigned int mask = BIT(rmobile_pd->bit_shift);
  52
  53        if (rmobile_pd->suspend) {
  54                int ret = rmobile_pd->suspend();
  55
  56                if (ret)
  57                        return ret;
  58        }
  59
  60        if (__raw_readl(rmobile_pd->base + PSTR) & mask) {
  61                unsigned int retry_count;
  62                __raw_writel(mask, rmobile_pd->base + SPDCR);
  63
  64                for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
  65                        if (!(__raw_readl(rmobile_pd->base + SPDCR) & mask))
  66                                break;
  67                        cpu_relax();
  68                }
  69        }
  70
  71        pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", genpd->name, mask,
  72                 __raw_readl(rmobile_pd->base + PSTR));
  73
  74        return 0;
  75}
  76
  77static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd)
  78{
  79        unsigned int mask = BIT(rmobile_pd->bit_shift);
  80        unsigned int retry_count;
  81        int ret = 0;
  82
  83        if (__raw_readl(rmobile_pd->base + PSTR) & mask)
  84                return ret;
  85
  86        __raw_writel(mask, rmobile_pd->base + SWUCR);
  87
  88        for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
  89                if (!(__raw_readl(rmobile_pd->base + SWUCR) & mask))
  90                        break;
  91                if (retry_count > PSTR_RETRIES)
  92                        udelay(PSTR_DELAY_US);
  93                else
  94                        cpu_relax();
  95        }
  96        if (!retry_count)
  97                ret = -EIO;
  98
  99        pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n",
 100                 rmobile_pd->genpd.name, mask,
 101                 __raw_readl(rmobile_pd->base + PSTR));
 102
 103        return ret;
 104}
 105
 106static int rmobile_pd_power_up(struct generic_pm_domain *genpd)
 107{
 108        return __rmobile_pd_power_up(to_rmobile_pd(genpd));
 109}
 110
 111static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
 112{
 113        struct generic_pm_domain *genpd = &rmobile_pd->genpd;
 114        struct dev_power_governor *gov = rmobile_pd->gov;
 115
 116        genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
 117        genpd->attach_dev = cpg_mstp_attach_dev;
 118        genpd->detach_dev = cpg_mstp_detach_dev;
 119
 120        if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) {
 121                genpd->power_off = rmobile_pd_power_down;
 122                genpd->power_on = rmobile_pd_power_up;
 123                __rmobile_pd_power_up(rmobile_pd);
 124        }
 125
 126        pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
 127}
 128
 129static int rmobile_pd_suspend_console(void)
 130{
 131        /*
 132         * Serial consoles make use of SCIF hardware located in this domain,
 133         * hence keep the power domain on if "no_console_suspend" is set.
 134         */
 135        return console_suspend_enabled ? 0 : -EBUSY;
 136}
 137
 138enum pd_types {
 139        PD_NORMAL,
 140        PD_CPU,
 141        PD_CONSOLE,
 142        PD_DEBUG,
 143        PD_MEMCTL,
 144};
 145
 146#define MAX_NUM_SPECIAL_PDS     16
 147
 148static struct special_pd {
 149        struct device_node *pd;
 150        enum pd_types type;
 151} special_pds[MAX_NUM_SPECIAL_PDS] __initdata;
 152
 153static unsigned int num_special_pds __initdata;
 154
 155static const struct of_device_id special_ids[] __initconst = {
 156        { .compatible = "arm,coresight-etm3x", .data = (void *)PD_DEBUG },
 157        { .compatible = "renesas,dbsc-r8a73a4", .data = (void *)PD_MEMCTL, },
 158        { .compatible = "renesas,dbsc3-r8a7740", .data = (void *)PD_MEMCTL, },
 159        { .compatible = "renesas,sbsc-sh73a0", .data = (void *)PD_MEMCTL, },
 160        { /* sentinel */ },
 161};
 162
 163static void __init add_special_pd(struct device_node *np, enum pd_types type)
 164{
 165        unsigned int i;
 166        struct device_node *pd;
 167
 168        pd = of_parse_phandle(np, "power-domains", 0);
 169        if (!pd)
 170                return;
 171
 172        for (i = 0; i < num_special_pds; i++)
 173                if (pd == special_pds[i].pd && type == special_pds[i].type) {
 174                        of_node_put(pd);
 175                        return;
 176                }
 177
 178        if (num_special_pds == ARRAY_SIZE(special_pds)) {
 179                pr_warn("Too many special PM domains\n");
 180                of_node_put(pd);
 181                return;
 182        }
 183
 184        pr_debug("Special PM domain %pOFn type %d for %pOF\n", pd, type, np);
 185
 186        special_pds[num_special_pds].pd = pd;
 187        special_pds[num_special_pds].type = type;
 188        num_special_pds++;
 189}
 190
 191static void __init get_special_pds(void)
 192{
 193        struct device_node *np;
 194        const struct of_device_id *id;
 195
 196        /* PM domains containing CPUs */
 197        for_each_of_cpu_node(np)
 198                add_special_pd(np, PD_CPU);
 199
 200        /* PM domain containing console */
 201        if (of_stdout)
 202                add_special_pd(of_stdout, PD_CONSOLE);
 203
 204        /* PM domains containing other special devices */
 205        for_each_matching_node_and_match(np, special_ids, &id)
 206                add_special_pd(np, (enum pd_types)id->data);
 207}
 208
 209static void __init put_special_pds(void)
 210{
 211        unsigned int i;
 212
 213        for (i = 0; i < num_special_pds; i++)
 214                of_node_put(special_pds[i].pd);
 215}
 216
 217static enum pd_types __init pd_type(const struct device_node *pd)
 218{
 219        unsigned int i;
 220
 221        for (i = 0; i < num_special_pds; i++)
 222                if (pd == special_pds[i].pd)
 223                        return special_pds[i].type;
 224
 225        return PD_NORMAL;
 226}
 227
 228static void __init rmobile_setup_pm_domain(struct device_node *np,
 229                                           struct rmobile_pm_domain *pd)
 230{
 231        const char *name = pd->genpd.name;
 232
 233        switch (pd_type(np)) {
 234        case PD_CPU:
 235                /*
 236                 * This domain contains the CPU core and therefore it should
 237                 * only be turned off if the CPU is not in use.
 238                 */
 239                pr_debug("PM domain %s contains CPU\n", name);
 240                pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
 241                break;
 242
 243        case PD_CONSOLE:
 244                pr_debug("PM domain %s contains serial console\n", name);
 245                pd->gov = &pm_domain_always_on_gov;
 246                pd->suspend = rmobile_pd_suspend_console;
 247                break;
 248
 249        case PD_DEBUG:
 250                /*
 251                 * This domain contains the Coresight-ETM hardware block and
 252                 * therefore it should only be turned off if the debug module
 253                 * is not in use.
 254                 */
 255                pr_debug("PM domain %s contains Coresight-ETM\n", name);
 256                pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
 257                break;
 258
 259        case PD_MEMCTL:
 260                /*
 261                 * This domain contains a memory-controller and therefore it
 262                 * should only be turned off if memory is not in use.
 263                 */
 264                pr_debug("PM domain %s contains MEMCTL\n", name);
 265                pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
 266                break;
 267
 268        case PD_NORMAL:
 269                if (pd->bit_shift == ~0) {
 270                        /* Top-level always-on domain */
 271                        pr_debug("PM domain %s is always-on domain\n", name);
 272                        pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
 273                }
 274                break;
 275        }
 276
 277        rmobile_init_pm_domain(pd);
 278}
 279
 280static int __init rmobile_add_pm_domains(void __iomem *base,
 281                                         struct device_node *parent,
 282                                         struct generic_pm_domain *genpd_parent)
 283{
 284        struct device_node *np;
 285
 286        for_each_child_of_node(parent, np) {
 287                struct rmobile_pm_domain *pd;
 288                u32 idx = ~0;
 289
 290                if (of_property_read_u32(np, "reg", &idx)) {
 291                        /* always-on domain */
 292                }
 293
 294                pd = kzalloc(sizeof(*pd), GFP_KERNEL);
 295                if (!pd) {
 296                        of_node_put(np);
 297                        return -ENOMEM;
 298                }
 299
 300                pd->genpd.name = np->name;
 301                pd->base = base;
 302                pd->bit_shift = idx;
 303
 304                rmobile_setup_pm_domain(np, pd);
 305                if (genpd_parent)
 306                        pm_genpd_add_subdomain(genpd_parent, &pd->genpd);
 307                of_genpd_add_provider_simple(np, &pd->genpd);
 308
 309                rmobile_add_pm_domains(base, np, &pd->genpd);
 310        }
 311        return 0;
 312}
 313
 314static int __init rmobile_init_pm_domains(void)
 315{
 316        struct device_node *np, *pmd;
 317        bool scanned = false;
 318        void __iomem *base;
 319        int ret = 0;
 320
 321        for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") {
 322                base = of_iomap(np, 0);
 323                if (!base) {
 324                        pr_warn("%pOF cannot map reg 0\n", np);
 325                        continue;
 326                }
 327
 328                pmd = of_get_child_by_name(np, "pm-domains");
 329                if (!pmd) {
 330                        pr_warn("%pOF lacks pm-domains node\n", np);
 331                        continue;
 332                }
 333
 334                if (!scanned) {
 335                        /* Find PM domains containing special blocks */
 336                        get_special_pds();
 337                        scanned = true;
 338                }
 339
 340                ret = rmobile_add_pm_domains(base, pmd, NULL);
 341                of_node_put(pmd);
 342                if (ret) {
 343                        of_node_put(np);
 344                        break;
 345                }
 346        }
 347
 348        put_special_pds();
 349
 350        return ret;
 351}
 352
 353core_initcall(rmobile_init_pm_domains);
 354