481 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * R-Car SYSC Power management support4 *5 * Copyright (C) 2014 Magnus Damm6 * Copyright (C) 2015-2017 Glider bvba7 */8 9#include <linux/clk/renesas.h>10#include <linux/delay.h>11#include <linux/err.h>12#include <linux/mm.h>13#include <linux/of_address.h>14#include <linux/pm_domain.h>15#include <linux/slab.h>16#include <linux/spinlock.h>17#include <linux/io.h>18#include <linux/iopoll.h>19#include <linux/soc/renesas/rcar-sysc.h>20 21#include "rcar-sysc.h"22 23/* SYSC Common */24#define SYSCSR 0x00 /* SYSC Status Register */25#define SYSCISR 0x04 /* Interrupt Status Register */26#define SYSCISCR 0x08 /* Interrupt Status Clear Register */27#define SYSCIER 0x0c /* Interrupt Enable Register */28#define SYSCIMR 0x10 /* Interrupt Mask Register */29 30/* SYSC Status Register */31#define SYSCSR_PONENB 1 /* Ready for power resume requests */32#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */33 34/*35 * Power Control Register Offsets inside the register block for each domain36 * Note: The "CR" registers for ARM cores exist on H1 only37 * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen238 * Use PSCI on R-Car Gen339 */40#define PWRSR_OFFS 0x00 /* Power Status Register */41#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */42#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */43#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */44#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */45#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */46 47 48#define SYSCSR_TIMEOUT 100049#define SYSCSR_DELAY_US 150 51#define PWRER_RETRIES 100052#define PWRER_DELAY_US 153 54#define SYSCISR_TIMEOUT 100055#define SYSCISR_DELAY_US 156 57#define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */58 59struct rcar_sysc_pd {60 struct generic_pm_domain genpd;61 u16 chan_offs;62 u8 chan_bit;63 u8 isr_bit;64 unsigned int flags;65 char name[];66};67 68static void __iomem *rcar_sysc_base;69static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */70static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val;71 72static int rcar_sysc_pwr_on_off(const struct rcar_sysc_pd *pd, bool on)73{74 unsigned int sr_bit, reg_offs;75 u32 val;76 int ret;77 78 if (on) {79 sr_bit = SYSCSR_PONENB;80 reg_offs = PWRONCR_OFFS;81 } else {82 sr_bit = SYSCSR_POFFENB;83 reg_offs = PWROFFCR_OFFS;84 }85 86 /* Wait until SYSC is ready to accept a power request */87 ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCSR, val,88 val & BIT(sr_bit), SYSCSR_DELAY_US,89 SYSCSR_TIMEOUT);90 if (ret)91 return -EAGAIN;92 93 /* Power-off delay quirk */94 if (!on && (pd->flags & PD_OFF_DELAY))95 udelay(1);96 97 /* Submit power shutoff or power resume request */98 iowrite32(BIT(pd->chan_bit), rcar_sysc_base + pd->chan_offs + reg_offs);99 100 return 0;101}102 103static int rcar_sysc_power(const struct rcar_sysc_pd *pd, bool on)104{105 unsigned int isr_mask = BIT(pd->isr_bit);106 unsigned int chan_mask = BIT(pd->chan_bit);107 unsigned int status, k;108 unsigned long flags;109 int ret;110 111 spin_lock_irqsave(&rcar_sysc_lock, flags);112 113 /*114 * Mask external power requests for CPU or 3DG domains115 */116 if (rcar_sysc_extmask_val) {117 iowrite32(rcar_sysc_extmask_val,118 rcar_sysc_base + rcar_sysc_extmask_offs);119 }120 121 /*122 * The interrupt source needs to be enabled, but masked, to prevent the123 * CPU from receiving it.124 */125 iowrite32(ioread32(rcar_sysc_base + SYSCIMR) | isr_mask,126 rcar_sysc_base + SYSCIMR);127 iowrite32(ioread32(rcar_sysc_base + SYSCIER) | isr_mask,128 rcar_sysc_base + SYSCIER);129 130 iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);131 132 /* Submit power shutoff or resume request until it was accepted */133 for (k = 0; k < PWRER_RETRIES; k++) {134 ret = rcar_sysc_pwr_on_off(pd, on);135 if (ret)136 goto out;137 138 status = ioread32(rcar_sysc_base + pd->chan_offs + PWRER_OFFS);139 if (!(status & chan_mask))140 break;141 142 udelay(PWRER_DELAY_US);143 }144 145 if (k == PWRER_RETRIES) {146 ret = -EIO;147 goto out;148 }149 150 /* Wait until the power shutoff or resume request has completed * */151 ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCISR, status,152 status & isr_mask, SYSCISR_DELAY_US,153 SYSCISR_TIMEOUT);154 if (ret)155 ret = -EIO;156 157 iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);158 159 out:160 if (rcar_sysc_extmask_val)161 iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs);162 163 spin_unlock_irqrestore(&rcar_sysc_lock, flags);164 165 pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off",166 pd->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret);167 return ret;168}169 170static bool rcar_sysc_power_is_off(const struct rcar_sysc_pd *pd)171{172 unsigned int st;173 174 st = ioread32(rcar_sysc_base + pd->chan_offs + PWRSR_OFFS);175 if (st & BIT(pd->chan_bit))176 return true;177 178 return false;179}180 181static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d)182{183 return container_of(d, struct rcar_sysc_pd, genpd);184}185 186static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd)187{188 struct rcar_sysc_pd *pd = to_rcar_pd(genpd);189 190 pr_debug("%s: %s\n", __func__, genpd->name);191 return rcar_sysc_power(pd, false);192}193 194static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd)195{196 struct rcar_sysc_pd *pd = to_rcar_pd(genpd);197 198 pr_debug("%s: %s\n", __func__, genpd->name);199 return rcar_sysc_power(pd, true);200}201 202static bool has_cpg_mstp;203 204static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)205{206 struct generic_pm_domain *genpd = &pd->genpd;207 const char *name = pd->genpd.name;208 int error;209 210 if (pd->flags & PD_CPU) {211 /*212 * This domain contains a CPU core and therefore it should213 * only be turned off if the CPU is not in use.214 */215 pr_debug("PM domain %s contains %s\n", name, "CPU");216 genpd->flags |= GENPD_FLAG_ALWAYS_ON;217 } else if (pd->flags & PD_SCU) {218 /*219 * This domain contains an SCU and cache-controller, and220 * therefore it should only be turned off if the CPU cores are221 * not in use.222 */223 pr_debug("PM domain %s contains %s\n", name, "SCU");224 genpd->flags |= GENPD_FLAG_ALWAYS_ON;225 } else if (pd->flags & PD_NO_CR) {226 /*227 * This domain cannot be turned off.228 */229 genpd->flags |= GENPD_FLAG_ALWAYS_ON;230 }231 232 if (!(pd->flags & (PD_CPU | PD_SCU))) {233 /* Enable Clock Domain for I/O devices */234 genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;235 if (has_cpg_mstp) {236 genpd->attach_dev = cpg_mstp_attach_dev;237 genpd->detach_dev = cpg_mstp_detach_dev;238 } else {239 genpd->attach_dev = cpg_mssr_attach_dev;240 genpd->detach_dev = cpg_mssr_detach_dev;241 }242 }243 244 genpd->power_off = rcar_sysc_pd_power_off;245 genpd->power_on = rcar_sysc_pd_power_on;246 247 if (pd->flags & (PD_CPU | PD_NO_CR)) {248 /* Skip CPUs (handled by SMP code) and areas without control */249 pr_debug("%s: Not touching %s\n", __func__, genpd->name);250 goto finalize;251 }252 253 if (!rcar_sysc_power_is_off(pd)) {254 pr_debug("%s: %s is already powered\n", __func__, genpd->name);255 goto finalize;256 }257 258 rcar_sysc_power(pd, true);259 260finalize:261 error = pm_genpd_init(genpd, &simple_qos_governor, false);262 if (error)263 pr_err("Failed to init PM domain %s: %d\n", name, error);264 265 return error;266}267 268static const struct of_device_id rcar_sysc_matches[] __initconst = {269#ifdef CONFIG_SYSC_R8A7742270 { .compatible = "renesas,r8a7742-sysc", .data = &r8a7742_sysc_info },271#endif272#ifdef CONFIG_SYSC_R8A7743273 { .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info },274 /* RZ/G1N is identical to RZ/G2M w.r.t. power domains. */275 { .compatible = "renesas,r8a7744-sysc", .data = &r8a7743_sysc_info },276#endif277#ifdef CONFIG_SYSC_R8A7745278 { .compatible = "renesas,r8a7745-sysc", .data = &r8a7745_sysc_info },279#endif280#ifdef CONFIG_SYSC_R8A77470281 { .compatible = "renesas,r8a77470-sysc", .data = &r8a77470_sysc_info },282#endif283#ifdef CONFIG_SYSC_R8A774A1284 { .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info },285#endif286#ifdef CONFIG_SYSC_R8A774B1287 { .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info },288#endif289#ifdef CONFIG_SYSC_R8A774C0290 { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info },291#endif292#ifdef CONFIG_SYSC_R8A774E1293 { .compatible = "renesas,r8a774e1-sysc", .data = &r8a774e1_sysc_info },294#endif295#ifdef CONFIG_SYSC_R8A7779296 { .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info },297#endif298#ifdef CONFIG_SYSC_R8A7790299 { .compatible = "renesas,r8a7790-sysc", .data = &r8a7790_sysc_info },300#endif301#ifdef CONFIG_SYSC_R8A7791302 { .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info },303 /* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */304 { .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info },305#endif306#ifdef CONFIG_SYSC_R8A7792307 { .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info },308#endif309#ifdef CONFIG_SYSC_R8A7794310 { .compatible = "renesas,r8a7794-sysc", .data = &r8a7794_sysc_info },311#endif312#ifdef CONFIG_SYSC_R8A7795313 { .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info },314#endif315#ifdef CONFIG_SYSC_R8A77960316 { .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info },317#endif318#ifdef CONFIG_SYSC_R8A77961319 { .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info },320#endif321#ifdef CONFIG_SYSC_R8A77965322 { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info },323#endif324#ifdef CONFIG_SYSC_R8A77970325 { .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info },326#endif327#ifdef CONFIG_SYSC_R8A77980328 { .compatible = "renesas,r8a77980-sysc", .data = &r8a77980_sysc_info },329#endif330#ifdef CONFIG_SYSC_R8A77990331 { .compatible = "renesas,r8a77990-sysc", .data = &r8a77990_sysc_info },332#endif333#ifdef CONFIG_SYSC_R8A77995334 { .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info },335#endif336 { /* sentinel */ }337};338 339struct rcar_pm_domains {340 struct genpd_onecell_data onecell_data;341 struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1];342};343 344static struct genpd_onecell_data *rcar_sysc_onecell_data;345 346static int __init rcar_sysc_pd_init(void)347{348 const struct rcar_sysc_info *info;349 const struct of_device_id *match;350 struct rcar_pm_domains *domains;351 struct device_node *np;352 void __iomem *base;353 unsigned int i;354 int error;355 356 np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match);357 if (!np)358 return -ENODEV;359 360 info = match->data;361 362 if (info->init) {363 error = info->init();364 if (error)365 goto out_put;366 }367 368 has_cpg_mstp = of_find_compatible_node(NULL, NULL,369 "renesas,cpg-mstp-clocks");370 371 base = of_iomap(np, 0);372 if (!base) {373 pr_warn("%pOF: Cannot map regs\n", np);374 error = -ENOMEM;375 goto out_put;376 }377 378 rcar_sysc_base = base;379 380 /* Optional External Request Mask Register */381 rcar_sysc_extmask_offs = info->extmask_offs;382 rcar_sysc_extmask_val = info->extmask_val;383 384 domains = kzalloc(sizeof(*domains), GFP_KERNEL);385 if (!domains) {386 error = -ENOMEM;387 goto out_put;388 }389 390 domains->onecell_data.domains = domains->domains;391 domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains);392 rcar_sysc_onecell_data = &domains->onecell_data;393 394 for (i = 0; i < info->num_areas; i++) {395 const struct rcar_sysc_area *area = &info->areas[i];396 struct rcar_sysc_pd *pd;397 size_t n;398 399 if (!area->name) {400 /* Skip NULLified area */401 continue;402 }403 404 n = strlen(area->name) + 1;405 pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL);406 if (!pd) {407 error = -ENOMEM;408 goto out_put;409 }410 411 memcpy(pd->name, area->name, n);412 pd->genpd.name = pd->name;413 pd->chan_offs = area->chan_offs;414 pd->chan_bit = area->chan_bit;415 pd->isr_bit = area->isr_bit;416 pd->flags = area->flags;417 418 error = rcar_sysc_pd_setup(pd);419 if (error)420 goto out_put;421 422 domains->domains[area->isr_bit] = &pd->genpd;423 424 if (area->parent < 0)425 continue;426 427 error = pm_genpd_add_subdomain(domains->domains[area->parent],428 &pd->genpd);429 if (error) {430 pr_warn("Failed to add PM subdomain %s to parent %u\n",431 area->name, area->parent);432 goto out_put;433 }434 }435 436 error = of_genpd_add_provider_onecell(np, &domains->onecell_data);437 if (!error)438 fwnode_dev_initialized(of_fwnode_handle(np), true);439 440out_put:441 of_node_put(np);442 return error;443}444early_initcall(rcar_sysc_pd_init);445 446#ifdef CONFIG_ARCH_R8A7779447static int rcar_sysc_power_cpu(unsigned int idx, bool on)448{449 struct generic_pm_domain *genpd;450 struct rcar_sysc_pd *pd;451 unsigned int i;452 453 if (!rcar_sysc_onecell_data)454 return -ENODEV;455 456 for (i = 0; i < rcar_sysc_onecell_data->num_domains; i++) {457 genpd = rcar_sysc_onecell_data->domains[i];458 if (!genpd)459 continue;460 461 pd = to_rcar_pd(genpd);462 if (!(pd->flags & PD_CPU) || pd->chan_bit != idx)463 continue;464 465 return rcar_sysc_power(pd, on);466 }467 468 return -ENOENT;469}470 471int rcar_sysc_power_down_cpu(unsigned int cpu)472{473 return rcar_sysc_power_cpu(cpu, false);474}475 476int rcar_sysc_power_up_cpu(unsigned int cpu)477{478 return rcar_sysc_power_cpu(cpu, true);479}480#endif /* CONFIG_ARCH_R8A7779 */481