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

soc: renesas: r8a779a0-sysc: Add r8a779a0 support

Add support for R-Car V3U (R8A779A0) SoC power areas and register
access, because register specification differs from R-Car Gen2/3.

Inspired by patches in the BSP by Tho Vu.

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Link: https://lore.kernel.org/r/1599810232-29035-5-git-send-email-yoshihiro.shimoda.uh@renesas.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>

authored by

Yoshihiro Shimoda and committed by
Geert Uytterhoeven
1b4298f0 468588c8

+453
+4
drivers/soc/renesas/Kconfig
··· 247 247 config ARCH_R8A779A0 248 248 bool "ARM64 Platform support for R-Car V3U" 249 249 select ARCH_RCAR_GEN3 250 + select SYSC_R8A779A0 250 251 help 251 252 This enables support for the Renesas R-Car V3U SoC. 252 253 ··· 338 337 config SYSC_R8A77970 339 338 bool "System Controller support for R-Car V3M" if COMPILE_TEST 340 339 select SYSC_RCAR 340 + 341 + config SYSC_R8A779A0 342 + bool "System Controller support for R-Car V3U" if COMPILE_TEST 341 343 342 344 config SYSC_RMOBILE 343 345 bool "System Controller support for R-Mobile" if COMPILE_TEST
+1
drivers/soc/renesas/Makefile
··· 24 24 obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o 25 25 obj-$(CONFIG_SYSC_R8A77990) += r8a77990-sysc.o 26 26 obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o 27 + obj-$(CONFIG_SYSC_R8A779A0) += r8a779a0-sysc.o 27 28 ifdef CONFIG_SMP 28 29 obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o 29 30 endif
+448
drivers/soc/renesas/r8a779a0-sysc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Renesas R-Car V3U System Controller 4 + * 5 + * Copyright (C) 2020 Renesas Electronics Corp. 6 + */ 7 + 8 + #include <linux/bits.h> 9 + #include <linux/clk/renesas.h> 10 + #include <linux/delay.h> 11 + #include <linux/err.h> 12 + #include <linux/io.h> 13 + #include <linux/iopoll.h> 14 + #include <linux/kernel.h> 15 + #include <linux/mm.h> 16 + #include <linux/of_address.h> 17 + #include <linux/pm_domain.h> 18 + #include <linux/slab.h> 19 + #include <linux/spinlock.h> 20 + #include <linux/types.h> 21 + 22 + #include <dt-bindings/power/r8a779a0-sysc.h> 23 + 24 + /* 25 + * Power Domain flags 26 + */ 27 + #define PD_CPU BIT(0) /* Area contains main CPU core */ 28 + #define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ 29 + #define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ 30 + 31 + #define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR */ 32 + #define PD_ALWAYS_ON PD_NO_CR /* Always-on area */ 33 + 34 + /* 35 + * Description of a Power Area 36 + */ 37 + struct r8a779a0_sysc_area { 38 + const char *name; 39 + u8 pdr; /* PDRn */ 40 + int parent; /* -1 if none */ 41 + unsigned int flags; /* See PD_* */ 42 + }; 43 + 44 + /* 45 + * SoC-specific Power Area Description 46 + */ 47 + struct r8a779a0_sysc_info { 48 + const struct r8a779a0_sysc_area *areas; 49 + unsigned int num_areas; 50 + }; 51 + 52 + static struct r8a779a0_sysc_area r8a779a0_areas[] __initdata = { 53 + { "always-on", R8A779A0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, 54 + { "a3e0", R8A779A0_PD_A3E0, R8A779A0_PD_ALWAYS_ON, PD_SCU }, 55 + { "a3e1", R8A779A0_PD_A3E1, R8A779A0_PD_ALWAYS_ON, PD_SCU }, 56 + { "a2e0d0", R8A779A0_PD_A2E0D0, R8A779A0_PD_A3E0, PD_SCU }, 57 + { "a2e0d1", R8A779A0_PD_A2E0D1, R8A779A0_PD_A3E0, PD_SCU }, 58 + { "a2e1d0", R8A779A0_PD_A2E1D0, R8A779A0_PD_A3E1, PD_SCU }, 59 + { "a2e1d1", R8A779A0_PD_A2E1D1, R8A779A0_PD_A3E1, PD_SCU }, 60 + { "a1e0d0c0", R8A779A0_PD_A1E0D0C0, R8A779A0_PD_A2E0D0, PD_CPU_NOCR }, 61 + { "a1e0d0c1", R8A779A0_PD_A1E0D0C1, R8A779A0_PD_A2E0D0, PD_CPU_NOCR }, 62 + { "a1e0d1c0", R8A779A0_PD_A1E0D1C0, R8A779A0_PD_A2E0D1, PD_CPU_NOCR }, 63 + { "a1e0d1c1", R8A779A0_PD_A1E0D1C1, R8A779A0_PD_A2E0D1, PD_CPU_NOCR }, 64 + { "a1e1d0c0", R8A779A0_PD_A1E1D0C0, R8A779A0_PD_A2E1D0, PD_CPU_NOCR }, 65 + { "a1e1d0c1", R8A779A0_PD_A1E1D0C1, R8A779A0_PD_A2E1D0, PD_CPU_NOCR }, 66 + { "a1e1d1c0", R8A779A0_PD_A1E1D1C0, R8A779A0_PD_A2E1D1, PD_CPU_NOCR }, 67 + { "a1e1d1c1", R8A779A0_PD_A1E1D1C1, R8A779A0_PD_A2E1D1, PD_CPU_NOCR }, 68 + { "3dg-a", R8A779A0_PD_3DG_A, R8A779A0_PD_ALWAYS_ON }, 69 + { "3dg-b", R8A779A0_PD_3DG_B, R8A779A0_PD_3DG_A }, 70 + { "a3vip0", R8A779A0_PD_A3VIP0, R8A779A0_PD_ALWAYS_ON }, 71 + { "a3vip1", R8A779A0_PD_A3VIP1, R8A779A0_PD_ALWAYS_ON }, 72 + { "a3vip3", R8A779A0_PD_A3VIP3, R8A779A0_PD_ALWAYS_ON }, 73 + { "a3vip2", R8A779A0_PD_A3VIP2, R8A779A0_PD_ALWAYS_ON }, 74 + { "a3isp01", R8A779A0_PD_A3ISP01, R8A779A0_PD_ALWAYS_ON }, 75 + { "a3isp23", R8A779A0_PD_A3ISP23, R8A779A0_PD_ALWAYS_ON }, 76 + { "a3ir", R8A779A0_PD_A3IR, R8A779A0_PD_ALWAYS_ON }, 77 + { "a2cn0", R8A779A0_PD_A2CN0, R8A779A0_PD_A3IR }, 78 + { "a2imp01", R8A779A0_PD_A2IMP01, R8A779A0_PD_A3IR }, 79 + { "a2dp0", R8A779A0_PD_A2DP0, R8A779A0_PD_A3IR }, 80 + { "a2cv0", R8A779A0_PD_A2CV0, R8A779A0_PD_A3IR }, 81 + { "a2cv1", R8A779A0_PD_A2CV1, R8A779A0_PD_A3IR }, 82 + { "a2cv4", R8A779A0_PD_A2CV4, R8A779A0_PD_A3IR }, 83 + { "a2cv6", R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR }, 84 + { "a2cn2", R8A779A0_PD_A2CN2, R8A779A0_PD_A3IR }, 85 + { "a2imp23", R8A779A0_PD_A2IMP23, R8A779A0_PD_A3IR }, 86 + { "a2dp1", R8A779A0_PD_A2DP0, R8A779A0_PD_A3IR }, 87 + { "a2cv2", R8A779A0_PD_A2CV0, R8A779A0_PD_A3IR }, 88 + { "a2cv3", R8A779A0_PD_A2CV1, R8A779A0_PD_A3IR }, 89 + { "a2cv5", R8A779A0_PD_A2CV4, R8A779A0_PD_A3IR }, 90 + { "a2cv7", R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR }, 91 + { "a2cn1", R8A779A0_PD_A2CN1, R8A779A0_PD_A3IR }, 92 + { "a1cnn0", R8A779A0_PD_A1CNN0, R8A779A0_PD_A2CN0 }, 93 + { "a1cnn2", R8A779A0_PD_A1CNN2, R8A779A0_PD_A2CN2 }, 94 + { "a1dsp0", R8A779A0_PD_A1DSP0, R8A779A0_PD_A2CN2 }, 95 + { "a1cnn1", R8A779A0_PD_A1CNN1, R8A779A0_PD_A2CN1 }, 96 + { "a1dsp1", R8A779A0_PD_A1DSP1, R8A779A0_PD_A2CN1 }, 97 + }; 98 + 99 + static const struct r8a779a0_sysc_info r8a779a0_sysc_info __initconst = { 100 + .areas = r8a779a0_areas, 101 + .num_areas = ARRAY_SIZE(r8a779a0_areas), 102 + }; 103 + 104 + /* SYSC Common */ 105 + #define SYSCSR 0x000 /* SYSC Status Register */ 106 + #define SYSCPONSR(x) (0x800 + ((x) * 0x4)) /* Power-ON Status Register 0 */ 107 + #define SYSCPOFFSR(x) (0x808 + ((x) * 0x4)) /* Power-OFF Status Register */ 108 + #define SYSCISCR(x) (0x810 + ((x) * 0x4)) /* Interrupt Status/Clear Register */ 109 + #define SYSCIER(x) (0x820 + ((x) * 0x4)) /* Interrupt Enable Register */ 110 + #define SYSCIMR(x) (0x830 + ((x) * 0x4)) /* Interrupt Mask Register */ 111 + 112 + /* Power Domain Registers */ 113 + #define PDRSR(n) (0x1000 + ((n) * 0x40)) 114 + #define PDRONCR(n) (0x1004 + ((n) * 0x40)) 115 + #define PDROFFCR(n) (0x1008 + ((n) * 0x40)) 116 + #define PDRESR(n) (0x100C + ((n) * 0x40)) 117 + 118 + /* PWRON/PWROFF */ 119 + #define PWRON_PWROFF BIT(0) /* Power-ON/OFF request */ 120 + 121 + /* PDRESR */ 122 + #define PDRESR_ERR BIT(0) 123 + 124 + /* PDRSR */ 125 + #define PDRSR_OFF BIT(0) /* Power-OFF state */ 126 + #define PDRSR_ON BIT(4) /* Power-ON state */ 127 + #define PDRSR_OFF_STATE BIT(8) /* Processing Power-OFF sequence */ 128 + #define PDRSR_ON_STATE BIT(12) /* Processing Power-ON sequence */ 129 + 130 + #define SYSCSR_BUSY GENMASK(1, 0) /* All bit sets is not busy */ 131 + 132 + #define SYSCSR_TIMEOUT 10000 133 + #define SYSCSR_DELAY_US 10 134 + 135 + #define PDRESR_RETRIES 1000 136 + #define PDRESR_DELAY_US 10 137 + 138 + #define SYSCISR_TIMEOUT 10000 139 + #define SYSCISR_DELAY_US 10 140 + 141 + #define NUM_DOMAINS_EACH_REG BITS_PER_TYPE(u32) 142 + 143 + static void __iomem *r8a779a0_sysc_base; 144 + static DEFINE_SPINLOCK(r8a779a0_sysc_lock); /* SMP CPUs + I/O devices */ 145 + 146 + static int r8a779a0_sysc_pwr_on_off(u8 pdr, bool on) 147 + { 148 + unsigned int reg_offs; 149 + u32 val; 150 + int ret; 151 + 152 + if (on) 153 + reg_offs = PDRONCR(pdr); 154 + else 155 + reg_offs = PDROFFCR(pdr); 156 + 157 + /* Wait until SYSC is ready to accept a power request */ 158 + ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCSR, val, 159 + (val & SYSCSR_BUSY) == SYSCSR_BUSY, 160 + SYSCSR_DELAY_US, SYSCSR_TIMEOUT); 161 + if (ret < 0) 162 + return -EAGAIN; 163 + 164 + /* Submit power shutoff or power resume request */ 165 + iowrite32(PWRON_PWROFF, r8a779a0_sysc_base + reg_offs); 166 + 167 + return 0; 168 + } 169 + 170 + static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask) 171 + { 172 + u32 val; 173 + int ret; 174 + 175 + iowrite32(isr_mask, r8a779a0_sysc_base + SYSCISCR(reg_idx)); 176 + 177 + ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCISCR(reg_idx), 178 + val, !(val & isr_mask), 179 + SYSCISR_DELAY_US, SYSCISR_TIMEOUT); 180 + if (ret < 0) { 181 + pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__); 182 + return -EIO; 183 + } 184 + 185 + return 0; 186 + } 187 + 188 + static int r8a779a0_sysc_power(u8 pdr, bool on) 189 + { 190 + unsigned int isr_mask; 191 + unsigned int reg_idx, bit_idx; 192 + unsigned int status; 193 + unsigned long flags; 194 + int ret = 0; 195 + u32 val; 196 + int k; 197 + 198 + spin_lock_irqsave(&r8a779a0_sysc_lock, flags); 199 + 200 + reg_idx = pdr / NUM_DOMAINS_EACH_REG; 201 + bit_idx = pdr % NUM_DOMAINS_EACH_REG; 202 + 203 + isr_mask = BIT(bit_idx); 204 + 205 + /* 206 + * The interrupt source needs to be enabled, but masked, to prevent the 207 + * CPU from receiving it. 208 + */ 209 + iowrite32(ioread32(r8a779a0_sysc_base + SYSCIER(reg_idx)) | isr_mask, 210 + r8a779a0_sysc_base + SYSCIER(reg_idx)); 211 + iowrite32(ioread32(r8a779a0_sysc_base + SYSCIMR(reg_idx)) | isr_mask, 212 + r8a779a0_sysc_base + SYSCIMR(reg_idx)); 213 + 214 + ret = clear_irq_flags(reg_idx, isr_mask); 215 + if (ret) 216 + goto out; 217 + 218 + /* Submit power shutoff or resume request until it was accepted */ 219 + for (k = 0; k < PDRESR_RETRIES; k++) { 220 + ret = r8a779a0_sysc_pwr_on_off(pdr, on); 221 + if (ret) 222 + goto out; 223 + 224 + status = ioread32(r8a779a0_sysc_base + PDRESR(pdr)); 225 + if (!(status & PDRESR_ERR)) 226 + break; 227 + 228 + udelay(PDRESR_DELAY_US); 229 + } 230 + 231 + if (k == PDRESR_RETRIES) { 232 + ret = -EIO; 233 + goto out; 234 + } 235 + 236 + /* Wait until the power shutoff or resume request has completed * */ 237 + ret = readl_poll_timeout_atomic(r8a779a0_sysc_base + SYSCISCR(reg_idx), 238 + val, (val & isr_mask), 239 + SYSCISR_DELAY_US, SYSCISR_TIMEOUT); 240 + if (ret < 0) { 241 + ret = -EIO; 242 + goto out; 243 + } 244 + 245 + /* Clear interrupt flags */ 246 + ret = clear_irq_flags(reg_idx, isr_mask); 247 + if (ret) 248 + goto out; 249 + 250 + out: 251 + spin_unlock_irqrestore(&r8a779a0_sysc_lock, flags); 252 + 253 + pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", 254 + pdr, ioread32(r8a779a0_sysc_base + SYSCISCR(reg_idx)), ret); 255 + return ret; 256 + } 257 + 258 + static bool r8a779a0_sysc_power_is_off(u8 pdr) 259 + { 260 + unsigned int st; 261 + 262 + st = ioread32(r8a779a0_sysc_base + PDRSR(pdr)); 263 + 264 + if (st & PDRSR_OFF) 265 + return true; 266 + 267 + return false; 268 + } 269 + 270 + struct r8a779a0_sysc_pd { 271 + struct generic_pm_domain genpd; 272 + u8 pdr; 273 + unsigned int flags; 274 + char name[]; 275 + }; 276 + 277 + static inline struct r8a779a0_sysc_pd *to_r8a779a0_pd(struct generic_pm_domain *d) 278 + { 279 + return container_of(d, struct r8a779a0_sysc_pd, genpd); 280 + } 281 + 282 + static int r8a779a0_sysc_pd_power_off(struct generic_pm_domain *genpd) 283 + { 284 + struct r8a779a0_sysc_pd *pd = to_r8a779a0_pd(genpd); 285 + 286 + pr_debug("%s: %s\n", __func__, genpd->name); 287 + return r8a779a0_sysc_power(pd->pdr, false); 288 + } 289 + 290 + static int r8a779a0_sysc_pd_power_on(struct generic_pm_domain *genpd) 291 + { 292 + struct r8a779a0_sysc_pd *pd = to_r8a779a0_pd(genpd); 293 + 294 + pr_debug("%s: %s\n", __func__, genpd->name); 295 + return r8a779a0_sysc_power(pd->pdr, true); 296 + } 297 + 298 + static int __init r8a779a0_sysc_pd_setup(struct r8a779a0_sysc_pd *pd) 299 + { 300 + struct generic_pm_domain *genpd = &pd->genpd; 301 + const char *name = pd->genpd.name; 302 + int error; 303 + 304 + if (pd->flags & PD_CPU) { 305 + /* 306 + * This domain contains a CPU core and therefore it should 307 + * only be turned off if the CPU is not in use. 308 + */ 309 + pr_debug("PM domain %s contains %s\n", name, "CPU"); 310 + genpd->flags |= GENPD_FLAG_ALWAYS_ON; 311 + } else if (pd->flags & PD_SCU) { 312 + /* 313 + * This domain contains an SCU and cache-controller, and 314 + * therefore it should only be turned off if the CPU cores are 315 + * not in use. 316 + */ 317 + pr_debug("PM domain %s contains %s\n", name, "SCU"); 318 + genpd->flags |= GENPD_FLAG_ALWAYS_ON; 319 + } else if (pd->flags & PD_NO_CR) { 320 + /* 321 + * This domain cannot be turned off. 322 + */ 323 + genpd->flags |= GENPD_FLAG_ALWAYS_ON; 324 + } 325 + 326 + if (!(pd->flags & (PD_CPU | PD_SCU))) { 327 + /* Enable Clock Domain for I/O devices */ 328 + genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; 329 + genpd->attach_dev = cpg_mssr_attach_dev; 330 + genpd->detach_dev = cpg_mssr_detach_dev; 331 + } 332 + 333 + genpd->power_off = r8a779a0_sysc_pd_power_off; 334 + genpd->power_on = r8a779a0_sysc_pd_power_on; 335 + 336 + if (pd->flags & (PD_CPU | PD_NO_CR)) { 337 + /* Skip CPUs (handled by SMP code) and areas without control */ 338 + pr_debug("%s: Not touching %s\n", __func__, genpd->name); 339 + goto finalize; 340 + } 341 + 342 + if (!r8a779a0_sysc_power_is_off(pd->pdr)) { 343 + pr_debug("%s: %s is already powered\n", __func__, genpd->name); 344 + goto finalize; 345 + } 346 + 347 + r8a779a0_sysc_power(pd->pdr, true); 348 + 349 + finalize: 350 + error = pm_genpd_init(genpd, &simple_qos_governor, false); 351 + if (error) 352 + pr_err("Failed to init PM domain %s: %d\n", name, error); 353 + 354 + return error; 355 + } 356 + 357 + static const struct of_device_id r8a779a0_sysc_matches[] __initconst = { 358 + { .compatible = "renesas,r8a779a0-sysc", .data = &r8a779a0_sysc_info }, 359 + { /* sentinel */ } 360 + }; 361 + 362 + struct r8a779a0_pm_domains { 363 + struct genpd_onecell_data onecell_data; 364 + struct generic_pm_domain *domains[R8A779A0_PD_ALWAYS_ON + 1]; 365 + }; 366 + 367 + static struct genpd_onecell_data *r8a779a0_sysc_onecell_data; 368 + 369 + static int __init r8a779a0_sysc_pd_init(void) 370 + { 371 + const struct r8a779a0_sysc_info *info; 372 + const struct of_device_id *match; 373 + struct r8a779a0_pm_domains *domains; 374 + struct device_node *np; 375 + void __iomem *base; 376 + unsigned int i; 377 + int error; 378 + 379 + np = of_find_matching_node_and_match(NULL, r8a779a0_sysc_matches, &match); 380 + if (!np) 381 + return -ENODEV; 382 + 383 + info = match->data; 384 + 385 + base = of_iomap(np, 0); 386 + if (!base) { 387 + pr_warn("%pOF: Cannot map regs\n", np); 388 + error = -ENOMEM; 389 + goto out_put; 390 + } 391 + 392 + r8a779a0_sysc_base = base; 393 + 394 + domains = kzalloc(sizeof(*domains), GFP_KERNEL); 395 + if (!domains) { 396 + error = -ENOMEM; 397 + goto out_put; 398 + } 399 + 400 + domains->onecell_data.domains = domains->domains; 401 + domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); 402 + r8a779a0_sysc_onecell_data = &domains->onecell_data; 403 + 404 + for (i = 0; i < info->num_areas; i++) { 405 + const struct r8a779a0_sysc_area *area = &info->areas[i]; 406 + struct r8a779a0_sysc_pd *pd; 407 + 408 + if (!area->name) { 409 + /* Skip NULLified area */ 410 + continue; 411 + } 412 + 413 + pd = kzalloc(sizeof(*pd) + strlen(area->name) + 1, GFP_KERNEL); 414 + if (!pd) { 415 + error = -ENOMEM; 416 + goto out_put; 417 + } 418 + 419 + strcpy(pd->name, area->name); 420 + pd->genpd.name = pd->name; 421 + pd->pdr = area->pdr; 422 + pd->flags = area->flags; 423 + 424 + error = r8a779a0_sysc_pd_setup(pd); 425 + if (error) 426 + goto out_put; 427 + 428 + domains->domains[area->pdr] = &pd->genpd; 429 + 430 + if (area->parent < 0) 431 + continue; 432 + 433 + error = pm_genpd_add_subdomain(domains->domains[area->parent], 434 + &pd->genpd); 435 + if (error) { 436 + pr_warn("Failed to add PM subdomain %s to parent %u\n", 437 + area->name, area->parent); 438 + goto out_put; 439 + } 440 + } 441 + 442 + error = of_genpd_add_provider_onecell(np, &domains->onecell_data); 443 + 444 + out_put: 445 + of_node_put(np); 446 + return error; 447 + } 448 + early_initcall(r8a779a0_sysc_pd_init);