162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * R-Car SYSC Power management support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Magnus Damm 662306a36Sopenharmony_ci * Copyright (C) 2015-2017 Glider bvba 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk/renesas.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/pm_domain.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/spinlock.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/iopoll.h> 1962306a36Sopenharmony_ci#include <linux/soc/renesas/rcar-sysc.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "rcar-sysc.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* SYSC Common */ 2462306a36Sopenharmony_ci#define SYSCSR 0x00 /* SYSC Status Register */ 2562306a36Sopenharmony_ci#define SYSCISR 0x04 /* Interrupt Status Register */ 2662306a36Sopenharmony_ci#define SYSCISCR 0x08 /* Interrupt Status Clear Register */ 2762306a36Sopenharmony_ci#define SYSCIER 0x0c /* Interrupt Enable Register */ 2862306a36Sopenharmony_ci#define SYSCIMR 0x10 /* Interrupt Mask Register */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* SYSC Status Register */ 3162306a36Sopenharmony_ci#define SYSCSR_PONENB 1 /* Ready for power resume requests */ 3262306a36Sopenharmony_ci#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Power Control Register Offsets inside the register block for each domain 3662306a36Sopenharmony_ci * Note: The "CR" registers for ARM cores exist on H1 only 3762306a36Sopenharmony_ci * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 3862306a36Sopenharmony_ci * Use PSCI on R-Car Gen3 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci#define PWRSR_OFFS 0x00 /* Power Status Register */ 4162306a36Sopenharmony_ci#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ 4262306a36Sopenharmony_ci#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */ 4362306a36Sopenharmony_ci#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ 4462306a36Sopenharmony_ci#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */ 4562306a36Sopenharmony_ci#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define SYSCSR_TIMEOUT 100 4962306a36Sopenharmony_ci#define SYSCSR_DELAY_US 1 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define PWRER_RETRIES 100 5262306a36Sopenharmony_ci#define PWRER_DELAY_US 1 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define SYSCISR_TIMEOUT 1000 5562306a36Sopenharmony_ci#define SYSCISR_DELAY_US 1 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct rcar_sysc_ch { 6062306a36Sopenharmony_ci u16 chan_offs; 6162306a36Sopenharmony_ci u8 chan_bit; 6262306a36Sopenharmony_ci u8 isr_bit; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void __iomem *rcar_sysc_base; 6662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ 6762306a36Sopenharmony_cistatic u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci unsigned int sr_bit, reg_offs; 7262306a36Sopenharmony_ci u32 val; 7362306a36Sopenharmony_ci int ret; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (on) { 7662306a36Sopenharmony_ci sr_bit = SYSCSR_PONENB; 7762306a36Sopenharmony_ci reg_offs = PWRONCR_OFFS; 7862306a36Sopenharmony_ci } else { 7962306a36Sopenharmony_ci sr_bit = SYSCSR_POFFENB; 8062306a36Sopenharmony_ci reg_offs = PWROFFCR_OFFS; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Wait until SYSC is ready to accept a power request */ 8462306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCSR, val, 8562306a36Sopenharmony_ci val & BIT(sr_bit), SYSCSR_DELAY_US, 8662306a36Sopenharmony_ci SYSCSR_TIMEOUT); 8762306a36Sopenharmony_ci if (ret) 8862306a36Sopenharmony_ci return -EAGAIN; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Submit power shutoff or power resume request */ 9162306a36Sopenharmony_ci iowrite32(BIT(sysc_ch->chan_bit), 9262306a36Sopenharmony_ci rcar_sysc_base + sysc_ch->chan_offs + reg_offs); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned int isr_mask = BIT(sysc_ch->isr_bit); 10062306a36Sopenharmony_ci unsigned int chan_mask = BIT(sysc_ch->chan_bit); 10162306a36Sopenharmony_ci unsigned int status, k; 10262306a36Sopenharmony_ci unsigned long flags; 10362306a36Sopenharmony_ci int ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci spin_lock_irqsave(&rcar_sysc_lock, flags); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * Mask external power requests for CPU or 3DG domains 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (rcar_sysc_extmask_val) { 11162306a36Sopenharmony_ci iowrite32(rcar_sysc_extmask_val, 11262306a36Sopenharmony_ci rcar_sysc_base + rcar_sysc_extmask_offs); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * The interrupt source needs to be enabled, but masked, to prevent the 11762306a36Sopenharmony_ci * CPU from receiving it. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci iowrite32(ioread32(rcar_sysc_base + SYSCIMR) | isr_mask, 12062306a36Sopenharmony_ci rcar_sysc_base + SYSCIMR); 12162306a36Sopenharmony_ci iowrite32(ioread32(rcar_sysc_base + SYSCIER) | isr_mask, 12262306a36Sopenharmony_ci rcar_sysc_base + SYSCIER); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Submit power shutoff or resume request until it was accepted */ 12762306a36Sopenharmony_ci for (k = 0; k < PWRER_RETRIES; k++) { 12862306a36Sopenharmony_ci ret = rcar_sysc_pwr_on_off(sysc_ch, on); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci goto out; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci status = ioread32(rcar_sysc_base + 13362306a36Sopenharmony_ci sysc_ch->chan_offs + PWRER_OFFS); 13462306a36Sopenharmony_ci if (!(status & chan_mask)) 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci udelay(PWRER_DELAY_US); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (k == PWRER_RETRIES) { 14162306a36Sopenharmony_ci ret = -EIO; 14262306a36Sopenharmony_ci goto out; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Wait until the power shutoff or resume request has completed * */ 14662306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCISR, status, 14762306a36Sopenharmony_ci status & isr_mask, SYSCISR_DELAY_US, 14862306a36Sopenharmony_ci SYSCISR_TIMEOUT); 14962306a36Sopenharmony_ci if (ret) 15062306a36Sopenharmony_ci ret = -EIO; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci out: 15562306a36Sopenharmony_ci if (rcar_sysc_extmask_val) 15662306a36Sopenharmony_ci iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci spin_unlock_irqrestore(&rcar_sysc_lock, flags); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", 16162306a36Sopenharmony_ci sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci unsigned int st; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); 17062306a36Sopenharmony_ci if (st & BIT(sysc_ch->chan_bit)) 17162306a36Sopenharmony_ci return true; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return false; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistruct rcar_sysc_pd { 17762306a36Sopenharmony_ci struct generic_pm_domain genpd; 17862306a36Sopenharmony_ci struct rcar_sysc_ch ch; 17962306a36Sopenharmony_ci unsigned int flags; 18062306a36Sopenharmony_ci char name[]; 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci return container_of(d, struct rcar_sysc_pd, genpd); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct rcar_sysc_pd *pd = to_rcar_pd(genpd); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci pr_debug("%s: %s\n", __func__, genpd->name); 19362306a36Sopenharmony_ci return rcar_sysc_power(&pd->ch, false); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct rcar_sysc_pd *pd = to_rcar_pd(genpd); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci pr_debug("%s: %s\n", __func__, genpd->name); 20162306a36Sopenharmony_ci return rcar_sysc_power(&pd->ch, true); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic bool has_cpg_mstp; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct generic_pm_domain *genpd = &pd->genpd; 20962306a36Sopenharmony_ci const char *name = pd->genpd.name; 21062306a36Sopenharmony_ci int error; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (pd->flags & PD_CPU) { 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * This domain contains a CPU core and therefore it should 21562306a36Sopenharmony_ci * only be turned off if the CPU is not in use. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci pr_debug("PM domain %s contains %s\n", name, "CPU"); 21862306a36Sopenharmony_ci genpd->flags |= GENPD_FLAG_ALWAYS_ON; 21962306a36Sopenharmony_ci } else if (pd->flags & PD_SCU) { 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * This domain contains an SCU and cache-controller, and 22262306a36Sopenharmony_ci * therefore it should only be turned off if the CPU cores are 22362306a36Sopenharmony_ci * not in use. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci pr_debug("PM domain %s contains %s\n", name, "SCU"); 22662306a36Sopenharmony_ci genpd->flags |= GENPD_FLAG_ALWAYS_ON; 22762306a36Sopenharmony_ci } else if (pd->flags & PD_NO_CR) { 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * This domain cannot be turned off. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci genpd->flags |= GENPD_FLAG_ALWAYS_ON; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (!(pd->flags & (PD_CPU | PD_SCU))) { 23562306a36Sopenharmony_ci /* Enable Clock Domain for I/O devices */ 23662306a36Sopenharmony_ci genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; 23762306a36Sopenharmony_ci if (has_cpg_mstp) { 23862306a36Sopenharmony_ci genpd->attach_dev = cpg_mstp_attach_dev; 23962306a36Sopenharmony_ci genpd->detach_dev = cpg_mstp_detach_dev; 24062306a36Sopenharmony_ci } else { 24162306a36Sopenharmony_ci genpd->attach_dev = cpg_mssr_attach_dev; 24262306a36Sopenharmony_ci genpd->detach_dev = cpg_mssr_detach_dev; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci genpd->power_off = rcar_sysc_pd_power_off; 24762306a36Sopenharmony_ci genpd->power_on = rcar_sysc_pd_power_on; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (pd->flags & (PD_CPU | PD_NO_CR)) { 25062306a36Sopenharmony_ci /* Skip CPUs (handled by SMP code) and areas without control */ 25162306a36Sopenharmony_ci pr_debug("%s: Not touching %s\n", __func__, genpd->name); 25262306a36Sopenharmony_ci goto finalize; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!rcar_sysc_power_is_off(&pd->ch)) { 25662306a36Sopenharmony_ci pr_debug("%s: %s is already powered\n", __func__, genpd->name); 25762306a36Sopenharmony_ci goto finalize; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci rcar_sysc_power(&pd->ch, true); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cifinalize: 26362306a36Sopenharmony_ci error = pm_genpd_init(genpd, &simple_qos_governor, false); 26462306a36Sopenharmony_ci if (error) 26562306a36Sopenharmony_ci pr_err("Failed to init PM domain %s: %d\n", name, error); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return error; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic const struct of_device_id rcar_sysc_matches[] __initconst = { 27162306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7742 27262306a36Sopenharmony_ci { .compatible = "renesas,r8a7742-sysc", .data = &r8a7742_sysc_info }, 27362306a36Sopenharmony_ci#endif 27462306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7743 27562306a36Sopenharmony_ci { .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info }, 27662306a36Sopenharmony_ci /* RZ/G1N is identical to RZ/G2M w.r.t. power domains. */ 27762306a36Sopenharmony_ci { .compatible = "renesas,r8a7744-sysc", .data = &r8a7743_sysc_info }, 27862306a36Sopenharmony_ci#endif 27962306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7745 28062306a36Sopenharmony_ci { .compatible = "renesas,r8a7745-sysc", .data = &r8a7745_sysc_info }, 28162306a36Sopenharmony_ci#endif 28262306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A77470 28362306a36Sopenharmony_ci { .compatible = "renesas,r8a77470-sysc", .data = &r8a77470_sysc_info }, 28462306a36Sopenharmony_ci#endif 28562306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A774A1 28662306a36Sopenharmony_ci { .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info }, 28762306a36Sopenharmony_ci#endif 28862306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A774B1 28962306a36Sopenharmony_ci { .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info }, 29062306a36Sopenharmony_ci#endif 29162306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A774C0 29262306a36Sopenharmony_ci { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, 29362306a36Sopenharmony_ci#endif 29462306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A774E1 29562306a36Sopenharmony_ci { .compatible = "renesas,r8a774e1-sysc", .data = &r8a774e1_sysc_info }, 29662306a36Sopenharmony_ci#endif 29762306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7779 29862306a36Sopenharmony_ci { .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info }, 29962306a36Sopenharmony_ci#endif 30062306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7790 30162306a36Sopenharmony_ci { .compatible = "renesas,r8a7790-sysc", .data = &r8a7790_sysc_info }, 30262306a36Sopenharmony_ci#endif 30362306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7791 30462306a36Sopenharmony_ci { .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info }, 30562306a36Sopenharmony_ci /* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */ 30662306a36Sopenharmony_ci { .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info }, 30762306a36Sopenharmony_ci#endif 30862306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7792 30962306a36Sopenharmony_ci { .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info }, 31062306a36Sopenharmony_ci#endif 31162306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7794 31262306a36Sopenharmony_ci { .compatible = "renesas,r8a7794-sysc", .data = &r8a7794_sysc_info }, 31362306a36Sopenharmony_ci#endif 31462306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A7795 31562306a36Sopenharmony_ci { .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info }, 31662306a36Sopenharmony_ci#endif 31762306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A77960 31862306a36Sopenharmony_ci { .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info }, 31962306a36Sopenharmony_ci#endif 32062306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A77961 32162306a36Sopenharmony_ci { .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info }, 32262306a36Sopenharmony_ci#endif 32362306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A77965 32462306a36Sopenharmony_ci { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, 32562306a36Sopenharmony_ci#endif 32662306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A77970 32762306a36Sopenharmony_ci { .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info }, 32862306a36Sopenharmony_ci#endif 32962306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A77980 33062306a36Sopenharmony_ci { .compatible = "renesas,r8a77980-sysc", .data = &r8a77980_sysc_info }, 33162306a36Sopenharmony_ci#endif 33262306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A77990 33362306a36Sopenharmony_ci { .compatible = "renesas,r8a77990-sysc", .data = &r8a77990_sysc_info }, 33462306a36Sopenharmony_ci#endif 33562306a36Sopenharmony_ci#ifdef CONFIG_SYSC_R8A77995 33662306a36Sopenharmony_ci { .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info }, 33762306a36Sopenharmony_ci#endif 33862306a36Sopenharmony_ci { /* sentinel */ } 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistruct rcar_pm_domains { 34262306a36Sopenharmony_ci struct genpd_onecell_data onecell_data; 34362306a36Sopenharmony_ci struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1]; 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic struct genpd_onecell_data *rcar_sysc_onecell_data; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic int __init rcar_sysc_pd_init(void) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci const struct rcar_sysc_info *info; 35162306a36Sopenharmony_ci const struct of_device_id *match; 35262306a36Sopenharmony_ci struct rcar_pm_domains *domains; 35362306a36Sopenharmony_ci struct device_node *np; 35462306a36Sopenharmony_ci void __iomem *base; 35562306a36Sopenharmony_ci unsigned int i; 35662306a36Sopenharmony_ci int error; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match); 35962306a36Sopenharmony_ci if (!np) 36062306a36Sopenharmony_ci return -ENODEV; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci info = match->data; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (info->init) { 36562306a36Sopenharmony_ci error = info->init(); 36662306a36Sopenharmony_ci if (error) 36762306a36Sopenharmony_ci goto out_put; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci has_cpg_mstp = of_find_compatible_node(NULL, NULL, 37162306a36Sopenharmony_ci "renesas,cpg-mstp-clocks"); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci base = of_iomap(np, 0); 37462306a36Sopenharmony_ci if (!base) { 37562306a36Sopenharmony_ci pr_warn("%pOF: Cannot map regs\n", np); 37662306a36Sopenharmony_ci error = -ENOMEM; 37762306a36Sopenharmony_ci goto out_put; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci rcar_sysc_base = base; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Optional External Request Mask Register */ 38362306a36Sopenharmony_ci rcar_sysc_extmask_offs = info->extmask_offs; 38462306a36Sopenharmony_ci rcar_sysc_extmask_val = info->extmask_val; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci domains = kzalloc(sizeof(*domains), GFP_KERNEL); 38762306a36Sopenharmony_ci if (!domains) { 38862306a36Sopenharmony_ci error = -ENOMEM; 38962306a36Sopenharmony_ci goto out_put; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci domains->onecell_data.domains = domains->domains; 39362306a36Sopenharmony_ci domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); 39462306a36Sopenharmony_ci rcar_sysc_onecell_data = &domains->onecell_data; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci for (i = 0; i < info->num_areas; i++) { 39762306a36Sopenharmony_ci const struct rcar_sysc_area *area = &info->areas[i]; 39862306a36Sopenharmony_ci struct rcar_sysc_pd *pd; 39962306a36Sopenharmony_ci size_t n; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (!area->name) { 40262306a36Sopenharmony_ci /* Skip NULLified area */ 40362306a36Sopenharmony_ci continue; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci n = strlen(area->name) + 1; 40762306a36Sopenharmony_ci pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL); 40862306a36Sopenharmony_ci if (!pd) { 40962306a36Sopenharmony_ci error = -ENOMEM; 41062306a36Sopenharmony_ci goto out_put; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci memcpy(pd->name, area->name, n); 41462306a36Sopenharmony_ci pd->genpd.name = pd->name; 41562306a36Sopenharmony_ci pd->ch.chan_offs = area->chan_offs; 41662306a36Sopenharmony_ci pd->ch.chan_bit = area->chan_bit; 41762306a36Sopenharmony_ci pd->ch.isr_bit = area->isr_bit; 41862306a36Sopenharmony_ci pd->flags = area->flags; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci error = rcar_sysc_pd_setup(pd); 42162306a36Sopenharmony_ci if (error) 42262306a36Sopenharmony_ci goto out_put; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci domains->domains[area->isr_bit] = &pd->genpd; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (area->parent < 0) 42762306a36Sopenharmony_ci continue; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci error = pm_genpd_add_subdomain(domains->domains[area->parent], 43062306a36Sopenharmony_ci &pd->genpd); 43162306a36Sopenharmony_ci if (error) { 43262306a36Sopenharmony_ci pr_warn("Failed to add PM subdomain %s to parent %u\n", 43362306a36Sopenharmony_ci area->name, area->parent); 43462306a36Sopenharmony_ci goto out_put; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci error = of_genpd_add_provider_onecell(np, &domains->onecell_data); 43962306a36Sopenharmony_ci if (!error) 44062306a36Sopenharmony_ci fwnode_dev_initialized(of_fwnode_handle(np), true); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ciout_put: 44362306a36Sopenharmony_ci of_node_put(np); 44462306a36Sopenharmony_ci return error; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ciearly_initcall(rcar_sysc_pd_init); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_civoid __init rcar_sysc_nullify(struct rcar_sysc_area *areas, 44962306a36Sopenharmony_ci unsigned int num_areas, u8 id) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci unsigned int i; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci for (i = 0; i < num_areas; i++) 45462306a36Sopenharmony_ci if (areas[i].isr_bit == id) { 45562306a36Sopenharmony_ci areas[i].name = NULL; 45662306a36Sopenharmony_ci return; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci#ifdef CONFIG_ARCH_R8A7779 46162306a36Sopenharmony_cistatic int rcar_sysc_power_cpu(unsigned int idx, bool on) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct generic_pm_domain *genpd; 46462306a36Sopenharmony_ci struct rcar_sysc_pd *pd; 46562306a36Sopenharmony_ci unsigned int i; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (!rcar_sysc_onecell_data) 46862306a36Sopenharmony_ci return -ENODEV; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci for (i = 0; i < rcar_sysc_onecell_data->num_domains; i++) { 47162306a36Sopenharmony_ci genpd = rcar_sysc_onecell_data->domains[i]; 47262306a36Sopenharmony_ci if (!genpd) 47362306a36Sopenharmony_ci continue; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci pd = to_rcar_pd(genpd); 47662306a36Sopenharmony_ci if (!(pd->flags & PD_CPU) || pd->ch.chan_bit != idx) 47762306a36Sopenharmony_ci continue; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return rcar_sysc_power(&pd->ch, on); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return -ENOENT; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ciint rcar_sysc_power_down_cpu(unsigned int cpu) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci return rcar_sysc_power_cpu(cpu, false); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciint rcar_sysc_power_up_cpu(unsigned int cpu) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci return rcar_sysc_power_cpu(cpu, true); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci#endif /* CONFIG_ARCH_R8A7779 */ 495