linux/drivers/soc/renesas/r8a779a0-sysc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Renesas R-Car V3U System Controller
   4 *
   5 * Copyright (C) 2020 Renesas Electronics Corp.
   6 */
   7
   8#include <linux/bits.h>
   9#include <linux/clk/renesas.h>
  10#include <linux/delay.h>
  11#include <linux/err.h>
  12#include <linux/io.h>
  13#include <linux/iopoll.h>
  14#include <linux/kernel.h>
  15#include <linux/mm.h>
  16#include <linux/of_address.h>
  17#include <linux/pm_domain.h>
  18#include <linux/slab.h>
  19#include <linux/spinlock.h>
  20#include <linux/types.h>
  21
  22#include <dt-bindings/power/r8a779a0-sysc.h>
  23
  24/*
  25 * Power Domain flags
  26 */
  27#define PD_CPU          BIT(0)  /* Area contains main CPU core */
  28#define PD_SCU          BIT(1)  /* Area contains SCU and L2 cache */
  29#define PD_NO_CR        BIT(2)  /* Area lacks PWR{ON,OFF}CR registers */
  30
  31#define PD_CPU_NOCR     PD_CPU | PD_NO_CR /* CPU area lacks CR */
  32#define PD_ALWAYS_ON    PD_NO_CR          /* Always-on area */
  33
  34/*
  35 * Description of a Power Area
  36 */
  37struct r8a779a0_sysc_area {
  38        const char *name;
  39        u8 pdr;                 /* PDRn */
  40        int parent;             /* -1 if none */
  41        unsigned int flags;     /* See PD_* */
  42};
  43
  44/*
  45 * SoC-specific Power Area Description
  46 */
  47struct r8a779a0_sysc_info {
  48        const struct r8a779a0_sysc_area *areas;
  49        unsigned int num_areas;
  50};
  51
  52static struct r8a779a0_sysc_area r8a779a0_areas[] __initdata = {
  53        { "always-on",  R8A779A0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
  54        { "a3e0",       R8A779A0_PD_A3E0, R8A779A0_PD_ALWAYS_ON, PD_SCU },
  55        { "a3e1",       R8A779A0_PD_A3E1, R8A779A0_PD_ALWAYS_ON, PD_SCU },
  56        { "a2e0d0",     R8A779A0_PD_A2E0D0, R8A779A0_PD_A3E0, PD_SCU },
  57        { "a2e0d1",     R8A779A0_PD_A2E0D1, R8A779A0_PD_A3E0, PD_SCU },
  58        { "a2e1d0",     R8A779A0_PD_A2E1D0, R8A779A0_PD_A3E1, PD_SCU },
  59        { "a2e1d1",     R8A779A0_PD_A2E1D1, R8A779A0_PD_A3E1, PD_SCU },
  60        { "a1e0d0c0",   R8A779A0_PD_A1E0D0C0, R8A779A0_PD_A2E0D0, PD_CPU_NOCR },
  61        { "a1e0d0c1",   R8A779A0_PD_A1E0D0C1, R8A779A0_PD_A2E0D0, PD_CPU_NOCR },
  62        { "a1e0d1c0",   R8A779A0_PD_A1E0D1C0, R8A779A0_PD_A2E0D1, PD_CPU_NOCR },
  63        { "a1e0d1c1",   R8A779A0_PD_A1E0D1C1, R8A779A0_PD_A2E0D1, PD_CPU_NOCR },
  64        { "a1e1d0c0",   R8A779A0_PD_A1E1D0C0, R8A779A0_PD_A2E1D0, PD_CPU_NOCR },
  65        { "a1e1d0c1",   R8A779A0_PD_A1E1D0C1, R8A779A0_PD_A2E1D0, PD_CPU_NOCR },
  66        { "a1e1d1c0",   R8A779A0_PD_A1E1D1C0, R8A779A0_PD_A2E1D1, PD_CPU_NOCR },
  67        { "a1e1d1c1",   R8A779A0_PD_A1E1D1C1, R8A779A0_PD_A2E1D1, PD_CPU_NOCR },
  68        { "3dg-a",      R8A779A0_PD_3DG_A, R8A779A0_PD_ALWAYS_ON },
  69        { "3dg-b",      R8A779A0_PD_3DG_B, R8A779A0_PD_3DG_A },
  70        { "a3vip0",     R8A779A0_PD_A3VIP0, R8A779A0_PD_ALWAYS_ON },
  71        { "a3vip1",     R8A779A0_PD_A3VIP1, R8A779A0_PD_ALWAYS_ON },
  72        { "a3vip3",     R8A779A0_PD_A3VIP3, R8A779A0_PD_ALWAYS_ON },
  73        { "a3vip2",     R8A779A0_PD_A3VIP2, R8A779A0_PD_ALWAYS_ON },
  74        { "a3isp01",    R8A779A0_PD_A3ISP01, R8A779A0_PD_ALWAYS_ON },
  75        { "a3isp23",    R8A779A0_PD_A3ISP23, R8A779A0_PD_ALWAYS_ON },
  76        { "a3ir",       R8A779A0_PD_A3IR, R8A779A0_PD_ALWAYS_ON },
  77        { "a2cn0",      R8A779A0_PD_A2CN0, R8A779A0_PD_A3IR },
  78        { "a2imp01",    R8A779A0_PD_A2IMP01, R8A779A0_PD_A3IR },
  79        { "a2dp0",      R8A779A0_PD_A2DP0, R8A779A0_PD_A3IR },
  80        { "a2cv0",      R8A779A0_PD_A2CV0, R8A779A0_PD_A3IR },
  81        { "a2cv1",      R8A779A0_PD_A2CV1, R8A779A0_PD_A3IR },
  82        { "a2cv4",      R8A779A0_PD_A2CV4, R8A779A0_PD_A3IR },
  83        { "a2cv6",      R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR },
  84        { "a2cn2",      R8A779A0_PD_A2CN2, R8A779A0_PD_A3IR },
  85        { "a2imp23",    R8A779A0_PD_A2IMP23, R8A779A0_PD_A3IR },
  86        { "a2dp1",      R8A779A0_PD_A2DP0, R8A779A0_PD_A3IR },
  87        { "a2cv2",      R8A779A0_PD_A2CV0, R8A779A0_PD_A3IR },
  88        { "a2cv3",      R8A779A0_PD_A2CV1, R8A779A0_PD_A3IR },
  89        { "a2cv5",      R8A779A0_PD_A2CV4, R8A779A0_PD_A3IR },
  90        { "a2cv7",      R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR },
  91        { "a2cn1",      R8A779A0_PD_A2CN1, R8A779A0_PD_A3IR },
  92        { "a1cnn0",     R8A779A0_PD_A1CNN0, R8A779A0_PD_A2CN0 },
  93        { "a1cnn2",     R8A779A0_PD_A1CNN2, R8A779A0_PD_A2CN2 },
  94        { "a1dsp0",     R8A779A0_PD_A1DSP0, R8A779A0_PD_A2CN2 },
  95        { "a1cnn1",     R8A779A0_PD_A1CNN1, R8A779A0_PD_A2CN1 },
  96        { "a1dsp1",     R8A779A0_PD_A1DSP1, R8A779A0_PD_A2CN1 },
  97};
  98
  99static const struct r8a779a0_sysc_info r8a779a0_sysc_info __initconst = {
 100        .areas = r8a779a0_areas,
 101        .num_areas = ARRAY_SIZE(r8a779a0_areas),
 102};
 103
 104/* SYSC Common */
 105#define SYSCSR          0x000   /* SYSC Status Register */
 106#define SYSCPONSR(x)    (0x800 + ((x) * 0x4)) /* Power-ON Status Register 0 */
 107#define SYSCPOFFSR(x)   (0x808 + ((x) * 0x4)) /* Power-OFF Status Register */
 108#define SYSCISCR(x)     (0x810 + ((x) * 0x4)) /* Interrupt Status/Clear Register */
 109#define SYSCIER(x)      (0x820 + ((x) * 0x4)) /* Interrupt Enable Register */
 110#define SYSCIMR(x)      (0x830 + ((x) * 0x4)) /* Interrupt Mask Register */
 111
 112/* Power Domain Registers */
 113#define PDRSR(n)        (0x1000 + ((n) * 0x40))
 114#define PDRONCR(n)      (0x1004 + ((n) * 0x40))
 115#define PDROFFCR(n)     (0x1008 + ((n) * 0x40))
 116#define PDRESR(n)       (0x100C + ((n) * 0x40))
 117
 118/* PWRON/PWROFF */
 119#define PWRON_PWROFF            BIT(0)  /* Power-ON/OFF request */
 120
 121/* PDRESR */
 122#define PDRESR_ERR              BIT(0)
 123
 124/* PDRSR */
 125#define PDRSR_OFF               BIT(0)  /* Power-OFF state */
 126#define PDRSR_ON                BIT(4)  /* Power-ON state */
 127#define PDRSR_OFF_STATE         BIT(8)  /* Processing Power-OFF sequence */
 128#define PDRSR_ON_STATE          BIT(12) /* Processing Power-ON sequence */
 129
 130#define SYSCSR_BUSY             GENMASK(1, 0)   /* All bit sets is not busy */
 131
 132#define SYSCSR_TIMEOUT          10000
 133#define SYSCSR_DELAY_US         10
 134
 135#define PDRESR_RETRIES          1000
 136#define PDRESR_DELAY_US         10
 137
 138#define SYSCISR_TIMEOUT         10000
 139#define SYSCISR_DELAY_US        10
 140
 141#define NUM_DOMAINS_EACH_REG    BITS_PER_TYPE(u32)
 142
 143static void __iomem *r8a779a0_sysc_base;
 144static DEFINE_SPINLOCK(r8a779a0_sysc_lock); /* SMP CPUs + I/O devices */
 145
 146static int r8a779a0_sysc_pwr_on_off(u8 pdr, bool on)
 147{
 148        unsigned int reg_offs;
 149        u32 val;
 150        int ret;
 151
 152        if (on)
 153                reg_offs = PDRONCR(pdr);
 154        else
 155                reg_offs = PDROFFCR(pdr);
 156
 157        /* Wait until SYSC is ready to accept a power request */
 158        ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCSR, val,
 159                                        (val & SYSCSR_BUSY) == SYSCSR_BUSY,
 160                                        SYSCSR_DELAY_US, SYSCSR_TIMEOUT);
 161        if (ret < 0)
 162                return -EAGAIN;
 163
 164        /* Submit power shutoff or power resume request */
 165        iowrite32(PWRON_PWROFF, r8a779a0_sysc_base + reg_offs);
 166
 167        return 0;
 168}
 169
 170static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask)
 171{
 172        u32 val;
 173        int ret;
 174
 175        iowrite32(isr_mask, r8a779a0_sysc_base + SYSCISCR(reg_idx));
 176
 177        ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCISCR(reg_idx),
 178                                        val, !(val & isr_mask),
 179                                        SYSCISR_DELAY_US, SYSCISR_TIMEOUT);
 180        if (ret < 0) {
 181                pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__);
 182                return -EIO;
 183        }
 184
 185        return 0;
 186}
 187
 188static int r8a779a0_sysc_power(u8 pdr, bool on)
 189{
 190        unsigned int isr_mask;
 191        unsigned int reg_idx, bit_idx;
 192        unsigned int status;
 193        unsigned long flags;
 194        int ret = 0;
 195        u32 val;
 196        int k;
 197
 198        spin_lock_irqsave(&r8a779a0_sysc_lock, flags);
 199
 200        reg_idx = pdr / NUM_DOMAINS_EACH_REG;
 201        bit_idx = pdr % NUM_DOMAINS_EACH_REG;
 202
 203        isr_mask = BIT(bit_idx);
 204
 205        /*
 206         * The interrupt source needs to be enabled, but masked, to prevent the
 207         * CPU from receiving it.
 208         */
 209        iowrite32(ioread32(r8a779a0_sysc_base + SYSCIER(reg_idx)) | isr_mask,
 210                  r8a779a0_sysc_base + SYSCIER(reg_idx));
 211        iowrite32(ioread32(r8a779a0_sysc_base + SYSCIMR(reg_idx)) | isr_mask,
 212                  r8a779a0_sysc_base + SYSCIMR(reg_idx));
 213
 214        ret = clear_irq_flags(reg_idx, isr_mask);
 215        if (ret)
 216                goto out;
 217
 218        /* Submit power shutoff or resume request until it was accepted */
 219        for (k = 0; k < PDRESR_RETRIES; k++) {
 220                ret = r8a779a0_sysc_pwr_on_off(pdr, on);
 221                if (ret)
 222                        goto out;
 223
 224                status = ioread32(r8a779a0_sysc_base + PDRESR(pdr));
 225                if (!(status & PDRESR_ERR))
 226                        break;
 227
 228                udelay(PDRESR_DELAY_US);
 229        }
 230
 231        if (k == PDRESR_RETRIES) {
 232                ret = -EIO;
 233                goto out;
 234        }
 235
 236        /* Wait until the power shutoff or resume request has completed * */
 237        ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCISCR(reg_idx),
 238                                        val, (val & isr_mask),
 239                                        SYSCISR_DELAY_US, SYSCISR_TIMEOUT);
 240        if (ret < 0) {
 241                ret = -EIO;
 242                goto out;
 243        }
 244
 245        /* Clear interrupt flags */
 246        ret = clear_irq_flags(reg_idx, isr_mask);
 247        if (ret)
 248                goto out;
 249
 250 out:
 251        spin_unlock_irqrestore(&r8a779a0_sysc_lock, flags);
 252
 253        pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off",
 254                 pdr, ioread32(r8a779a0_sysc_base + SYSCISCR(reg_idx)), ret);
 255        return ret;
 256}
 257
 258static bool r8a779a0_sysc_power_is_off(u8 pdr)
 259{
 260        unsigned int st;
 261
 262        st = ioread32(r8a779a0_sysc_base + PDRSR(pdr));
 263
 264        if (st & PDRSR_OFF)
 265                return true;
 266
 267        return false;
 268}
 269
 270struct r8a779a0_sysc_pd {
 271        struct generic_pm_domain genpd;
 272        u8 pdr;
 273        unsigned int flags;
 274        char name[];
 275};
 276
 277static inline struct r8a779a0_sysc_pd *to_r8a779a0_pd(struct generic_pm_domain *d)
 278{
 279        return container_of(d, struct r8a779a0_sysc_pd, genpd);
 280}
 281
 282static int r8a779a0_sysc_pd_power_off(struct generic_pm_domain *genpd)
 283{
 284        struct r8a779a0_sysc_pd *pd = to_r8a779a0_pd(genpd);
 285
 286        pr_debug("%s: %s\n", __func__, genpd->name);
 287        return r8a779a0_sysc_power(pd->pdr, false);
 288}
 289
 290static int r8a779a0_sysc_pd_power_on(struct generic_pm_domain *genpd)
 291{
 292        struct r8a779a0_sysc_pd *pd = to_r8a779a0_pd(genpd);
 293
 294        pr_debug("%s: %s\n", __func__, genpd->name);
 295        return r8a779a0_sysc_power(pd->pdr, true);
 296}
 297
 298static int __init r8a779a0_sysc_pd_setup(struct r8a779a0_sysc_pd *pd)
 299{
 300        struct generic_pm_domain *genpd = &pd->genpd;
 301        const char *name = pd->genpd.name;
 302        int error;
 303
 304        if (pd->flags & PD_CPU) {
 305                /*
 306                 * This domain contains a CPU core and therefore it should
 307                 * only be turned off if the CPU is not in use.
 308                 */
 309                pr_debug("PM domain %s contains %s\n", name, "CPU");
 310                genpd->flags |= GENPD_FLAG_ALWAYS_ON;
 311        } else if (pd->flags & PD_SCU) {
 312                /*
 313                 * This domain contains an SCU and cache-controller, and
 314                 * therefore it should only be turned off if the CPU cores are
 315                 * not in use.
 316                 */
 317                pr_debug("PM domain %s contains %s\n", name, "SCU");
 318                genpd->flags |= GENPD_FLAG_ALWAYS_ON;
 319        } else if (pd->flags & PD_NO_CR) {
 320                /*
 321                 * This domain cannot be turned off.
 322                 */
 323                genpd->flags |= GENPD_FLAG_ALWAYS_ON;
 324        }
 325
 326        if (!(pd->flags & (PD_CPU | PD_SCU))) {
 327                /* Enable Clock Domain for I/O devices */
 328                genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
 329                genpd->attach_dev = cpg_mssr_attach_dev;
 330                genpd->detach_dev = cpg_mssr_detach_dev;
 331        }
 332
 333        genpd->power_off = r8a779a0_sysc_pd_power_off;
 334        genpd->power_on = r8a779a0_sysc_pd_power_on;
 335
 336        if (pd->flags & (PD_CPU | PD_NO_CR)) {
 337                /* Skip CPUs (handled by SMP code) and areas without control */
 338                pr_debug("%s: Not touching %s\n", __func__, genpd->name);
 339                goto finalize;
 340        }
 341
 342        if (!r8a779a0_sysc_power_is_off(pd->pdr)) {
 343                pr_debug("%s: %s is already powered\n", __func__, genpd->name);
 344                goto finalize;
 345        }
 346
 347        r8a779a0_sysc_power(pd->pdr, true);
 348
 349finalize:
 350        error = pm_genpd_init(genpd, &simple_qos_governor, false);
 351        if (error)
 352                pr_err("Failed to init PM domain %s: %d\n", name, error);
 353
 354        return error;
 355}
 356
 357static const struct of_device_id r8a779a0_sysc_matches[] __initconst = {
 358        { .compatible = "renesas,r8a779a0-sysc", .data = &r8a779a0_sysc_info },
 359        { /* sentinel */ }
 360};
 361
 362struct r8a779a0_pm_domains {
 363        struct genpd_onecell_data onecell_data;
 364        struct generic_pm_domain *domains[R8A779A0_PD_ALWAYS_ON + 1];
 365};
 366
 367static struct genpd_onecell_data *r8a779a0_sysc_onecell_data;
 368
 369static int __init r8a779a0_sysc_pd_init(void)
 370{
 371        const struct r8a779a0_sysc_info *info;
 372        const struct of_device_id *match;
 373        struct r8a779a0_pm_domains *domains;
 374        struct device_node *np;
 375        void __iomem *base;
 376        unsigned int i;
 377        int error;
 378
 379        np = of_find_matching_node_and_match(NULL, r8a779a0_sysc_matches, &match);
 380        if (!np)
 381                return -ENODEV;
 382
 383        info = match->data;
 384
 385        base = of_iomap(np, 0);
 386        if (!base) {
 387                pr_warn("%pOF: Cannot map regs\n", np);
 388                error = -ENOMEM;
 389                goto out_put;
 390        }
 391
 392        r8a779a0_sysc_base = base;
 393
 394        domains = kzalloc(sizeof(*domains), GFP_KERNEL);
 395        if (!domains) {
 396                error = -ENOMEM;
 397                goto out_put;
 398        }
 399
 400        domains->onecell_data.domains = domains->domains;
 401        domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains);
 402        r8a779a0_sysc_onecell_data = &domains->onecell_data;
 403
 404        for (i = 0; i < info->num_areas; i++) {
 405                const struct r8a779a0_sysc_area *area = &info->areas[i];
 406                struct r8a779a0_sysc_pd *pd;
 407                size_t n;
 408
 409                if (!area->name) {
 410                        /* Skip NULLified area */
 411                        continue;
 412                }
 413
 414                n = strlen(area->name) + 1;
 415                pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL);
 416                if (!pd) {
 417                        error = -ENOMEM;
 418                        goto out_put;
 419                }
 420
 421                memcpy(pd->name, area->name, n);
 422                pd->genpd.name = pd->name;
 423                pd->pdr = area->pdr;
 424                pd->flags = area->flags;
 425
 426                error = r8a779a0_sysc_pd_setup(pd);
 427                if (error)
 428                        goto out_put;
 429
 430                domains->domains[area->pdr] = &pd->genpd;
 431
 432                if (area->parent < 0)
 433                        continue;
 434
 435                error = pm_genpd_add_subdomain(domains->domains[area->parent],
 436                                               &pd->genpd);
 437                if (error) {
 438                        pr_warn("Failed to add PM subdomain %s to parent %u\n",
 439                                area->name, area->parent);
 440                        goto out_put;
 441                }
 442        }
 443
 444        error = of_genpd_add_provider_onecell(np, &domains->onecell_data);
 445
 446out_put:
 447        of_node_put(np);
 448        return error;
 449}
 450early_initcall(r8a779a0_sysc_pd_init);
 451