Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  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->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	16
134
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 should
224		 * 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 and
239		 * therefore it should only be turned off if the debug module
240		 * 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 it
249		 * 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	struct device_node *np;
272
273	for_each_child_of_node(parent, np) {
274		struct rmobile_pm_domain *pd;
275		u32 idx = ~0;
276
277		if (of_property_read_u32(np, "reg", &idx)) {
278			/* always-on domain */
279		}
280
281		pd = kzalloc(sizeof(*pd), GFP_KERNEL);
282		if (!pd) {
283			of_node_put(np);
284			return -ENOMEM;
285		}
286
287		pd->genpd.name = np->name;
288		pd->base = base;
289		pd->bit_shift = idx;
290
291		rmobile_setup_pm_domain(np, pd);
292		if (genpd_parent)
293			pm_genpd_add_subdomain(genpd_parent, &pd->genpd);
294		of_genpd_add_provider_simple(np, &pd->genpd);
295
296		rmobile_add_pm_domains(base, np, &pd->genpd);
297	}
298	return 0;
299}
300
301static int __init rmobile_init_pm_domains(void)
302{
303	struct device_node *np, *pmd;
304	bool scanned = false;
305	void __iomem *base;
306	int ret = 0;
307
308	for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") {
309		base = of_iomap(np, 0);
310		if (!base) {
311			pr_warn("%pOF cannot map reg 0\n", np);
312			continue;
313		}
314
315		pmd = of_get_child_by_name(np, "pm-domains");
316		if (!pmd) {
317			iounmap(base);
318			pr_warn("%pOF lacks pm-domains node\n", np);
319			continue;
320		}
321
322		if (!scanned) {
323			/* Find PM domains containing special blocks */
324			get_special_pds();
325			scanned = true;
326		}
327
328		ret = rmobile_add_pm_domains(base, pmd, NULL);
329		of_node_put(pmd);
330		if (ret) {
331			of_node_put(np);
332			break;
333		}
334
335		fwnode_dev_initialized(of_fwnode_handle(np), true);
336	}
337
338	put_special_pds();
339
340	return ret;
341}
342
343core_initcall(rmobile_init_pm_domains);