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

ARM: vexpress: introduce DCSCB support

This adds basic CPU and cluster reset controls on RTSM for the
A15x4-A7x4 model configuration using the Dual Cluster System
Configuration Block (DCSCB).

The cache coherency interconnect (CCI) is not handled yet.

Signed-off-by: Nicolas Pitre <nico@linaro.org>
Reviewed-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: Pawel Moll <pawel.moll@arm.com>

+192
+19
Documentation/devicetree/bindings/arm/rtsm-dcscb.txt
··· 1 + ARM Dual Cluster System Configuration Block 2 + ------------------------------------------- 3 + 4 + The Dual Cluster System Configuration Block (DCSCB) provides basic 5 + functionality for controlling clocks, resets and configuration pins in 6 + the Dual Cluster System implemented by the Real-Time System Model (RTSM). 7 + 8 + Required properties: 9 + 10 + - compatible : should be "arm,rtsm,dcscb" 11 + 12 + - reg : physical base address and the size of the registers window 13 + 14 + Example: 15 + 16 + dcscb@60000000 { 17 + compatible = "arm,rtsm,dcscb"; 18 + reg = <0x60000000 0x1000>; 19 + };
+8
arch/arm/mach-vexpress/Kconfig
··· 57 57 config ARCH_VEXPRESS_CA9X4 58 58 bool "Versatile Express Cortex-A9x4 tile" 59 59 60 + config ARCH_VEXPRESS_DCSCB 61 + bool "Dual Cluster System Control Block (DCSCB) support" 62 + depends on MCPM 63 + help 64 + Support for the Dual Cluster System Configuration Block (DCSCB). 65 + This is needed to provide CPU and cluster power management 66 + on RTSM implementing big.LITTLE. 67 + 60 68 endmenu
+1
arch/arm/mach-vexpress/Makefile
··· 6 6 7 7 obj-y := v2m.o 8 8 obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o 9 + obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o 9 10 obj-$(CONFIG_SMP) += platsmp.o 10 11 obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+164
arch/arm/mach-vexpress/dcscb.c
··· 1 + /* 2 + * arch/arm/mach-vexpress/dcscb.c - Dual Cluster System Configuration Block 3 + * 4 + * Created by: Nicolas Pitre, May 2012 5 + * Copyright: (C) 2012-2013 Linaro Limited 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #include <linux/init.h> 13 + #include <linux/kernel.h> 14 + #include <linux/io.h> 15 + #include <linux/spinlock.h> 16 + #include <linux/errno.h> 17 + #include <linux/of_address.h> 18 + #include <linux/vexpress.h> 19 + 20 + #include <asm/mcpm.h> 21 + #include <asm/proc-fns.h> 22 + #include <asm/cacheflush.h> 23 + #include <asm/cputype.h> 24 + #include <asm/cp15.h> 25 + 26 + 27 + #define RST_HOLD0 0x0 28 + #define RST_HOLD1 0x4 29 + #define SYS_SWRESET 0x8 30 + #define RST_STAT0 0xc 31 + #define RST_STAT1 0x10 32 + #define EAG_CFG_R 0x20 33 + #define EAG_CFG_W 0x24 34 + #define KFC_CFG_R 0x28 35 + #define KFC_CFG_W 0x2c 36 + #define DCS_CFG_R 0x30 37 + 38 + /* 39 + * We can't use regular spinlocks. In the switcher case, it is possible 40 + * for an outbound CPU to call power_down() while its inbound counterpart 41 + * is already live using the same logical CPU number which trips lockdep 42 + * debugging. 43 + */ 44 + static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED; 45 + 46 + static void __iomem *dcscb_base; 47 + 48 + static int dcscb_power_up(unsigned int cpu, unsigned int cluster) 49 + { 50 + unsigned int rst_hold, cpumask = (1 << cpu); 51 + 52 + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); 53 + if (cpu >= 4 || cluster >= 2) 54 + return -EINVAL; 55 + 56 + /* 57 + * Since this is called with IRQs enabled, and no arch_spin_lock_irq 58 + * variant exists, we need to disable IRQs manually here. 59 + */ 60 + local_irq_disable(); 61 + arch_spin_lock(&dcscb_lock); 62 + 63 + rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); 64 + if (rst_hold & (1 << 8)) { 65 + /* remove cluster reset and add individual CPU's reset */ 66 + rst_hold &= ~(1 << 8); 67 + rst_hold |= 0xf; 68 + } 69 + rst_hold &= ~(cpumask | (cpumask << 4)); 70 + writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); 71 + 72 + arch_spin_unlock(&dcscb_lock); 73 + local_irq_enable(); 74 + 75 + return 0; 76 + } 77 + 78 + static void dcscb_power_down(void) 79 + { 80 + unsigned int mpidr, cpu, cluster, rst_hold, cpumask, last_man; 81 + 82 + mpidr = read_cpuid_mpidr(); 83 + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 84 + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 85 + cpumask = (1 << cpu); 86 + 87 + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); 88 + BUG_ON(cpu >= 4 || cluster >= 2); 89 + 90 + arch_spin_lock(&dcscb_lock); 91 + rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); 92 + rst_hold |= cpumask; 93 + if (((rst_hold | (rst_hold >> 4)) & 0xf) == 0xf) 94 + rst_hold |= (1 << 8); 95 + writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); 96 + arch_spin_unlock(&dcscb_lock); 97 + last_man = (rst_hold & (1 << 8)); 98 + 99 + /* 100 + * Now let's clean our L1 cache and shut ourself down. 101 + * If we're the last CPU in this cluster then clean L2 too. 102 + */ 103 + 104 + /* 105 + * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need 106 + * a preliminary flush here for those CPUs. At least, that's 107 + * the theory -- without the extra flush, Linux explodes on 108 + * RTSM (to be investigated).. 109 + */ 110 + flush_cache_louis(); 111 + set_cr(get_cr() & ~CR_C); 112 + 113 + if (!last_man) { 114 + flush_cache_louis(); 115 + } else { 116 + flush_cache_all(); 117 + outer_flush_all(); 118 + } 119 + 120 + /* Disable local coherency by clearing the ACTLR "SMP" bit: */ 121 + set_auxcr(get_auxcr() & ~(1 << 6)); 122 + 123 + /* Now we are prepared for power-down, do it: */ 124 + dsb(); 125 + wfi(); 126 + 127 + /* Not dead at this point? Let our caller cope. */ 128 + } 129 + 130 + static const struct mcpm_platform_ops dcscb_power_ops = { 131 + .power_up = dcscb_power_up, 132 + .power_down = dcscb_power_down, 133 + }; 134 + 135 + static int __init dcscb_init(void) 136 + { 137 + struct device_node *node; 138 + int ret; 139 + 140 + node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb"); 141 + if (!node) 142 + return -ENODEV; 143 + dcscb_base = of_iomap(node, 0); 144 + if (!dcscb_base) 145 + return -EADDRNOTAVAIL; 146 + 147 + ret = mcpm_platform_register(&dcscb_power_ops); 148 + if (ret) { 149 + iounmap(dcscb_base); 150 + return ret; 151 + } 152 + 153 + pr_info("VExpress DCSCB support installed\n"); 154 + 155 + /* 156 + * Future entries into the kernel can now go 157 + * through the cluster entry vectors. 158 + */ 159 + vexpress_flags_set(virt_to_phys(mcpm_entry_point)); 160 + 161 + return 0; 162 + } 163 + 164 + early_initcall(dcscb_init);