aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeert Uytterhoeven <geert+renesas@glider.be>2016-04-20 08:02:38 -0400
committerSimon Horman <horms+renesas@verge.net.au>2016-04-22 03:30:37 -0400
commitdcc09fd143bb97c2e83e443f7343c07aa0a9a6c0 (patch)
treee98508aebce84779290f9ff18d17d3ad9ad5f89e
parent68667cebfc0d27d2153d7a6b489f3231b533d9bc (diff)
soc: renesas: rcar-sysc: Add DT support for SYSC PM domains
Populate the SYSC PM domains from DT, based on the presence of a device node for the System Controller. The actual power area hiearchy, and features of specific areas are obtained from tables in the C code. The SYSCIER and SYSCIMR register values are derived from the power areas present, which will help to get rid of the hardcoded values in R-Car H1 and R-Car Gen2 platform code later. Initialization is done from an early_initcall(), to make sure the PM Domains are initialized before secondary CPU bringup. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
-rw-r--r--drivers/soc/renesas/rcar-sysc.c202
-rw-r--r--drivers/soc/renesas/rcar-sysc.h53
2 files changed, 254 insertions, 1 deletions
diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c
index 9ba5fd15c53b..95f2b59cbd76 100644
--- a/drivers/soc/renesas/rcar-sysc.c
+++ b/drivers/soc/renesas/rcar-sysc.c
@@ -2,6 +2,7 @@
2 * R-Car SYSC Power management support 2 * R-Car SYSC Power management support
3 * 3 *
4 * Copyright (C) 2014 Magnus Damm 4 * Copyright (C) 2014 Magnus Damm
5 * Copyright (C) 2015-2016 Glider bvba
5 * 6 *
6 * This file is subject to the terms and conditions of the GNU General Public 7 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file "COPYING" in the main directory of this archive 8 * License. See the file "COPYING" in the main directory of this archive
@@ -11,10 +12,15 @@
11#include <linux/delay.h> 12#include <linux/delay.h>
12#include <linux/err.h> 13#include <linux/err.h>
13#include <linux/mm.h> 14#include <linux/mm.h>
15#include <linux/of_address.h>
16#include <linux/pm_domain.h>
17#include <linux/slab.h>
14#include <linux/spinlock.h> 18#include <linux/spinlock.h>
15#include <linux/io.h> 19#include <linux/io.h>
16#include <linux/soc/renesas/rcar-sysc.h> 20#include <linux/soc/renesas/rcar-sysc.h>
17 21
22#include "rcar-sysc.h"
23
18/* SYSC Common */ 24/* SYSC Common */
19#define SYSCSR 0x00 /* SYSC Status Register */ 25#define SYSCSR 0x00 /* SYSC Status Register */
20#define SYSCISR 0x04 /* Interrupt Status Register */ 26#define SYSCISR 0x04 /* Interrupt Status Register */
@@ -29,7 +35,8 @@
29/* 35/*
30 * Power Control Register Offsets inside the register block for each domain 36 * Power Control Register Offsets inside the register block for each domain
31 * Note: The "CR" registers for ARM cores exist on H1 only 37 * Note: The "CR" registers for ARM cores exist on H1 only
32 * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 38 * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2
39 * Use PSCI on R-Car Gen3
33 */ 40 */
34#define PWRSR_OFFS 0x00 /* Power Status Register */ 41#define PWRSR_OFFS 0x00 /* Power Status Register */
35#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ 42#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */
@@ -48,6 +55,8 @@
48#define SYSCISR_RETRIES 1000 55#define SYSCISR_RETRIES 1000
49#define SYSCISR_DELAY_US 1 56#define SYSCISR_DELAY_US 1
50 57
58#define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */
59
51static void __iomem *rcar_sysc_base; 60static void __iomem *rcar_sysc_base;
52static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ 61static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */
53 62
@@ -162,3 +171,194 @@ void __iomem *rcar_sysc_init(phys_addr_t base)
162 171
163 return rcar_sysc_base; 172 return rcar_sysc_base;
164} 173}
174
175struct rcar_sysc_pd {
176 struct generic_pm_domain genpd;
177 struct rcar_sysc_ch ch;
178 unsigned int flags;
179 char name[0];
180};
181
182static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d)
183{
184 return container_of(d, struct rcar_sysc_pd, genpd);
185}
186
187static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd)
188{
189 struct rcar_sysc_pd *pd = to_rcar_pd(genpd);
190
191 pr_debug("%s: %s\n", __func__, genpd->name);
192
193 if (pd->flags & PD_NO_CR) {
194 pr_debug("%s: Cannot control %s\n", __func__, genpd->name);
195 return -EBUSY;
196 }
197
198 if (pd->flags & PD_BUSY) {
199 pr_debug("%s: %s busy\n", __func__, genpd->name);
200 return -EBUSY;
201 }
202
203 return rcar_sysc_power_down(&pd->ch);
204}
205
206static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd)
207{
208 struct rcar_sysc_pd *pd = to_rcar_pd(genpd);
209
210 pr_debug("%s: %s\n", __func__, genpd->name);
211
212 if (pd->flags & PD_NO_CR) {
213 pr_debug("%s: Cannot control %s\n", __func__, genpd->name);
214 return 0;
215 }
216
217 return rcar_sysc_power_up(&pd->ch);
218}
219
220static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
221{
222 struct generic_pm_domain *genpd = &pd->genpd;
223 const char *name = pd->genpd.name;
224 struct dev_power_governor *gov = &simple_qos_governor;
225
226 if (pd->flags & PD_CPU) {
227 /*
228 * This domain contains a CPU core and therefore it should
229 * only be turned off if the CPU is not in use.
230 */
231 pr_debug("PM domain %s contains %s\n", name, "CPU");
232 pd->flags |= PD_BUSY;
233 gov = &pm_domain_always_on_gov;
234 } else if (pd->flags & PD_SCU) {
235 /*
236 * This domain contains an SCU and cache-controller, and
237 * therefore it should only be turned off if the CPU cores are
238 * not in use.
239 */
240 pr_debug("PM domain %s contains %s\n", name, "SCU");
241 pd->flags |= PD_BUSY;
242 gov = &pm_domain_always_on_gov;
243 } else if (pd->flags & PD_NO_CR) {
244 /*
245 * This domain cannot be turned off.
246 */
247 pd->flags |= PD_BUSY;
248 gov = &pm_domain_always_on_gov;
249 }
250
251 genpd->power_off = rcar_sysc_pd_power_off;
252 genpd->power_on = rcar_sysc_pd_power_on;
253
254 if (pd->flags & (PD_CPU | PD_NO_CR)) {
255 /* Skip CPUs (handled by SMP code) and areas without control */
256 pr_debug("%s: Not touching %s\n", __func__, genpd->name);
257 goto finalize;
258 }
259
260 if (!rcar_sysc_power_is_off(&pd->ch)) {
261 pr_debug("%s: %s is already powered\n", __func__, genpd->name);
262 goto finalize;
263 }
264
265 rcar_sysc_power_up(&pd->ch);
266
267finalize:
268 pm_genpd_init(genpd, gov, false);
269}
270
271static const struct of_device_id rcar_sysc_matches[] = {
272 { /* sentinel */ }
273};
274
275struct rcar_pm_domains {
276 struct genpd_onecell_data onecell_data;
277 struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1];
278};
279
280static int __init rcar_sysc_pd_init(void)
281{
282 const struct rcar_sysc_info *info;
283 const struct of_device_id *match;
284 struct rcar_pm_domains *domains;
285 struct device_node *np;
286 u32 syscier, syscimr;
287 void __iomem *base;
288 unsigned int i;
289 int error;
290
291 np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match);
292 if (!np)
293 return -ENODEV;
294
295 info = match->data;
296
297 base = of_iomap(np, 0);
298 if (!base) {
299 pr_warn("%s: Cannot map regs\n", np->full_name);
300 error = -ENOMEM;
301 goto out_put;
302 }
303
304 rcar_sysc_base = base;
305
306 domains = kzalloc(sizeof(*domains), GFP_KERNEL);
307 if (!domains) {
308 error = -ENOMEM;
309 goto out_put;
310 }
311
312 domains->onecell_data.domains = domains->domains;
313 domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains);
314
315 for (i = 0, syscier = 0; i < info->num_areas; i++)
316 syscier |= BIT(info->areas[i].isr_bit);
317
318 /*
319 * Mask all interrupt sources to prevent the CPU from receiving them.
320 * Make sure not to clear reserved bits that were set before.
321 */
322 syscimr = ioread32(base + SYSCIMR);
323 syscimr |= syscier;
324 pr_debug("%s: syscimr = 0x%08x\n", np->full_name, syscimr);
325 iowrite32(syscimr, base + SYSCIMR);
326
327 /*
328 * SYSC needs all interrupt sources enabled to control power.
329 */
330 pr_debug("%s: syscier = 0x%08x\n", np->full_name, syscier);
331 iowrite32(syscier, base + SYSCIER);
332
333 for (i = 0; i < info->num_areas; i++) {
334 const struct rcar_sysc_area *area = &info->areas[i];
335 struct rcar_sysc_pd *pd;
336
337 pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL);
338 if (!pd) {
339 error = -ENOMEM;
340 goto out_put;
341 }
342
343 strcpy(pd->name, area->name);
344 pd->genpd.name = pd->name;
345 pd->ch.chan_offs = area->chan_offs;
346 pd->ch.chan_bit = area->chan_bit;
347 pd->ch.isr_bit = area->isr_bit;
348 pd->flags = area->flags;
349
350 rcar_sysc_pd_setup(pd);
351 if (area->parent >= 0)
352 pm_genpd_add_subdomain(domains->domains[area->parent],
353 &pd->genpd);
354
355 domains->domains[area->isr_bit] = &pd->genpd;
356 }
357
358 of_genpd_add_provider_onecell(np, &domains->onecell_data);
359
360out_put:
361 of_node_put(np);
362 return error;
363}
364early_initcall(rcar_sysc_pd_init);
diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h
new file mode 100644
index 000000000000..7bb48b4f7334
--- /dev/null
+++ b/drivers/soc/renesas/rcar-sysc.h
@@ -0,0 +1,53 @@
1/*
2 * Renesas R-Car System Controller
3 *
4 * Copyright (C) 2016 Glider bvba
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 */
10#ifndef __SOC_RENESAS_RCAR_SYSC_H__
11#define __SOC_RENESAS_RCAR_SYSC_H__
12
13#include <linux/types.h>
14
15
16/*
17 * Power Domain flags
18 */
19#define PD_CPU BIT(0) /* Area contains main CPU core */
20#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */
21#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */
22
23#define PD_BUSY BIT(3) /* Busy, for internal use only */
24
25#define PD_CPU_CR PD_CPU /* CPU area has CR (R-Car H1) */
26#define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR (R-Car Gen2/3) */
27#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */
28
29
30/*
31 * Description of a Power Area
32 */
33
34struct rcar_sysc_area {
35 const char *name;
36 u16 chan_offs; /* Offset of PWRSR register for this area */
37 u8 chan_bit; /* Bit in PWR* (except for PWRUP in PWRSR) */
38 u8 isr_bit; /* Bit in SYSCI*R */
39 int parent; /* -1 if none */
40 unsigned int flags; /* See PD_* */
41};
42
43
44/*
45 * SoC-specific Power Area Description
46 */
47
48struct rcar_sysc_info {
49 const struct rcar_sysc_area *areas;
50 unsigned int num_areas;
51};
52
53#endif /* __SOC_RENESAS_RCAR_SYSC_H__ */