File Index
Files and directories
Sign in
FILE

rmobile-sysc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pmdomain/renesas/rmobile-sysc.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * rmobile power management support
  4 *
  5 * Copyright (C) 2012  Renesas Solutions Corp.
  6 * Copyright (C) 2012  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  7 * Copyright (C) 2014  Glider bvba
  8 *
  9 * based on pm-sh7372.c
 10 *  Copyright (C) 2011 Magnus Damm
 11 */
 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	100
 30#define PSTR_DELAY_US	10
 31
 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 inline
 41struct 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_FLAG_NO_STAY_ON;
105	genpd->attach_dev = cpg_mstp_attach_dev;
106	genpd->detach_dev = cpg_mstp_detach_dev;
107
108	if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) {
109		genpd->power_off = rmobile_pd_power_down;
110		genpd->power_on = rmobile_pd_power_up;
111		__rmobile_pd_power_up(rmobile_pd);
112	}
113
114	pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
115}
116
117static int rmobile_pd_suspend_console(void)
118{
119	/*
120	 * Serial consoles make use of SCIF hardware located in this domain,
121	 * hence keep the power domain on if "no_console_suspend" is set.
122	 */
123	return console_suspend_enabled ? 0 : -EBUSY;
124}
125
126enum pd_types {
127	PD_NORMAL,
128	PD_CPU,
129	PD_CONSOLE,
130	PD_DEBUG,
131	PD_MEMCTL,
132};
133
134#define MAX_NUM_SPECIAL_PDS	16
135
136static struct special_pd {
137	struct device_node *pd;
138	enum pd_types type;
139} special_pds[MAX_NUM_SPECIAL_PDS] __initdata;
140
141static unsigned int num_special_pds __initdata;
142
143static const struct of_device_id special_ids[] __initconst = {
144	{ .compatible = "arm,coresight-etm3x", .data = (void *)PD_DEBUG },
145	{ .compatible = "renesas,dbsc-r8a73a4", .data = (void *)PD_MEMCTL, },
146	{ .compatible = "renesas,dbsc3-r8a7740", .data = (void *)PD_MEMCTL, },
147	{ .compatible = "renesas,sbsc-sh73a0", .data = (void *)PD_MEMCTL, },
148	{ /* sentinel */ },
149};
150
151static void __init add_special_pd(struct device_node *np, enum pd_types type)
152{
153	unsigned int i;
154	struct device_node *pd;
155
156	pd = of_parse_phandle(np, "power-domains", 0);
157	if (!pd)
158		return;
159
160	for (i = 0; i < num_special_pds; i++)
161		if (pd == special_pds[i].pd && type == special_pds[i].type) {
162			of_node_put(pd);
163			return;
164		}
165
166	if (num_special_pds == ARRAY_SIZE(special_pds)) {
167		pr_warn("Too many special PM domains\n");
168		of_node_put(pd);
169		return;
170	}
171
172	pr_debug("Special PM domain %pOFn type %d for %pOF\n", pd, type, np);
173
174	special_pds[num_special_pds].pd = pd;
175	special_pds[num_special_pds].type = type;
176	num_special_pds++;
177}
178
179static void __init get_special_pds(void)
180{
181	struct device_node *np;
182	const struct of_device_id *id;
183
184	/* PM domains containing CPUs */
185	for_each_of_cpu_node(np)
186		add_special_pd(np, PD_CPU);
187
188	/* PM domain containing console */
189	if (of_stdout)
190		add_special_pd(of_stdout, PD_CONSOLE);
191
192	/* PM domains containing other special devices */
193	for_each_matching_node_and_match(np, special_ids, &id)
194		add_special_pd(np, (uintptr_t)id->data);
195}
196
197static void __init put_special_pds(void)
198{
199	unsigned int i;
200
201	for (i = 0; i < num_special_pds; i++)
202		of_node_put(special_pds[i].pd);
203}
204
205static enum pd_types __init pd_type(const struct device_node *pd)
206{
207	unsigned int i;
208
209	for (i = 0; i < num_special_pds; i++)
210		if (pd == special_pds[i].pd)
211			return special_pds[i].type;
212
213	return PD_NORMAL;
214}
215
216static void __init rmobile_setup_pm_domain(struct device_node *np,
217					   struct rmobile_pm_domain *pd)
218{
219	const char *name = pd->genpd.name;
220
221	switch (pd_type(np)) {
222	case PD_CPU:
223		/*
224		 * This domain contains the CPU core and therefore it should
225		 * only be turned off if the CPU is not in use.
226		 */
227		pr_debug("PM domain %s contains CPU\n", name);
228		pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
229		break;
230
231	case PD_CONSOLE:
232		pr_debug("PM domain %s contains serial console\n", name);
233		pd->gov = &pm_domain_always_on_gov;
234		pd->suspend = rmobile_pd_suspend_console;
235		break;
236
237	case PD_DEBUG:
238		/*
239		 * This domain contains the Coresight-ETM hardware block and
240		 * therefore it should only be turned off if the debug module
241		 * is not in use.
242		 */
243		pr_debug("PM domain %s contains Coresight-ETM\n", name);
244		pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
245		break;
246
247	case PD_MEMCTL:
248		/*
249		 * This domain contains a memory-controller and therefore it
250		 * should only be turned off if memory is not in use.
251		 */
252		pr_debug("PM domain %s contains MEMCTL\n", name);
253		pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
254		break;
255
256	case PD_NORMAL:
257		if (pd->bit_shift == ~0) {
258			/* Top-level always-on domain */
259			pr_debug("PM domain %s is always-on domain\n", name);
260			pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
261		}
262		break;
263	}
264
265	rmobile_init_pm_domain(pd);
266}
267
268static int __init rmobile_add_pm_domains(void __iomem *base,
269					 struct device_node *parent,
270					 struct generic_pm_domain *genpd_parent)
271{
272	for_each_child_of_node_scoped(parent, np) {
273		struct rmobile_pm_domain *pd;
274		u32 idx = ~0;
275
276		if (of_property_read_u32(np, "reg", &idx)) {
277			/* always-on domain */
278		}
279
280		pd = kzalloc(sizeof(*pd), GFP_KERNEL);
281		if (!pd)
282			return -ENOMEM;
283
284		pd->genpd.name = np->name;
285		pd->base = base;
286		pd->bit_shift = idx;
287
288		rmobile_setup_pm_domain(np, pd);
289		if (genpd_parent)
290			pm_genpd_add_subdomain(genpd_parent, &pd->genpd);
291		of_genpd_add_provider_simple(np, &pd->genpd);
292
293		rmobile_add_pm_domains(base, np, &pd->genpd);
294	}
295	return 0;
296}
297
298static int __init rmobile_init_pm_domains(void)
299{
300	struct device_node *np, *pmd;
301	bool scanned = false;
302	void __iomem *base;
303	int ret = 0;
304
305	for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") {
306		base = of_iomap(np, 0);
307		if (!base) {
308			pr_warn("%pOF cannot map reg 0\n", np);
309			continue;
310		}
311
312		pmd = of_get_child_by_name(np, "pm-domains");
313		if (!pmd) {
314			iounmap(base);
315			pr_warn("%pOF lacks pm-domains node\n", np);
316			continue;
317		}
318
319		if (!scanned) {
320			/* Find PM domains containing special blocks */
321			get_special_pds();
322			scanned = true;
323		}
324
325		ret = rmobile_add_pm_domains(base, pmd, NULL);
326		of_node_put(pmd);
327		if (ret) {
328			of_node_put(np);
329			break;
330		}
331
332		fwnode_dev_initialized(of_fwnode_handle(np), true);
333	}
334
335	put_special_pds();
336
337	return ret;
338}
339postcore_initcall(rmobile_init_pm_domains);
~/ Read-only file preview.