brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · 5848e79 Raw
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