From patchwork Thu Nov 7 08:12:48 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vyacheslav Tyrtov X-Patchwork-Id: 3151521 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id D82789F466 for ; Thu, 7 Nov 2013 08:17:29 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 91DC720457 for ; Thu, 7 Nov 2013 08:17:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 05BEE201FD for ; Thu, 7 Nov 2013 08:17:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753509Ab3KGIRR (ORCPT ); Thu, 7 Nov 2013 03:17:17 -0500 Received: from mailout3.w1.samsung.com ([210.118.77.13]:32087 "EHLO mailout3.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753447Ab3KGIRI (ORCPT ); Thu, 7 Nov 2013 03:17:08 -0500 Received: from eucpsbgm1.samsung.com (unknown [203.254.199.244]) by mailout3.w1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0MVV008LGWB3GQ80@mailout3.w1.samsung.com>; Thu, 07 Nov 2013 08:17:03 +0000 (GMT) X-AuditID: cbfec7f4-b7f0a6d000007b1b-e5-527b4c7ee361 Received: from eusync3.samsung.com ( [203.254.199.213]) by eucpsbgm1.samsung.com (EUCPMTA) with SMTP id 62.67.31515.E7C4B725; Thu, 07 Nov 2013 08:17:02 +0000 (GMT) Received: from tyrtov.rnd.samsung.ru ([106.109.8.174]) by eusync3.samsung.com (Oracle Communications Messaging Server 7u4-23.01 (7.0.4.23.0) 64bit (built Aug 10 2011)) with ESMTPA id <0MVV000V3WBI3F80@eusync3.samsung.com>; Thu, 07 Nov 2013 08:17:02 +0000 (GMT) From: Vyacheslav Tyrtov To: linux-kernel@vger.kernel.org Cc: Rob Herring , Pawel Moll , Mark Rutland , Stephen Warren , Ian Campbell , Rob Landley , Kukjin Kim , Russell King , Ben Dooks , Mike Turquette , Daniel Lezcano , Thomas Gleixner , Heiko Stuebner , Naour Romain , devicetree@vger.kernel.org, linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, Tarek Dakhran , Tyrtov Vyacheslav , Dave.Martin@arm.com, nicolas.pitre@linaro.org, tomasz.figa@gmail.com Subject: [PATCH v3 3/4] ARM: EXYNOS: add Exynos Dual Cluster Support Date: Thu, 07 Nov 2013 12:12:48 +0400 Message-id: <1383811969-32712-4-git-send-email-v.tyrtov@samsung.com> X-Mailer: git-send-email 1.8.1.5 In-reply-to: <1383811969-32712-1-git-send-email-v.tyrtov@samsung.com> References: <1383811969-32712-1-git-send-email-v.tyrtov@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrKLMWRmVeSWpSXmKPExsVy+t/xq7p1PtVBBh9n6VtMWneAyWLeZ1mL prl7GC3mHznHavH/0WtWi3OvVjJa9C64ymax6fE1VouFbUtYLC7vmsNmMeP8PiaL25d5LZZe v8hk8XTCRTaLT8/+sVtMmL6WxeLwCqBp615OZ7HY8rOD0eLVwTYWi/XPTzFabN40ldli1a4/ jBZTZ/xgd5DwWDNvDaNHS3MPm8eCz1fYPf6uesHssXPWXXaPlcu/sHm8Wj2T1ePOtT1sHu/O nWP32Lyk3uPVNRaPvi2rGD22X5vH7PF5k5zHxrmhAfxRXDYpqTmZZalF+nYJXBkzbxUXTHKv +HdnBVMD42nLLkZODgkBE4mW9nvsELaYxIV769m6GLk4hASWMkr8vPaPCcLpYZKYuXETM0gV m4CeRN+rGywgtoiAgsTm3mesIEXMAofYJF7fvwtUxMEhLOAiceO3O0gNi4CqRMv6X0wgYV6g 8KMrYhDLFCSOtr0FW8wp4Crx894cNpASIaCSNe1RExh5FzAyrGIUTS1NLihOSs811CtOzC0u zUvXS87P3cQIiaovOxgXH7M6xCjAwajEwzujpipIiDWxrLgy9xCjBAezkgivuVF1kBBvSmJl VWpRfnxRaU5q8SFGJg5OqQbGiP43GTuicwJX5PB5PojNjFzyM2R60E7DrE/VyTbHuzRqdvwQ PHBjEWvMdNEewQk/FItXZq9YLOSfUNIouH3GjocXrZ6xV/altfh8yhH6lrNg9j12SffZu/pU 1+xX/1M2UzVh7566hnQpxqibHLEnX7iydFmUhh6vcPiRyVKjcpxp0w6pSROUWIozEg21mIuK EwExv5H6iAIAAA== Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Tarek Dakhran Add EDCS(Exynos Dual Cluster Support) for Samsung Exynos5410 SoC. This enables all 8 cores, 4 x A7 and 4 x A15 run at the same time. Signed-off-by: Tarek Dakhran Signed-off-by: Vyacheslav Tyrtov --- arch/arm/mach-exynos/Makefile | 2 + arch/arm/mach-exynos/edcs.c | 278 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 280 insertions(+) create mode 100644 arch/arm/mach-exynos/edcs.c diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 5369615..ba6efdb 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -34,3 +34,5 @@ AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec) obj-$(CONFIG_MACH_EXYNOS4_DT) += mach-exynos4-dt.o obj-$(CONFIG_MACH_EXYNOS5_DT) += mach-exynos5-dt.o + +obj-$(CONFIG_SOC_EXYNOS5410) += edcs.o diff --git a/arch/arm/mach-exynos/edcs.c b/arch/arm/mach-exynos/edcs.c new file mode 100644 index 0000000..980bfdd --- /dev/null +++ b/arch/arm/mach-exynos/edcs.c @@ -0,0 +1,278 @@ +/* + * arch/arm/mach-exynos/edcs.c - exynos dual cluster power management support + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Tarek Dakhran + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * EDCS(exynos dual cluster support) for Exynos5410 SoC. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define EDCS_CPUS_PER_CLUSTER 4 +#define EDCS_CLUSTERS 2 + +/* Exynos5410 power management registers */ +#define EDCS_CORE_CONFIGURATION(_nr) (S5P_ARM_CORE0_CONFIGURATION \ + + ((_nr) * 0x80)) +#define EDCS_CORE_STATUS(_nr) (EDCS_CORE_CONFIGURATION(_nr) + 0x4) +#define EDCS_CORE_OPTION(_nr) (EDCS_CORE_CONFIGURATION(_nr) + 0x8) + +#define REG_CPU_STATE_ADDR0 (S5P_VA_SYSRAM_NS + 0x28) +#define REG_CPU_STATE_ADDR(_nr) (REG_CPU_STATE_ADDR0 + \ + (_nr) * EDCS_CPUS_PER_CLUSTER) + +#define SECONDARY_RESET (1 << 1) +#define REG_ENTRY_ADDR (S5P_VA_SYSRAM_NS + 0x1c) + +static arch_spinlock_t edcs_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +static int edcs_use_count[EDCS_CPUS_PER_CLUSTER][EDCS_CLUSTERS]; +static int core_count[EDCS_CLUSTERS]; + +static void exynos_core_power_control(unsigned int cpu, unsigned int cluster, + bool enable) +{ + unsigned int offset = cluster * MAX_CPUS_PER_CLUSTER + cpu; + int value = enable ? S5P_CORE_LOCAL_PWR_EN : 0; + + if ((readl_relaxed(EDCS_CORE_STATUS(offset)) & 0x3) != value) { + wmb(); + writel_relaxed(value, EDCS_CORE_CONFIGURATION(offset)); + } +} + +static void exynos_core_power_up(unsigned int cpu, unsigned int cluster) +{ + exynos_core_power_control(cpu, cluster, true); +} + +static void exynos_core_power_down(unsigned int cpu, unsigned int cluster) +{ + exynos_core_power_control(cpu, cluster, false); +} + +void set_boot_flag(unsigned int cpu, unsigned int mode) +{ + writel_relaxed(mode, REG_CPU_STATE_ADDR(cpu)); +} + +static int exynos_power_up(unsigned int cpu, unsigned int cluster) +{ + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cpu >= EDCS_CPUS_PER_CLUSTER || cluster >= EDCS_CLUSTERS); + + local_irq_disable(); + arch_spin_lock(&edcs_lock); + + edcs_use_count[cpu][cluster]++; + if (edcs_use_count[cpu][cluster] == 1) { + ++core_count[cluster]; + set_boot_flag(cpu, SECONDARY_RESET); + exynos_core_power_up(cpu, cluster); + } else if (edcs_use_count[cpu][cluster] != 2) { + /* + * The only possible values are: + * 0 = CPU down + * 1 = CPU (still) up + * 2 = CPU requested to be up before it had a chance + * to actually make itself down. + * Any other value is a bug. + */ + BUG(); + } + + arch_spin_unlock(&edcs_lock); + local_irq_enable(); + + return 0; +} +static void exynos_power_down(void) +{ + unsigned int mpidr, cpu, cluster; + bool last_man = false, skip_wfi = false; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: CORE%d on CLUSTER %d\n", __func__, cpu, cluster); + BUG_ON(cpu >= EDCS_CPUS_PER_CLUSTER || cluster >= EDCS_CLUSTERS); + + __mcpm_cpu_going_down(cpu, cluster); + + arch_spin_lock(&edcs_lock); + BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); + edcs_use_count[cpu][cluster]--; + if (edcs_use_count[cpu][cluster] == 0) { + --core_count[cluster]; + if (core_count[cluster] == 0) + last_man = true; + } else if (edcs_use_count[cpu][cluster] == 1) { + /* + * A power_up request went ahead of us. + * Even if we do not want to shut this CPU down, + * the caller expects a certain state as if the WFI + * was aborted. So let's continue with cache cleaning. + */ + skip_wfi = true; + } else + BUG(); + + if (!skip_wfi) + gic_cpu_if_down(); + + if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { + arch_spin_unlock(&edcs_lock); + + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) { + /* + * On the Cortex-A15 we need to disable + * L2 prefetching before flushing the cache. + */ + asm volatile( + "mcr p15, 1, %0, c15, c0, 3\n\t" + "isb\n\t" + "dsb" + : : "r" (0x400)); + } + + /* + * 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); + + __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN); + + } else { + arch_spin_unlock(&edcs_lock); + /* + * 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); + + if (!skip_wfi) { + exynos_core_power_down(cpu, cluster); + wfi(); + } +} + +static const struct mcpm_platform_ops exynos_power_ops = { + .power_up = exynos_power_up, + .power_down = exynos_power_down, +}; + +static void __init edcs_data_init(void) +{ + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cpu >= EDCS_CPUS_PER_CLUSTER || cluster >= EDCS_CLUSTERS); + edcs_use_count[cpu][cluster] = 1; + ++core_count[cluster]; +} + +/* + * Enable cluster-level coherency, in preparation for turning on the MMU. + */ +static void __naked edcs_power_up_setup(unsigned int affinity_level) +{ + asm volatile ("\n" + "b cci_enable_port_for_self"); +} + +static int __init edcs_init(void) +{ + int ret; + struct device_node *node; + + node = of_find_compatible_node(NULL, NULL, "samsung,exynos5410"); + if (!node) + return -ENODEV; + + if (!cci_probed()) + return -ENODEV; + + /* + * Future entries into the kernel can now go + * through the cluster entry vectors. + */ + writel_relaxed(virt_to_phys(mcpm_entry_point), REG_ENTRY_ADDR); + + edcs_data_init(); + mcpm_smp_set_ops(); + + ret = mcpm_platform_register(&exynos_power_ops); + if (!ret) { + mcpm_sync_init(edcs_power_up_setup); + pr_info("EDCS power management initialized\n"); + } + return ret; +} + +early_initcall(edcs_init);