diff options
author | Scott Wood <scottwood@freescale.com> | 2014-04-07 23:49:35 (GMT) |
---|---|---|
committer | Scott Wood <scottwood@freescale.com> | 2014-04-07 23:49:35 (GMT) |
commit | 62b8c978ee6b8d135d9e7953221de58000dba986 (patch) | |
tree | 683b04b2e627f6710c22c151b23c8cc9a165315e /arch/arm/mach-vexpress | |
parent | 78fd82238d0e5716578c326404184a27ba67fd6e (diff) | |
download | linux-fsl-qoriq-62b8c978ee6b8d135d9e7953221de58000dba986.tar.xz |
Rewind v3.13-rc3+ (78fd82238d0e5716) to v3.12
Diffstat (limited to 'arch/arm/mach-vexpress')
-rw-r--r-- | arch/arm/mach-vexpress/Kconfig | 15 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/dcscb.c | 56 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/spc.c | 406 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/spc.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/tc2_pm.c | 121 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/v2m.c | 14 |
7 files changed, 123 insertions, 495 deletions
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 4a70be4..3657954 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -1,16 +1,17 @@ config ARCH_VEXPRESS bool "ARM Ltd. Versatile Express family" if ARCH_MULTI_V7 select ARCH_REQUIRE_GPIOLIB - select ARCH_SUPPORTS_BIG_ENDIAN select ARM_AMBA select ARM_GIC select ARM_TIMER_SP804 + select CLKDEV_LOOKUP select COMMON_CLK select COMMON_CLK_VERSATILE select CPU_V7 select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD if SMP + select HAVE_CLK select HAVE_PATA_PLATFORM select HAVE_SMP select ICST @@ -65,22 +66,10 @@ config ARCH_VEXPRESS_DCSCB This is needed to provide CPU and cluster power management on RTSM implementing big.LITTLE. -config ARCH_VEXPRESS_SPC - bool "Versatile Express Serial Power Controller (SPC)" - select ARCH_HAS_CPUFREQ - select ARCH_HAS_OPP - select PM_OPP - help - The TC2 (A15x2 A7x3) versatile express core tile integrates a logic - block called Serial Power Controller (SPC) that provides the interface - between the dual cluster test-chip and the M3 microcontroller that - carries out power management. - config ARCH_VEXPRESS_TC2_PM bool "Versatile Express TC2 power management" depends on MCPM select ARM_CCI - select ARCH_VEXPRESS_SPC help Support for CPU and cluster power management on Versatile Express with a TC2 (A15x2 A7x3) big.LITTLE core tile. diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile index 0997e0b..505e64a 100644 --- a/arch/arm/mach-vexpress/Makefile +++ b/arch/arm/mach-vexpress/Makefile @@ -8,8 +8,7 @@ obj-y := v2m.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o CFLAGS_dcscb.o += -march=armv7-a -obj-$(CONFIG_ARCH_VEXPRESS_SPC) += spc.o -obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += tc2_pm.o +obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += tc2_pm.o spc.o CFLAGS_tc2_pm.o += -march=armv7-a obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c index 14d4996..3a6384c 100644 --- a/arch/arm/mach-vexpress/dcscb.c +++ b/arch/arm/mach-vexpress/dcscb.c @@ -133,8 +133,38 @@ static void dcscb_power_down(void) if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { arch_spin_unlock(&dcscb_lock); - /* Flush all cache levels for this cluster. */ - v7_exit_coherency_flush(all); + /* + * Flush all cache levels for this cluster. + * + * To do so we do: + * - Clear the SCTLR.C bit to prevent further cache allocations + * - Flush the whole cache + * - Clear the ACTLR "SMP" bit to disable local coherency + * + * Let's do it in the safest possible way i.e. with + * no memory access within the following sequence + * including to the stack. + * + * Note: fp is preserved to the stack explicitly prior doing + * this since adding it to the clobber list is incompatible + * with having CONFIG_FRAME_POINTER=y. + */ + asm volatile( + "str fp, [sp, #-4]! \n\t" + "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t" + "bic r0, r0, #"__stringify(CR_C)" \n\t" + "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t" + "isb \n\t" + "bl v7_flush_dcache_all \n\t" + "clrex \n\t" + "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t" + "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" + "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t" + "isb \n\t" + "dsb \n\t" + "ldr fp, [sp], #4" + : : : "r0","r1","r2","r3","r4","r5","r6","r7", + "r9","r10","lr","memory"); /* * This is a harmless no-op. On platforms with a real @@ -153,8 +183,26 @@ static void dcscb_power_down(void) } else { arch_spin_unlock(&dcscb_lock); - /* Disable and flush the local CPU cache. */ - v7_exit_coherency_flush(louis); + /* + * Flush the local CPU cache. + * Let's do it in the safest possible way as above. + */ + asm volatile( + "str fp, [sp, #-4]! \n\t" + "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t" + "bic r0, r0, #"__stringify(CR_C)" \n\t" + "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t" + "isb \n\t" + "bl v7_flush_dcache_louis \n\t" + "clrex \n\t" + "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t" + "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" + "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t" + "isb \n\t" + "dsb \n\t" + "ldr fp, [sp], #4" + : : : "r0","r1","r2","r3","r4","r5","r6","r7", + "r9","r10","lr","memory"); } __mcpm_cpu_down(cpu, cluster); diff --git a/arch/arm/mach-vexpress/spc.c b/arch/arm/mach-vexpress/spc.c index c26ef5b..eefb029 100644 --- a/arch/arm/mach-vexpress/spc.c +++ b/arch/arm/mach-vexpress/spc.c @@ -17,31 +17,14 @@ * GNU General Public License for more details. */ -#include <linux/clk-provider.h> -#include <linux/clkdev.h> -#include <linux/cpu.h> -#include <linux/delay.h> #include <linux/err.h> -#include <linux/interrupt.h> #include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/pm_opp.h> #include <linux/slab.h> -#include <linux/semaphore.h> #include <asm/cacheflush.h> #define SPCLOG "vexpress-spc: " -#define PERF_LVL_A15 0x00 -#define PERF_REQ_A15 0x04 -#define PERF_LVL_A7 0x08 -#define PERF_REQ_A7 0x0c -#define COMMS 0x10 -#define COMMS_REQ 0x14 -#define PWC_STATUS 0x18 -#define PWC_FLAG 0x1c - /* SPC wake-up IRQs status and mask */ #define WAKE_INT_MASK 0x24 #define WAKE_INT_RAW 0x28 @@ -53,50 +36,12 @@ #define A15_BX_ADDR0 0x68 #define A7_BX_ADDR0 0x78 -/* SPC CPU/cluster reset statue */ -#define STANDBYWFI_STAT 0x3c -#define STANDBYWFI_STAT_A15_CPU_MASK(cpu) (1 << (cpu)) -#define STANDBYWFI_STAT_A7_CPU_MASK(cpu) (1 << (3 + (cpu))) - -/* SPC system config interface registers */ -#define SYSCFG_WDATA 0x70 -#define SYSCFG_RDATA 0x74 - -/* A15/A7 OPP virtual register base */ -#define A15_PERFVAL_BASE 0xC10 -#define A7_PERFVAL_BASE 0xC30 - -/* Config interface control bits */ -#define SYSCFG_START (1 << 31) -#define SYSCFG_SCC (6 << 20) -#define SYSCFG_STAT (14 << 20) - /* wake-up interrupt masks */ #define GBL_WAKEUP_INT_MSK (0x3 << 10) /* TC2 static dual-cluster configuration */ #define MAX_CLUSTERS 2 -/* - * Even though the SPC takes max 3-5 ms to complete any OPP/COMMS - * operation, the operation could start just before jiffie is about - * to be incremented. So setting timeout value of 20ms = 2jiffies@100Hz - */ -#define TIMEOUT_US 20000 - -#define MAX_OPPS 8 -#define CA15_DVFS 0 -#define CA7_DVFS 1 -#define SPC_SYS_CFG 2 -#define STAT_COMPLETE(type) ((1 << 0) << (type << 2)) -#define STAT_ERR(type) ((1 << 1) << (type << 2)) -#define RESPONSE_MASK(type) (STAT_COMPLETE(type) | STAT_ERR(type)) - -struct ve_spc_opp { - unsigned long freq; - unsigned long u_volt; -}; - struct ve_spc_drvdata { void __iomem *baseaddr; /* @@ -104,12 +49,6 @@ struct ve_spc_drvdata { * It corresponds to A15 processors MPIDR[15:8] bitfield */ u32 a15_clusid; - uint32_t cur_rsp_mask; - uint32_t cur_rsp_stat; - struct semaphore sem; - struct completion done; - struct ve_spc_opp *opps[MAX_CLUSTERS]; - int num_opps[MAX_CLUSTERS]; }; static struct ve_spc_drvdata *info; @@ -218,232 +157,8 @@ void ve_spc_powerdown(u32 cluster, bool enable) writel_relaxed(enable, info->baseaddr + pwdrn_reg); } -static u32 standbywfi_cpu_mask(u32 cpu, u32 cluster) -{ - return cluster_is_a15(cluster) ? - STANDBYWFI_STAT_A15_CPU_MASK(cpu) - : STANDBYWFI_STAT_A7_CPU_MASK(cpu); -} - -/** - * ve_spc_cpu_in_wfi(u32 cpu, u32 cluster) - * - * @cpu: mpidr[7:0] bitfield describing CPU affinity level within cluster - * @cluster: mpidr[15:8] bitfield describing cluster affinity level - * - * @return: non-zero if and only if the specified CPU is in WFI - * - * Take care when interpreting the result of this function: a CPU might - * be in WFI temporarily due to idle, and is not necessarily safely - * parked. - */ -int ve_spc_cpu_in_wfi(u32 cpu, u32 cluster) -{ - int ret; - u32 mask = standbywfi_cpu_mask(cpu, cluster); - - if (cluster >= MAX_CLUSTERS) - return 1; - - ret = readl_relaxed(info->baseaddr + STANDBYWFI_STAT); - - pr_debug("%s: PCFGREG[0x%X] = 0x%08X, mask = 0x%X\n", - __func__, STANDBYWFI_STAT, ret, mask); - - return ret & mask; -} - -static int ve_spc_get_performance(int cluster, u32 *freq) -{ - struct ve_spc_opp *opps = info->opps[cluster]; - u32 perf_cfg_reg = 0; - u32 perf; - - perf_cfg_reg = cluster_is_a15(cluster) ? PERF_LVL_A15 : PERF_LVL_A7; - - perf = readl_relaxed(info->baseaddr + perf_cfg_reg); - if (perf >= info->num_opps[cluster]) - return -EINVAL; - - opps += perf; - *freq = opps->freq; - - return 0; -} - -/* find closest match to given frequency in OPP table */ -static int ve_spc_round_performance(int cluster, u32 freq) -{ - int idx, max_opp = info->num_opps[cluster]; - struct ve_spc_opp *opps = info->opps[cluster]; - u32 fmin = 0, fmax = ~0, ftmp; - - freq /= 1000; /* OPP entries in kHz */ - for (idx = 0; idx < max_opp; idx++, opps++) { - ftmp = opps->freq; - if (ftmp >= freq) { - if (ftmp <= fmax) - fmax = ftmp; - } else { - if (ftmp >= fmin) - fmin = ftmp; - } - } - if (fmax != ~0) - return fmax * 1000; - else - return fmin * 1000; -} - -static int ve_spc_find_performance_index(int cluster, u32 freq) -{ - int idx, max_opp = info->num_opps[cluster]; - struct ve_spc_opp *opps = info->opps[cluster]; - - for (idx = 0; idx < max_opp; idx++, opps++) - if (opps->freq == freq) - break; - return (idx == max_opp) ? -EINVAL : idx; -} - -static int ve_spc_waitforcompletion(int req_type) -{ - int ret = wait_for_completion_interruptible_timeout( - &info->done, usecs_to_jiffies(TIMEOUT_US)); - if (ret == 0) - ret = -ETIMEDOUT; - else if (ret > 0) - ret = info->cur_rsp_stat & STAT_COMPLETE(req_type) ? 0 : -EIO; - return ret; -} - -static int ve_spc_set_performance(int cluster, u32 freq) -{ - u32 perf_cfg_reg, perf_stat_reg; - int ret, perf, req_type; - - if (cluster_is_a15(cluster)) { - req_type = CA15_DVFS; - perf_cfg_reg = PERF_LVL_A15; - perf_stat_reg = PERF_REQ_A15; - } else { - req_type = CA7_DVFS; - perf_cfg_reg = PERF_LVL_A7; - perf_stat_reg = PERF_REQ_A7; - } - - perf = ve_spc_find_performance_index(cluster, freq); - - if (perf < 0) - return perf; - - if (down_timeout(&info->sem, usecs_to_jiffies(TIMEOUT_US))) - return -ETIME; - - init_completion(&info->done); - info->cur_rsp_mask = RESPONSE_MASK(req_type); - - writel(perf, info->baseaddr + perf_cfg_reg); - ret = ve_spc_waitforcompletion(req_type); - - info->cur_rsp_mask = 0; - up(&info->sem); - - return ret; -} - -static int ve_spc_read_sys_cfg(int func, int offset, uint32_t *data) -{ - int ret; - - if (down_timeout(&info->sem, usecs_to_jiffies(TIMEOUT_US))) - return -ETIME; - - init_completion(&info->done); - info->cur_rsp_mask = RESPONSE_MASK(SPC_SYS_CFG); - - /* Set the control value */ - writel(SYSCFG_START | func | offset >> 2, info->baseaddr + COMMS); - ret = ve_spc_waitforcompletion(SPC_SYS_CFG); - - if (ret == 0) - *data = readl(info->baseaddr + SYSCFG_RDATA); - - info->cur_rsp_mask = 0; - up(&info->sem); - - return ret; -} - -static irqreturn_t ve_spc_irq_handler(int irq, void *data) -{ - struct ve_spc_drvdata *drv_data = data; - uint32_t status = readl_relaxed(drv_data->baseaddr + PWC_STATUS); - - if (info->cur_rsp_mask & status) { - info->cur_rsp_stat = status; - complete(&drv_data->done); - } - - return IRQ_HANDLED; -} - -/* - * +--------------------------+ - * | 31 20 | 19 0 | - * +--------------------------+ - * | u_volt | freq(kHz) | - * +--------------------------+ - */ -#define MULT_FACTOR 20 -#define VOLT_SHIFT 20 -#define FREQ_MASK (0xFFFFF) -static int ve_spc_populate_opps(uint32_t cluster) -{ - uint32_t data = 0, off, ret, idx; - struct ve_spc_opp *opps; - - opps = kzalloc(sizeof(*opps) * MAX_OPPS, GFP_KERNEL); - if (!opps) - return -ENOMEM; - - info->opps[cluster] = opps; - - off = cluster_is_a15(cluster) ? A15_PERFVAL_BASE : A7_PERFVAL_BASE; - for (idx = 0; idx < MAX_OPPS; idx++, off += 4, opps++) { - ret = ve_spc_read_sys_cfg(SYSCFG_SCC, off, &data); - if (!ret) { - opps->freq = (data & FREQ_MASK) * MULT_FACTOR; - opps->u_volt = data >> VOLT_SHIFT; - } else { - break; - } - } - info->num_opps[cluster] = idx; - - return ret; -} - -static int ve_init_opp_table(struct device *cpu_dev) +int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid) { - int cluster = topology_physical_package_id(cpu_dev->id); - int idx, ret = 0, max_opp = info->num_opps[cluster]; - struct ve_spc_opp *opps = info->opps[cluster]; - - for (idx = 0; idx < max_opp; idx++, opps++) { - ret = dev_pm_opp_add(cpu_dev, opps->freq * 1000, opps->u_volt); - if (ret) { - dev_warn(cpu_dev, "failed to add opp %lu %lu\n", - opps->freq, opps->u_volt); - return ret; - } - } - return ret; -} - -int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq) -{ - int ret; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { pr_err(SPCLOG "unable to allocate mem\n"); @@ -453,25 +168,6 @@ int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq) info->baseaddr = baseaddr; info->a15_clusid = a15_clusid; - if (irq <= 0) { - pr_err(SPCLOG "Invalid IRQ %d\n", irq); - kfree(info); - return -EINVAL; - } - - init_completion(&info->done); - - readl_relaxed(info->baseaddr + PWC_STATUS); - - ret = request_irq(irq, ve_spc_irq_handler, IRQF_TRIGGER_HIGH - | IRQF_ONESHOT, "vexpress-spc", info); - if (ret) { - pr_err(SPCLOG "IRQ %d request failed\n", irq); - kfree(info); - return -ENODEV; - } - - sema_init(&info->sem, 1); /* * Multi-cluster systems may need this data when non-coherent, during * cluster power-up/power-down. Make sure driver info reaches main @@ -482,103 +178,3 @@ int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq) return 0; } - -struct clk_spc { - struct clk_hw hw; - int cluster; -}; - -#define to_clk_spc(spc) container_of(spc, struct clk_spc, hw) -static unsigned long spc_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct clk_spc *spc = to_clk_spc(hw); - u32 freq; - - if (ve_spc_get_performance(spc->cluster, &freq)) - return -EIO; - - return freq * 1000; -} - -static long spc_round_rate(struct clk_hw *hw, unsigned long drate, - unsigned long *parent_rate) -{ - struct clk_spc *spc = to_clk_spc(hw); - - return ve_spc_round_performance(spc->cluster, drate); -} - -static int spc_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct clk_spc *spc = to_clk_spc(hw); - - return ve_spc_set_performance(spc->cluster, rate / 1000); -} - -static struct clk_ops clk_spc_ops = { - .recalc_rate = spc_recalc_rate, - .round_rate = spc_round_rate, - .set_rate = spc_set_rate, -}; - -static struct clk *ve_spc_clk_register(struct device *cpu_dev) -{ - struct clk_init_data init; - struct clk_spc *spc; - - spc = kzalloc(sizeof(*spc), GFP_KERNEL); - if (!spc) { - pr_err("could not allocate spc clk\n"); - return ERR_PTR(-ENOMEM); - } - - spc->hw.init = &init; - spc->cluster = topology_physical_package_id(cpu_dev->id); - - init.name = dev_name(cpu_dev); - init.ops = &clk_spc_ops; - init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE; - init.num_parents = 0; - - return devm_clk_register(cpu_dev, &spc->hw); -} - -static int __init ve_spc_clk_init(void) -{ - int cpu; - struct clk *clk; - - if (!info) - return 0; /* Continue only if SPC is initialised */ - - if (ve_spc_populate_opps(0) || ve_spc_populate_opps(1)) { - pr_err("failed to build OPP table\n"); - return -ENODEV; - } - - for_each_possible_cpu(cpu) { - struct device *cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_warn("failed to get cpu%d device\n", cpu); - continue; - } - clk = ve_spc_clk_register(cpu_dev); - if (IS_ERR(clk)) { - pr_warn("failed to register cpu%d clock\n", cpu); - continue; - } - if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) { - pr_warn("failed to register cpu%d clock lookup\n", cpu); - continue; - } - - if (ve_init_opp_table(cpu_dev)) - pr_warn("failed to initialise cpu%d opp table\n", cpu); - } - - platform_device_register_simple("vexpress-spc-cpufreq", -1, NULL, 0); - return 0; -} -module_init(ve_spc_clk_init); diff --git a/arch/arm/mach-vexpress/spc.h b/arch/arm/mach-vexpress/spc.h index 793d065..5f7e4a4 100644 --- a/arch/arm/mach-vexpress/spc.h +++ b/arch/arm/mach-vexpress/spc.h @@ -15,11 +15,10 @@ #ifndef __SPC_H_ #define __SPC_H_ -int __init ve_spc_init(void __iomem *base, u32 a15_clusid, int irq); +int __init ve_spc_init(void __iomem *base, u32 a15_clusid); void ve_spc_global_wakeup_irq(bool set); void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set); void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr); void ve_spc_powerdown(u32 cluster, bool enable); -int ve_spc_cpu_in_wfi(u32 cpu, u32 cluster); #endif diff --git a/arch/arm/mach-vexpress/tc2_pm.c b/arch/arm/mach-vexpress/tc2_pm.c index 29e7785..e6eb481 100644 --- a/arch/arm/mach-vexpress/tc2_pm.c +++ b/arch/arm/mach-vexpress/tc2_pm.c @@ -12,12 +12,10 @@ * published by the Free Software Foundation. */ -#include <linux/delay.h> #include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/of_address.h> -#include <linux/of_irq.h> #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/irqchip/arm-gic.h> @@ -33,17 +31,11 @@ #include "spc.h" /* SCC conf registers */ -#define RESET_CTRL 0x018 -#define RESET_A15_NCORERESET(cpu) (1 << (2 + (cpu))) -#define RESET_A7_NCORERESET(cpu) (1 << (16 + (cpu))) - #define A15_CONF 0x400 #define A7_CONF 0x500 #define SYS_INFO 0x700 #define SPC_BASE 0xb00 -static void __iomem *scc; - /* * We can't use regular spinlocks. In the switcher case, it is possible * for an outbound CPU to call power_down() after its inbound counterpart @@ -164,7 +156,32 @@ static void tc2_pm_down(u64 residency) : : "r" (0x400) ); } - v7_exit_coherency_flush(all); + /* + * We need to disable and flush the whole (L1 and L2) cache. + * Let's do it in the safest possible way i.e. with + * no memory access within the following sequence + * including the stack. + * + * Note: fp is preserved to the stack explicitly prior doing + * this since adding it to the clobber list is incompatible + * with having CONFIG_FRAME_POINTER=y. + */ + asm volatile( + "str fp, [sp, #-4]! \n\t" + "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t" + "bic r0, r0, #"__stringify(CR_C)" \n\t" + "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t" + "isb \n\t" + "bl v7_flush_dcache_all \n\t" + "clrex \n\t" + "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t" + "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" + "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t" + "isb \n\t" + "dsb \n\t" + "ldr fp, [sp], #4" + : : : "r0","r1","r2","r3","r4","r5","r6","r7", + "r9","r10","lr","memory"); cci_disable_port_by_cpu(mpidr); @@ -180,7 +197,26 @@ static void tc2_pm_down(u64 residency) arch_spin_unlock(&tc2_pm_lock); - v7_exit_coherency_flush(louis); + /* + * We need to disable and flush only the L1 cache. + * Let's do it in the safest possible way as above. + */ + asm volatile( + "str fp, [sp, #-4]! \n\t" + "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t" + "bic r0, r0, #"__stringify(CR_C)" \n\t" + "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t" + "isb \n\t" + "bl v7_flush_dcache_louis \n\t" + "clrex \n\t" + "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t" + "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" + "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t" + "isb \n\t" + "dsb \n\t" + "ldr fp, [sp], #4" + : : : "r0","r1","r2","r3","r4","r5","r6","r7", + "r9","r10","lr","memory"); } __mcpm_cpu_down(cpu, cluster); @@ -197,55 +233,6 @@ static void tc2_pm_power_down(void) tc2_pm_down(0); } -static int tc2_core_in_reset(unsigned int cpu, unsigned int cluster) -{ - u32 mask = cluster ? - RESET_A7_NCORERESET(cpu) - : RESET_A15_NCORERESET(cpu); - - return !(readl_relaxed(scc + RESET_CTRL) & mask); -} - -#define POLL_MSEC 10 -#define TIMEOUT_MSEC 1000 - -static int tc2_pm_power_down_finish(unsigned int cpu, unsigned int cluster) -{ - unsigned tries; - - pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); - BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER); - - for (tries = 0; tries < TIMEOUT_MSEC / POLL_MSEC; ++tries) { - /* - * Only examine the hardware state if the target CPU has - * caught up at least as far as tc2_pm_down(): - */ - if (ACCESS_ONCE(tc2_pm_use_count[cpu][cluster]) == 0) { - pr_debug("%s(cpu=%u, cluster=%u): RESET_CTRL = 0x%08X\n", - __func__, cpu, cluster, - readl_relaxed(scc + RESET_CTRL)); - - /* - * We need the CPU to reach WFI, but the power - * controller may put the cluster in reset and - * power it off as soon as that happens, before - * we have a chance to see STANDBYWFI. - * - * So we need to check for both conditions: - */ - if (tc2_core_in_reset(cpu, cluster) || - ve_spc_cpu_in_wfi(cpu, cluster)) - return 0; /* success: the CPU is halted */ - } - - /* Otherwise, wait and retry: */ - msleep(POLL_MSEC); - } - - return -ETIMEDOUT; /* timeout */ -} - static void tc2_pm_suspend(u64 residency) { unsigned int mpidr, cpu, cluster; @@ -288,11 +275,10 @@ static void tc2_pm_powered_up(void) } static const struct mcpm_platform_ops tc2_pm_power_ops = { - .power_up = tc2_pm_power_up, - .power_down = tc2_pm_power_down, - .power_down_finish = tc2_pm_power_down_finish, - .suspend = tc2_pm_suspend, - .powered_up = tc2_pm_powered_up, + .power_up = tc2_pm_power_up, + .power_down = tc2_pm_power_down, + .suspend = tc2_pm_suspend, + .powered_up = tc2_pm_powered_up, }; static bool __init tc2_pm_usage_count_init(void) @@ -325,7 +311,8 @@ static void __naked tc2_pm_power_up_setup(unsigned int affinity_level) static int __init tc2_pm_init(void) { - int ret, irq; + int ret; + void __iomem *scc; u32 a15_cluster_id, a7_cluster_id, sys_info; struct device_node *np; @@ -349,15 +336,13 @@ static int __init tc2_pm_init(void) tc2_nr_cpus[a15_cluster_id] = (sys_info >> 16) & 0xf; tc2_nr_cpus[a7_cluster_id] = (sys_info >> 20) & 0xf; - irq = irq_of_parse_and_map(np, 0); - /* * A subset of the SCC registers is also used to communicate * with the SPC (power controller). We need to be able to * drive it very early in the boot process to power up * processors, so we initialize the SPC driver here. */ - ret = ve_spc_init(scc + SPC_BASE, a15_cluster_id, irq); + ret = ve_spc_init(scc + SPC_BASE, a15_cluster_id); if (ret) return ret; diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index 4f8b8cb..95a469e 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -1,10 +1,12 @@ /* * Versatile Express V2M Motherboard Support */ +#include <linux/clocksource.h> #include <linux/device.h> #include <linux/amba/bus.h> #include <linux/amba/mmci.h> #include <linux/io.h> +#include <linux/clocksource.h> #include <linux/smp.h> #include <linux/init.h> #include <linux/of_address.h> @@ -20,6 +22,7 @@ #include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> #include <linux/vexpress.h> +#include <linux/clk-provider.h> #include <linux/clkdev.h> #include <asm/mach-types.h> @@ -419,8 +422,16 @@ void __init v2m_dt_init_early(void) pr_warning("vexpress: DT HBI (%x) is not matching " "hardware (%x)!\n", dt_hbi, hbi); } +} - versatile_sched_clock_init(vexpress_get_24mhz_clock_base(), 24000000); +static void __init v2m_dt_timer_init(void) +{ + of_clk_init(NULL); + + clocksource_of_init(); + + versatile_sched_clock_init(vexpress_get_24mhz_clock_base(), + 24000000); } static const struct of_device_id v2m_dt_bus_match[] __initconst = { @@ -447,5 +458,6 @@ DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express") .smp_init = smp_init_ops(vexpress_smp_init_ops), .map_io = v2m_dt_map_io, .init_early = v2m_dt_init_early, + .init_time = v2m_dt_timer_init, .init_machine = v2m_dt_init, MACHINE_END |