Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Geert Uytterhoeven and committed by
Simon Horman
dcc09fd1 68667ceb

+254 -1
+201 -1
drivers/soc/renesas/rcar-sysc.c
··· 2 2 * R-Car SYSC Power management support 3 3 * 4 4 * Copyright (C) 2014 Magnus Damm 5 + * Copyright (C) 2015-2016 Glider bvba 5 6 * 6 7 * This file is subject to the terms and conditions of the GNU General Public 7 8 * License. See the file "COPYING" in the main directory of this archive ··· 12 11 #include <linux/delay.h> 13 12 #include <linux/err.h> 14 13 #include <linux/mm.h> 14 + #include <linux/of_address.h> 15 + #include <linux/pm_domain.h> 16 + #include <linux/slab.h> 15 17 #include <linux/spinlock.h> 16 18 #include <linux/io.h> 17 19 #include <linux/soc/renesas/rcar-sysc.h> 20 + 21 + #include "rcar-sysc.h" 18 22 19 23 /* SYSC Common */ 20 24 #define SYSCSR 0x00 /* SYSC Status Register */ ··· 35 29 /* 36 30 * Power Control Register Offsets inside the register block for each domain 37 31 * Note: The "CR" registers for ARM cores exist on H1 only 38 - * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 32 + * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 33 + * Use PSCI on R-Car Gen3 39 34 */ 40 35 #define PWRSR_OFFS 0x00 /* Power Status Register */ 41 36 #define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ ··· 54 47 55 48 #define SYSCISR_RETRIES 1000 56 49 #define SYSCISR_DELAY_US 1 50 + 51 + #define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */ 57 52 58 53 static void __iomem *rcar_sysc_base; 59 54 static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ ··· 171 162 172 163 return rcar_sysc_base; 173 164 } 165 + 166 + struct rcar_sysc_pd { 167 + struct generic_pm_domain genpd; 168 + struct rcar_sysc_ch ch; 169 + unsigned int flags; 170 + char name[0]; 171 + }; 172 + 173 + static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d) 174 + { 175 + return container_of(d, struct rcar_sysc_pd, genpd); 176 + } 177 + 178 + static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd) 179 + { 180 + struct rcar_sysc_pd *pd = to_rcar_pd(genpd); 181 + 182 + pr_debug("%s: %s\n", __func__, genpd->name); 183 + 184 + if (pd->flags & PD_NO_CR) { 185 + pr_debug("%s: Cannot control %s\n", __func__, genpd->name); 186 + return -EBUSY; 187 + } 188 + 189 + if (pd->flags & PD_BUSY) { 190 + pr_debug("%s: %s busy\n", __func__, genpd->name); 191 + return -EBUSY; 192 + } 193 + 194 + return rcar_sysc_power_down(&pd->ch); 195 + } 196 + 197 + static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) 198 + { 199 + struct rcar_sysc_pd *pd = to_rcar_pd(genpd); 200 + 201 + pr_debug("%s: %s\n", __func__, genpd->name); 202 + 203 + if (pd->flags & PD_NO_CR) { 204 + pr_debug("%s: Cannot control %s\n", __func__, genpd->name); 205 + return 0; 206 + } 207 + 208 + return rcar_sysc_power_up(&pd->ch); 209 + } 210 + 211 + static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) 212 + { 213 + struct generic_pm_domain *genpd = &pd->genpd; 214 + const char *name = pd->genpd.name; 215 + struct dev_power_governor *gov = &simple_qos_governor; 216 + 217 + if (pd->flags & PD_CPU) { 218 + /* 219 + * This domain contains a CPU core and therefore it should 220 + * only be turned off if the CPU is not in use. 221 + */ 222 + pr_debug("PM domain %s contains %s\n", name, "CPU"); 223 + pd->flags |= PD_BUSY; 224 + gov = &pm_domain_always_on_gov; 225 + } else if (pd->flags & PD_SCU) { 226 + /* 227 + * This domain contains an SCU and cache-controller, and 228 + * therefore it should only be turned off if the CPU cores are 229 + * not in use. 230 + */ 231 + pr_debug("PM domain %s contains %s\n", name, "SCU"); 232 + pd->flags |= PD_BUSY; 233 + gov = &pm_domain_always_on_gov; 234 + } else if (pd->flags & PD_NO_CR) { 235 + /* 236 + * This domain cannot be turned off. 237 + */ 238 + pd->flags |= PD_BUSY; 239 + gov = &pm_domain_always_on_gov; 240 + } 241 + 242 + genpd->power_off = rcar_sysc_pd_power_off; 243 + genpd->power_on = rcar_sysc_pd_power_on; 244 + 245 + if (pd->flags & (PD_CPU | PD_NO_CR)) { 246 + /* Skip CPUs (handled by SMP code) and areas without control */ 247 + pr_debug("%s: Not touching %s\n", __func__, genpd->name); 248 + goto finalize; 249 + } 250 + 251 + if (!rcar_sysc_power_is_off(&pd->ch)) { 252 + pr_debug("%s: %s is already powered\n", __func__, genpd->name); 253 + goto finalize; 254 + } 255 + 256 + rcar_sysc_power_up(&pd->ch); 257 + 258 + finalize: 259 + pm_genpd_init(genpd, gov, false); 260 + } 261 + 262 + static const struct of_device_id rcar_sysc_matches[] = { 263 + { /* sentinel */ } 264 + }; 265 + 266 + struct rcar_pm_domains { 267 + struct genpd_onecell_data onecell_data; 268 + struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1]; 269 + }; 270 + 271 + static int __init rcar_sysc_pd_init(void) 272 + { 273 + const struct rcar_sysc_info *info; 274 + const struct of_device_id *match; 275 + struct rcar_pm_domains *domains; 276 + struct device_node *np; 277 + u32 syscier, syscimr; 278 + void __iomem *base; 279 + unsigned int i; 280 + int error; 281 + 282 + np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match); 283 + if (!np) 284 + return -ENODEV; 285 + 286 + info = match->data; 287 + 288 + base = of_iomap(np, 0); 289 + if (!base) { 290 + pr_warn("%s: Cannot map regs\n", np->full_name); 291 + error = -ENOMEM; 292 + goto out_put; 293 + } 294 + 295 + rcar_sysc_base = base; 296 + 297 + domains = kzalloc(sizeof(*domains), GFP_KERNEL); 298 + if (!domains) { 299 + error = -ENOMEM; 300 + goto out_put; 301 + } 302 + 303 + domains->onecell_data.domains = domains->domains; 304 + domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); 305 + 306 + for (i = 0, syscier = 0; i < info->num_areas; i++) 307 + syscier |= BIT(info->areas[i].isr_bit); 308 + 309 + /* 310 + * Mask all interrupt sources to prevent the CPU from receiving them. 311 + * Make sure not to clear reserved bits that were set before. 312 + */ 313 + syscimr = ioread32(base + SYSCIMR); 314 + syscimr |= syscier; 315 + pr_debug("%s: syscimr = 0x%08x\n", np->full_name, syscimr); 316 + iowrite32(syscimr, base + SYSCIMR); 317 + 318 + /* 319 + * SYSC needs all interrupt sources enabled to control power. 320 + */ 321 + pr_debug("%s: syscier = 0x%08x\n", np->full_name, syscier); 322 + iowrite32(syscier, base + SYSCIER); 323 + 324 + for (i = 0; i < info->num_areas; i++) { 325 + const struct rcar_sysc_area *area = &info->areas[i]; 326 + struct rcar_sysc_pd *pd; 327 + 328 + pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL); 329 + if (!pd) { 330 + error = -ENOMEM; 331 + goto out_put; 332 + } 333 + 334 + strcpy(pd->name, area->name); 335 + pd->genpd.name = pd->name; 336 + pd->ch.chan_offs = area->chan_offs; 337 + pd->ch.chan_bit = area->chan_bit; 338 + pd->ch.isr_bit = area->isr_bit; 339 + pd->flags = area->flags; 340 + 341 + rcar_sysc_pd_setup(pd); 342 + if (area->parent >= 0) 343 + pm_genpd_add_subdomain(domains->domains[area->parent], 344 + &pd->genpd); 345 + 346 + domains->domains[area->isr_bit] = &pd->genpd; 347 + } 348 + 349 + of_genpd_add_provider_onecell(np, &domains->onecell_data); 350 + 351 + out_put: 352 + of_node_put(np); 353 + return error; 354 + } 355 + early_initcall(rcar_sysc_pd_init);
+53
drivers/soc/renesas/rcar-sysc.h
··· 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 + 34 + struct 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 + 48 + struct rcar_sysc_info { 49 + const struct rcar_sysc_area *areas; 50 + unsigned int num_areas; 51 + }; 52 + 53 + #endif /* __SOC_RENESAS_RCAR_SYSC_H__ */