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