From patchwork Tue Dec 4 14:55:15 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shawn Guo X-Patchwork-Id: 203672 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:4978:20e::2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 797122C008A for ; Wed, 5 Dec 2012 01:59:43 +1100 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1Tftus-0007W9-U9; Tue, 04 Dec 2012 14:56:11 +0000 Received: from mail-pb0-f49.google.com ([209.85.160.49]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TftuM-0007GR-VX for linux-arm-kernel@lists.infradead.org; Tue, 04 Dec 2012 14:55:40 +0000 Received: by mail-pb0-f49.google.com with SMTP id un15so2524484pbc.36 for ; Tue, 04 Dec 2012 06:55:38 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=xyg/6GvTYcBBDORnU1uaRMuipbfJVtoFBOrHHjLlNHQ=; b=W6YMK43FK5tVScAbLOayQdEotjjLR8Yoct+McWZta6Vnl6xbxA7fiP8Jm2w8FNAY4n a0q/m/jRknac3Fu2lyI2t0tH7+Ajnf3DJWrtZi8RKhwhafOVdPBmQvH8kYeKPrcyM9Ns ecRPhozvQolWrMasqtkBEuVto/MYynTGvp2a0q9+QyqKQc7n/iV/NJCB6ZmUbxiwnjEZ /8jE39kjKBCaW4FFY08slVKtdibjGu+vfKhSbqii+mVTru/QF6Z0USHhy/HZwZplqMaq eoUW3ydCslmOE/9oielVS5duV8tygbbtTyT5t1xHYPMZMxUtw5tDbPdV9RWxQ6+pmsAG zE4Q== Received: by 10.68.239.232 with SMTP id vv8mr39282607pbc.53.1354632938684; Tue, 04 Dec 2012 06:55:38 -0800 (PST) Received: from S2101-09.ap.freescale.net ([114.216.216.215]) by mx.google.com with ESMTPS id gu5sm1128343pbc.10.2012.12.04.06.55.34 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 04 Dec 2012 06:55:38 -0800 (PST) From: Shawn Guo To: linux-arm-kernel@lists.infradead.org Subject: [PATCH v2 4/4] ARM: imx6q: support WAIT mode using cpuidle Date: Tue, 4 Dec 2012 22:55:15 +0800 Message-Id: <1354632915-27134-5-git-send-email-shawn.guo@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1354632915-27134-1-git-send-email-shawn.guo@linaro.org> References: <1354632915-27134-1-git-send-email-shawn.guo@linaro.org> X-Gm-Message-State: ALoCoQlSICkqKW4SJg98kTB9GczcnuQhAj8BYlOjQ4ip/3plDZ6JytatkT6hwv8VkSvddi0zR0sN X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20121204_095539_321605_E0B75C12 X-CRM114-Status: GOOD ( 21.31 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.160.49 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Sascha Hauer , Shawn Guo X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org Add WAIT mode (ARM core clock gating) support to imx6q cpuidle driver. As WAIT mode is broken on imx6q TO 1.0 and 1.1, it only enables the support for revision 1.2 with chicken bit set. Signed-off-by: Shawn Guo --- arch/arm/mach-imx/clk-imx6q.c | 12 ++++++ arch/arm/mach-imx/common.h | 3 ++ arch/arm/mach-imx/cpuidle-imx6q.c | 76 ++++++++++++++++++++++++++++++++++++- arch/arm/mach-imx/mach-imx6q.c | 7 +++- arch/arm/mach-imx/platsmp.c | 10 +++++ 5 files changed, 105 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 7f2c10c..c714219 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -54,10 +54,21 @@ #define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26) #define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27) +#define CGPR 0x64 +#define BM_CGPR_CHICKEN_BIT (0x1 << 17) + static void __iomem *ccm_base; void __init imx6q_clock_map_io(void) { } +void imx6q_set_chicken_bit(void) +{ + u32 val = readl_relaxed(ccm_base + CGPR); + + val |= BM_CGPR_CHICKEN_BIT; + writel_relaxed(val, ccm_base + CGPR); +} + int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) { u32 val = readl_relaxed(ccm_base + CLPCR); @@ -68,6 +79,7 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) break; case WAIT_UNCLOCKED: val |= 0x1 << BP_CLPCR_LPM; + val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; break; case STOP_POWER_ON: val |= 0x2 << BP_CLPCR_LPM; diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 7191ab4..7edd53d 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -127,9 +127,11 @@ extern u32 *pl310_get_save_ptr(void); extern void v7_secondary_startup(void); extern void imx_scu_map_io(void); extern void imx_smp_prepare(void); +extern void imx_scu_standby_enable(void); #else static inline void imx_scu_map_io(void) {} static inline void imx_smp_prepare(void) {} +static inline void imx_scu_standby_enable(void) {} #endif extern void imx_enable_cpu(int cpu, bool enable); extern void imx_set_cpu_jump(int cpu, void *jump_addr); @@ -139,6 +141,7 @@ extern void imx_gpc_init(void); extern void imx_gpc_pre_suspend(void); extern void imx_gpc_post_resume(void); extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); +extern void imx6q_set_chicken_bit(void); extern void imx6q_clock_map_io(void); extern void imx_cpu_die(unsigned int cpu); diff --git a/arch/arm/mach-imx/cpuidle-imx6q.c b/arch/arm/mach-imx/cpuidle-imx6q.c index 83facc9..ddc26c2 100644 --- a/arch/arm/mach-imx/cpuidle-imx6q.c +++ b/arch/arm/mach-imx/cpuidle-imx6q.c @@ -6,21 +6,93 @@ * published by the Free Software Foundation. */ +#include #include #include #include +#include +#include "common.h" #include "cpuidle.h" +static atomic_t master = ATOMIC_INIT(0); +static DEFINE_SPINLOCK(master_lock); + +static int imx6q_enter_wait(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + int cpu = dev->cpu; + + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu); + + if (atomic_inc_return(&master) == num_online_cpus()) { + /* + * With this lock, we prevent other cpu to exit and enter + * this function again and become the master. + */ + if (!spin_trylock(&master_lock)) + goto idle; + imx6q_set_lpm(WAIT_UNCLOCKED); + cpu_do_idle(); + imx6q_set_lpm(WAIT_CLOCKED); + spin_unlock(&master_lock); + goto done; + } + +idle: + cpu_do_idle(); +done: + atomic_dec(&master); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu); + + return index; +} + +/* + * For each cpu, setup the broadcast timer because local timer + * stops for the states other than WFI. + */ +static void imx6q_setup_broadcast_timer(void *arg) +{ + int cpu = smp_processor_id(); + + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu); +} + static struct cpuidle_driver imx6q_cpuidle_driver = { .name = "imx6q_cpuidle", .owner = THIS_MODULE, .en_core_tk_irqen = 1, - .states[0] = ARM_CPUIDLE_WFI_STATE, - .state_count = 1, + .states = { + /* WFI */ + ARM_CPUIDLE_WFI_STATE, + /* WAIT */ + { + .exit_latency = 50, + .target_residency = 75, + .flags = CPUIDLE_FLAG_TIME_VALID, + .enter = imx6q_enter_wait, + .name = "WAIT", + .desc = "Clock off", + }, + }, + .state_count = 2, + .safe_state_index = 0, }; int __init imx6q_cpuidle_init(void) { + /* Set initial power mode */ + imx6q_set_lpm(WAIT_CLOCKED); + + /* Need to enable SCU standby for entering WAIT modes */ + imx_scu_standby_enable(); + + /* Set chicken bit to get a reliable WAIT mode support */ + imx6q_set_chicken_bit(); + + /* Configure the broadcast timer on each cpu */ + on_each_cpu(imx6q_setup_broadcast_timer, NULL, 1); + return imx_cpuidle_init(&imx6q_cpuidle_driver); } diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 4ea7ad9..cda7622 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -201,7 +201,12 @@ static void __init imx6q_init_machine(void) static void __init imx6q_init_late(void) { - imx6q_cpuidle_init(); + /* + * WAIT mode is broken on TO 1.0 and 1.1, so there is no point + * to run cpuidle on them. + */ + if (imx6q_revision() > IMX_CHIP_REVISION_1_1) + imx6q_cpuidle_init(); } static void __init imx6q_map_io(void) diff --git a/arch/arm/mach-imx/platsmp.c b/arch/arm/mach-imx/platsmp.c index 3777b80..a70b548 100644 --- a/arch/arm/mach-imx/platsmp.c +++ b/arch/arm/mach-imx/platsmp.c @@ -20,6 +20,8 @@ #include "common.h" #include "hardware.h" +#define SCU_STANDBY_ENABLE (1 << 5) + static void __iomem *scu_base; static struct map_desc scu_io_desc __initdata = { @@ -42,6 +44,14 @@ void __init imx_scu_map_io(void) scu_base = IMX_IO_ADDRESS(base); } +void imx_scu_standby_enable(void) +{ + u32 val = readl_relaxed(scu_base); + + val |= SCU_STANDBY_ENABLE; + writel_relaxed(val, scu_base); +} + static void __cpuinit imx_secondary_init(unsigned int cpu) { /*