• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * rmobile power management support
3  *
4  * Copyright (C) 2012  Renesas Solutions Corp.
5  * Copyright (C) 2012  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6  * Copyright (C) 2014  Glider bvba
7  *
8  * based on pm-sh7372.c
9  *  Copyright (C) 2011 Magnus Damm
10  *
11  * This file is subject to the terms and conditions of the GNU General Public
12  * License.  See the file "COPYING" in the main directory of this archive
13  * for more details.
14  */
15 #include <linux/clk/shmobile.h>
16 #include <linux/console.h>
17 #include <linux/delay.h>
18 #include <linux/of.h>
19 #include <linux/of_address.h>
20 #include <linux/of_platform.h>
21 #include <linux/platform_device.h>
22 #include <linux/pm.h>
23 #include <linux/pm_clock.h>
24 #include <linux/slab.h>
25 
26 #include <asm/io.h>
27 
28 #include "pm-rmobile.h"
29 
30 /* SYSC */
31 #define SPDCR		0x08	/* SYS Power Down Control Register */
32 #define SWUCR		0x14	/* SYS Wakeup Control Register */
33 #define PSTR		0x80	/* Power Status Register */
34 
35 #define PSTR_RETRIES	100
36 #define PSTR_DELAY_US	10
37 
38 static inline
to_rmobile_pd(struct generic_pm_domain * d)39 struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d)
40 {
41 	return container_of(d, struct rmobile_pm_domain, genpd);
42 }
43 
rmobile_pd_power_down(struct generic_pm_domain * genpd)44 static int rmobile_pd_power_down(struct generic_pm_domain *genpd)
45 {
46 	struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd);
47 	unsigned int mask;
48 
49 	if (rmobile_pd->bit_shift == ~0)
50 		return -EBUSY;
51 
52 	mask = BIT(rmobile_pd->bit_shift);
53 	if (rmobile_pd->suspend) {
54 		int ret = rmobile_pd->suspend();
55 
56 		if (ret)
57 			return ret;
58 	}
59 
60 	if (__raw_readl(rmobile_pd->base + PSTR) & mask) {
61 		unsigned int retry_count;
62 		__raw_writel(mask, rmobile_pd->base + SPDCR);
63 
64 		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
65 			if (!(__raw_readl(rmobile_pd->base + SPDCR) & mask))
66 				break;
67 			cpu_relax();
68 		}
69 	}
70 
71 	if (!rmobile_pd->no_debug)
72 		pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n",
73 			 genpd->name, mask,
74 			 __raw_readl(rmobile_pd->base + PSTR));
75 
76 	return 0;
77 }
78 
__rmobile_pd_power_up(struct rmobile_pm_domain * rmobile_pd,bool do_resume)79 static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd,
80 				 bool do_resume)
81 {
82 	unsigned int mask;
83 	unsigned int retry_count;
84 	int ret = 0;
85 
86 	if (rmobile_pd->bit_shift == ~0)
87 		return 0;
88 
89 	mask = BIT(rmobile_pd->bit_shift);
90 	if (__raw_readl(rmobile_pd->base + PSTR) & mask)
91 		goto out;
92 
93 	__raw_writel(mask, rmobile_pd->base + SWUCR);
94 
95 	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
96 		if (!(__raw_readl(rmobile_pd->base + SWUCR) & mask))
97 			break;
98 		if (retry_count > PSTR_RETRIES)
99 			udelay(PSTR_DELAY_US);
100 		else
101 			cpu_relax();
102 	}
103 	if (!retry_count)
104 		ret = -EIO;
105 
106 	if (!rmobile_pd->no_debug)
107 		pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n",
108 			 rmobile_pd->genpd.name, mask,
109 			 __raw_readl(rmobile_pd->base + PSTR));
110 
111 out:
112 	if (ret == 0 && rmobile_pd->resume && do_resume)
113 		rmobile_pd->resume();
114 
115 	return ret;
116 }
117 
rmobile_pd_power_up(struct generic_pm_domain * genpd)118 static int rmobile_pd_power_up(struct generic_pm_domain *genpd)
119 {
120 	return __rmobile_pd_power_up(to_rmobile_pd(genpd), true);
121 }
122 
rmobile_pd_active_wakeup(struct device * dev)123 static bool rmobile_pd_active_wakeup(struct device *dev)
124 {
125 	return true;
126 }
127 
rmobile_init_pm_domain(struct rmobile_pm_domain * rmobile_pd)128 static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
129 {
130 	struct generic_pm_domain *genpd = &rmobile_pd->genpd;
131 	struct dev_power_governor *gov = rmobile_pd->gov;
132 
133 	genpd->flags = GENPD_FLAG_PM_CLK;
134 	pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
135 	genpd->dev_ops.active_wakeup	= rmobile_pd_active_wakeup;
136 	genpd->power_off		= rmobile_pd_power_down;
137 	genpd->power_on			= rmobile_pd_power_up;
138 	genpd->attach_dev		= cpg_mstp_attach_dev;
139 	genpd->detach_dev		= cpg_mstp_detach_dev;
140 	__rmobile_pd_power_up(rmobile_pd, false);
141 }
142 
rmobile_pd_suspend_busy(void)143 static int rmobile_pd_suspend_busy(void)
144 {
145 	/*
146 	 * This domain should not be turned off.
147 	 */
148 	return -EBUSY;
149 }
150 
rmobile_pd_suspend_console(void)151 static int rmobile_pd_suspend_console(void)
152 {
153 	/*
154 	 * Serial consoles make use of SCIF hardware located in this domain,
155 	 * hence keep the power domain on if "no_console_suspend" is set.
156 	 */
157 	return console_suspend_enabled ? 0 : -EBUSY;
158 }
159 
160 enum pd_types {
161 	PD_NORMAL,
162 	PD_CPU,
163 	PD_CONSOLE,
164 	PD_DEBUG,
165 	PD_MEMCTL,
166 };
167 
168 #define MAX_NUM_SPECIAL_PDS	16
169 
170 static struct special_pd {
171 	struct device_node *pd;
172 	enum pd_types type;
173 } special_pds[MAX_NUM_SPECIAL_PDS] __initdata;
174 
175 static unsigned int num_special_pds __initdata;
176 
177 static const struct of_device_id special_ids[] __initconst = {
178 	{ .compatible = "arm,coresight-etm3x", .data = (void *)PD_DEBUG },
179 	{ .compatible = "renesas,dbsc-r8a73a4", .data = (void *)PD_MEMCTL, },
180 	{ .compatible = "renesas,dbsc3-r8a7740", .data = (void *)PD_MEMCTL, },
181 	{ .compatible = "renesas,sbsc-sh73a0", .data = (void *)PD_MEMCTL, },
182 	{ /* sentinel */ },
183 };
184 
add_special_pd(struct device_node * np,enum pd_types type)185 static void __init add_special_pd(struct device_node *np, enum pd_types type)
186 {
187 	unsigned int i;
188 	struct device_node *pd;
189 
190 	pd = of_parse_phandle(np, "power-domains", 0);
191 	if (!pd)
192 		return;
193 
194 	for (i = 0; i < num_special_pds; i++)
195 		if (pd == special_pds[i].pd && type == special_pds[i].type) {
196 			of_node_put(pd);
197 			return;
198 		}
199 
200 	if (num_special_pds == ARRAY_SIZE(special_pds)) {
201 		pr_warn("Too many special PM domains\n");
202 		of_node_put(pd);
203 		return;
204 	}
205 
206 	pr_debug("Special PM domain %s type %d for %s\n", pd->name, type,
207 		 np->full_name);
208 
209 	special_pds[num_special_pds].pd = pd;
210 	special_pds[num_special_pds].type = type;
211 	num_special_pds++;
212 }
213 
get_special_pds(void)214 static void __init get_special_pds(void)
215 {
216 	struct device_node *np;
217 	const struct of_device_id *id;
218 
219 	/* PM domains containing CPUs */
220 	for_each_node_by_type(np, "cpu")
221 		add_special_pd(np, PD_CPU);
222 
223 	/* PM domain containing console */
224 	if (of_stdout)
225 		add_special_pd(of_stdout, PD_CONSOLE);
226 
227 	/* PM domains containing other special devices */
228 	for_each_matching_node_and_match(np, special_ids, &id)
229 		add_special_pd(np, (enum pd_types)id->data);
230 }
231 
put_special_pds(void)232 static void __init put_special_pds(void)
233 {
234 	unsigned int i;
235 
236 	for (i = 0; i < num_special_pds; i++)
237 		of_node_put(special_pds[i].pd);
238 }
239 
pd_type(const struct device_node * pd)240 static enum pd_types __init pd_type(const struct device_node *pd)
241 {
242 	unsigned int i;
243 
244 	for (i = 0; i < num_special_pds; i++)
245 		if (pd == special_pds[i].pd)
246 			return special_pds[i].type;
247 
248 	return PD_NORMAL;
249 }
250 
rmobile_setup_pm_domain(struct device_node * np,struct rmobile_pm_domain * pd)251 static void __init rmobile_setup_pm_domain(struct device_node *np,
252 					   struct rmobile_pm_domain *pd)
253 {
254 	const char *name = pd->genpd.name;
255 
256 	switch (pd_type(np)) {
257 	case PD_CPU:
258 		/*
259 		 * This domain contains the CPU core and therefore it should
260 		 * only be turned off if the CPU is not in use.
261 		 */
262 		pr_debug("PM domain %s contains CPU\n", name);
263 		pd->gov = &pm_domain_always_on_gov;
264 		pd->suspend = rmobile_pd_suspend_busy;
265 		break;
266 
267 	case PD_CONSOLE:
268 		pr_debug("PM domain %s contains serial console\n", name);
269 		pd->gov = &pm_domain_always_on_gov;
270 		pd->suspend = rmobile_pd_suspend_console;
271 		break;
272 
273 	case PD_DEBUG:
274 		/*
275 		 * This domain contains the Coresight-ETM hardware block and
276 		 * therefore it should only be turned off if the debug module
277 		 * is not in use.
278 		 */
279 		pr_debug("PM domain %s contains Coresight-ETM\n", name);
280 		pd->gov = &pm_domain_always_on_gov;
281 		pd->suspend = rmobile_pd_suspend_busy;
282 		break;
283 
284 	case PD_MEMCTL:
285 		/*
286 		 * This domain contains a memory-controller and therefore it
287 		 * should only be turned off if memory is not in use.
288 		 */
289 		pr_debug("PM domain %s contains MEMCTL\n", name);
290 		pd->gov = &pm_domain_always_on_gov;
291 		pd->suspend = rmobile_pd_suspend_busy;
292 		break;
293 
294 	case PD_NORMAL:
295 		break;
296 	}
297 
298 	rmobile_init_pm_domain(pd);
299 }
300 
rmobile_add_pm_domains(void __iomem * base,struct device_node * parent,struct generic_pm_domain * genpd_parent)301 static int __init rmobile_add_pm_domains(void __iomem *base,
302 					 struct device_node *parent,
303 					 struct generic_pm_domain *genpd_parent)
304 {
305 	struct device_node *np;
306 
307 	for_each_child_of_node(parent, np) {
308 		struct rmobile_pm_domain *pd;
309 		u32 idx = ~0;
310 
311 		if (of_property_read_u32(np, "reg", &idx)) {
312 			/* always-on domain */
313 		}
314 
315 		pd = kzalloc(sizeof(*pd), GFP_KERNEL);
316 		if (!pd) {
317 			of_node_put(np);
318 			return -ENOMEM;
319 		}
320 
321 		pd->genpd.name = np->name;
322 		pd->base = base;
323 		pd->bit_shift = idx;
324 
325 		rmobile_setup_pm_domain(np, pd);
326 		if (genpd_parent)
327 			pm_genpd_add_subdomain(genpd_parent, &pd->genpd);
328 		of_genpd_add_provider_simple(np, &pd->genpd);
329 
330 		rmobile_add_pm_domains(base, np, &pd->genpd);
331 	}
332 	return 0;
333 }
334 
rmobile_init_pm_domains(void)335 static int __init rmobile_init_pm_domains(void)
336 {
337 	struct device_node *np, *pmd;
338 	bool scanned = false;
339 	void __iomem *base;
340 	int ret = 0;
341 
342 	for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") {
343 		base = of_iomap(np, 0);
344 		if (!base) {
345 			pr_warn("%s cannot map reg 0\n", np->full_name);
346 			continue;
347 		}
348 
349 		pmd = of_get_child_by_name(np, "pm-domains");
350 		if (!pmd) {
351 			pr_warn("%s lacks pm-domains node\n", np->full_name);
352 			continue;
353 		}
354 
355 		if (!scanned) {
356 			/* Find PM domains containing special blocks */
357 			get_special_pds();
358 			scanned = true;
359 		}
360 
361 		ret = rmobile_add_pm_domains(base, pmd, NULL);
362 		of_node_put(pmd);
363 		if (ret) {
364 			of_node_put(np);
365 			break;
366 		}
367 	}
368 
369 	put_special_pds();
370 
371 	return ret;
372 }
373 
374 core_initcall(rmobile_init_pm_domains);
375