Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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	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);