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

ARM: sun9i: smp: Support CPU/cluster power down and hotplugging for cpu1~7

This patch adds common code used to power down all cores and clusters.
The code was previously based on the MCPM framework. It has now been
adapted to hook into struct smp_operations directly, but the code
structure still shows signs of prior work.

The primary core (cpu0) requires setting flags to have the BROM bounce
execution to the SMP software entry code. This is done in a subsequent
patch to keep the changes cleanly separated. By default the ARM SMP code
blocks cpu0 from being turned off, so splitting this out is safe.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>

+189 -1
+189 -1
arch/arm/mach-sunxi/mc_smp.c
··· 15 15 #include <linux/cpu_pm.h> 16 16 #include <linux/delay.h> 17 17 #include <linux/io.h> 18 + #include <linux/iopoll.h> 19 + #include <linux/irqchip/arm-gic.h> 18 20 #include <linux/of.h> 19 21 #include <linux/of_address.h> 20 22 #include <linux/of_device.h> ··· 32 30 #define SUNXI_CPUS_PER_CLUSTER 4 33 31 #define SUNXI_NR_CLUSTERS 2 34 32 33 + #define POLL_USEC 100 34 + #define TIMEOUT_USEC 100000 35 + 35 36 #define CPUCFG_CX_CTRL_REG0(c) (0x10 * (c)) 36 37 #define CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE(n) BIT(n) 37 38 #define CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE_ALL 0xf ··· 42 37 #define CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A15 BIT(0) 43 38 #define CPUCFG_CX_CTRL_REG1(c) (0x10 * (c) + 0x4) 44 39 #define CPUCFG_CX_CTRL_REG1_ACINACTM BIT(0) 40 + #define CPUCFG_CX_STATUS(c) (0x30 + 0x4 * (c)) 41 + #define CPUCFG_CX_STATUS_STANDBYWFI(n) BIT(16 + (n)) 42 + #define CPUCFG_CX_STATUS_STANDBYWFIL2 BIT(0) 45 43 #define CPUCFG_CX_RST_CTRL(c) (0x80 + 0x4 * (c)) 46 44 #define CPUCFG_CX_RST_CTRL_DBG_SOC_RST BIT(24) 47 45 #define CPUCFG_CX_RST_CTRL_ETM_RST(n) BIT(20 + (n)) ··· 129 121 { 130 122 u32 reg; 131 123 132 - pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); 124 + pr_debug("%s: cluster %u cpu %u\n", __func__, cluster, cpu); 133 125 if (cpu >= SUNXI_CPUS_PER_CLUSTER || cluster >= SUNXI_NR_CLUSTERS) 134 126 return -EINVAL; 135 127 ··· 398 390 return 0; 399 391 } 400 392 393 + #ifdef CONFIG_HOTPLUG_CPU 394 + static void sunxi_cluster_cache_disable(void) 395 + { 396 + unsigned int cluster = MPIDR_AFFINITY_LEVEL(read_cpuid_mpidr(), 1); 397 + u32 reg; 398 + 399 + pr_debug("%s: cluster %u\n", __func__, cluster); 400 + 401 + sunxi_cluster_cache_disable_without_axi(); 402 + 403 + /* last man standing, assert ACINACTM */ 404 + reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster)); 405 + reg |= CPUCFG_CX_CTRL_REG1_ACINACTM; 406 + writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster)); 407 + } 408 + 409 + static void sunxi_mc_smp_cpu_die(unsigned int l_cpu) 410 + { 411 + unsigned int mpidr, cpu, cluster; 412 + bool last_man; 413 + 414 + mpidr = cpu_logical_map(l_cpu); 415 + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 416 + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 417 + pr_debug("%s: cluster %u cpu %u\n", __func__, cluster, cpu); 418 + 419 + spin_lock(&boot_lock); 420 + sunxi_mc_smp_cpu_table[cluster][cpu]--; 421 + if (sunxi_mc_smp_cpu_table[cluster][cpu] == 1) { 422 + /* A power_up request went ahead of us. */ 423 + pr_debug("%s: aborting due to a power up request\n", 424 + __func__); 425 + spin_unlock(&boot_lock); 426 + return; 427 + } else if (sunxi_mc_smp_cpu_table[cluster][cpu] > 1) { 428 + pr_err("Cluster %d CPU%d boots multiple times\n", 429 + cluster, cpu); 430 + BUG(); 431 + } 432 + 433 + last_man = sunxi_mc_smp_cluster_is_down(cluster); 434 + spin_unlock(&boot_lock); 435 + 436 + gic_cpu_if_down(0); 437 + if (last_man) 438 + sunxi_cluster_cache_disable(); 439 + else 440 + v7_exit_coherency_flush(louis); 441 + 442 + for (;;) 443 + wfi(); 444 + } 445 + 446 + static int sunxi_cpu_powerdown(unsigned int cpu, unsigned int cluster) 447 + { 448 + u32 reg; 449 + 450 + pr_debug("%s: cluster %u cpu %u\n", __func__, cluster, cpu); 451 + if (cpu >= SUNXI_CPUS_PER_CLUSTER || cluster >= SUNXI_NR_CLUSTERS) 452 + return -EINVAL; 453 + 454 + /* gate processor power */ 455 + reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster)); 456 + reg |= PRCM_PWROFF_GATING_REG_CORE(cpu); 457 + writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster)); 458 + udelay(20); 459 + 460 + /* close power switch */ 461 + sunxi_cpu_power_switch_set(cpu, cluster, false); 462 + 463 + return 0; 464 + } 465 + 466 + static int sunxi_cluster_powerdown(unsigned int cluster) 467 + { 468 + u32 reg; 469 + 470 + pr_debug("%s: cluster %u\n", __func__, cluster); 471 + if (cluster >= SUNXI_NR_CLUSTERS) 472 + return -EINVAL; 473 + 474 + /* assert cluster resets or system will hang */ 475 + pr_debug("%s: assert cluster reset\n", __func__); 476 + reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); 477 + reg &= ~CPUCFG_CX_RST_CTRL_DBG_SOC_RST; 478 + reg &= ~CPUCFG_CX_RST_CTRL_H_RST; 479 + reg &= ~CPUCFG_CX_RST_CTRL_L2_RST; 480 + writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster)); 481 + 482 + /* gate cluster power */ 483 + pr_debug("%s: gate cluster power\n", __func__); 484 + reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster)); 485 + reg |= PRCM_PWROFF_GATING_REG_CLUSTER; 486 + writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster)); 487 + udelay(20); 488 + 489 + return 0; 490 + } 491 + 492 + static int sunxi_mc_smp_cpu_kill(unsigned int l_cpu) 493 + { 494 + unsigned int mpidr, cpu, cluster; 495 + unsigned int tries, count; 496 + int ret = 0; 497 + u32 reg; 498 + 499 + mpidr = cpu_logical_map(l_cpu); 500 + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 501 + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 502 + 503 + /* This should never happen */ 504 + if (WARN_ON(cluster >= SUNXI_NR_CLUSTERS || 505 + cpu >= SUNXI_CPUS_PER_CLUSTER)) 506 + return 0; 507 + 508 + /* wait for CPU core to die and enter WFI */ 509 + count = TIMEOUT_USEC / POLL_USEC; 510 + spin_lock_irq(&boot_lock); 511 + for (tries = 0; tries < count; tries++) { 512 + spin_unlock_irq(&boot_lock); 513 + usleep_range(POLL_USEC / 2, POLL_USEC); 514 + spin_lock_irq(&boot_lock); 515 + 516 + /* 517 + * If the user turns off a bunch of cores at the same 518 + * time, the kernel might call cpu_kill before some of 519 + * them are ready. This is because boot_lock serializes 520 + * both cpu_die and cpu_kill callbacks. Either one could 521 + * run first. We should wait for cpu_die to complete. 522 + */ 523 + if (sunxi_mc_smp_cpu_table[cluster][cpu]) 524 + continue; 525 + 526 + reg = readl(cpucfg_base + CPUCFG_CX_STATUS(cluster)); 527 + if (reg & CPUCFG_CX_STATUS_STANDBYWFI(cpu)) 528 + break; 529 + } 530 + 531 + if (tries >= count) { 532 + ret = ETIMEDOUT; 533 + goto out; 534 + } 535 + 536 + /* power down CPU core */ 537 + sunxi_cpu_powerdown(cpu, cluster); 538 + 539 + if (!sunxi_mc_smp_cluster_is_down(cluster)) 540 + goto out; 541 + 542 + /* wait for cluster L2 WFI */ 543 + ret = readl_poll_timeout(cpucfg_base + CPUCFG_CX_STATUS(cluster), reg, 544 + reg & CPUCFG_CX_STATUS_STANDBYWFIL2, 545 + POLL_USEC, TIMEOUT_USEC); 546 + if (ret) { 547 + /* 548 + * Ignore timeout on the cluster. Leaving the cluster on 549 + * will not affect system execution, just use a bit more 550 + * power. But returning an error here will only confuse 551 + * the user as the CPU has already been shutdown. 552 + */ 553 + ret = 0; 554 + goto out; 555 + } 556 + 557 + /* Power down cluster */ 558 + sunxi_cluster_powerdown(cluster); 559 + 560 + out: 561 + spin_unlock_irq(&boot_lock); 562 + pr_debug("%s: cluster %u cpu %u powerdown: %d\n", 563 + __func__, cluster, cpu, ret); 564 + return !ret; 565 + } 566 + 567 + #endif 568 + 401 569 static const struct smp_operations sunxi_mc_smp_smp_ops __initconst = { 402 570 .smp_boot_secondary = sunxi_mc_smp_boot_secondary, 571 + #ifdef CONFIG_HOTPLUG_CPU 572 + .cpu_die = sunxi_mc_smp_cpu_die, 573 + .cpu_kill = sunxi_mc_smp_cpu_kill, 574 + #endif 403 575 }; 404 576 405 577 static bool __init sunxi_mc_smp_cpu_table_init(void)