From patchwork Tue Apr 1 08:33:54 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dongsheng Wang X-Patchwork-Id: 335692 X-Patchwork-Delegate: scottwood@freescale.com Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [IPv6:::1]) by ozlabs.org (Postfix) with ESMTP id 47B0A140154 for ; Tue, 1 Apr 2014 19:38:06 +1100 (EST) Received: from va3outboundpool.messaging.microsoft.com (va3ehsobe005.messaging.microsoft.com [216.32.180.31]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 7C6C71400AD for ; Tue, 1 Apr 2014 19:37:29 +1100 (EST) Received: from mail51-va3-R.bigfish.com (10.7.14.238) by VA3EHSOBE003.bigfish.com (10.7.40.23) with Microsoft SMTP Server id 14.1.225.23; Tue, 1 Apr 2014 08:37:24 +0000 Received: from mail51-va3 (localhost [127.0.0.1]) by mail51-va3-R.bigfish.com (Postfix) with ESMTP id F17C5300305; Tue, 1 Apr 2014 08:37:23 +0000 (UTC) X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPV:NLI; H:mail.freescale.net; RD:none; EFVD:NLI X-SpamScore: 3 X-BigFish: VS3(zze0eahc8kzz1f42h2148h208ch1ee6h1de0h1fdah2073h2146h1202h1e76h2189h1d1ah1d2ah21bch1fc6hzz1de098h8275bh1de097hz2dh2a8h839hd24he5bhf0ah1288h12a5h12a9h12bdh12e5h137ah139eh13b6h1441h1504h1537h162dh1631h1758h1898h18e1h1946h19b5h1ad9h1b0ah1b2fh2222h224fh1fb3h1d0ch1d2eh1d3fh1dc1h1dfeh1dffh1e23h1fe8h1ff5h2218h2216h226dh22d0h24afh2327h2336h2438h2461h2487h24d7h2516h2545h255eh25cch25f6h2605h268bh1155h) Received: from mail51-va3 (localhost.localdomain [127.0.0.1]) by mail51-va3 (MessageSwitch) id 1396341442404702_7750; Tue, 1 Apr 2014 08:37:22 +0000 (UTC) Received: from VA3EHSMHS016.bigfish.com (unknown [10.7.14.242]) by mail51-va3.bigfish.com (Postfix) with ESMTP id 59B0C20050; Tue, 1 Apr 2014 08:37:22 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by VA3EHSMHS016.bigfish.com (10.7.99.26) with Microsoft SMTP Server (TLS) id 14.16.227.3; Tue, 1 Apr 2014 08:37:22 +0000 Received: from az84smr01.freescale.net (10.64.34.197) by 039-SN1MMR1-005.039d.mgd.msft.net (10.84.1.17) with Microsoft SMTP Server (TLS) id 14.3.158.2; Tue, 1 Apr 2014 08:37:21 +0000 Received: from titan.am.freescale.net (b45104-01-010192208233.ap.freescale.net [10.192.208.233]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id s318bFsw030391; Tue, 1 Apr 2014 01:37:16 -0700 From: Dongsheng Wang To: , Subject: [PATCH] cpuidle: add freescale e500 family porcessors idle support Date: Tue, 1 Apr 2014 16:33:54 +0800 Message-ID: <1396341234-40515-1-git-send-email-dongsheng.wang@freescale.com> X-Mailer: git-send-email 1.8.5 MIME-Version: 1.0 X-OriginatorOrg: freescale.com X-FOPE-CONNECTOR: Id%0$Dn%*$RO%0$TLS%0$FQDN%$TlsDn% X-FOPE-CONNECTOR: Id%0$Dn%FREESCALE.MAIL.ONMICROSOFT.COM$RO%1$TLS%0$FQDN%$TlsDn% Cc: chenhui.zhao@freescale.com, linux-pm@vger.kernel.org, Wang Dongsheng , rjw@sisk.pl, jason.jin@freescale.com, linuxppc-dev@lists.ozlabs.org X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" From: Wang Dongsheng Add cpuidle support for e500 family, using cpuidle framework to manage various low power modes. The new implementation will remain compatible with original idle method. I have done test about power consumption and latency. Cpuidle framework will make CPU response time faster than original method, but power consumption is higher than original method. Power consumption: The original method, power consumption is 10.51202 (W). The cpuidle framework, power consumption is 10.5311 (W). Latency: The original method, avg latency is 6782 (us). The cpuidle framework, avg latency is 6482 (us). Initially, this supports PW10, PW20 and subsequent patches will support DOZE/NAP and PH10, PH20. Signed-off-by: Wang Dongsheng diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 5b6c03f..9301420 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -294,6 +294,15 @@ extern void power7_idle(void); extern void ppc6xx_idle(void); extern void book3e_idle(void); +static inline void cpuidle_wait(void) +{ +#ifdef CONFIG_PPC64 + book3e_idle(); +#else + e500_idle(); +#endif +} + /* * ppc_md contains a copy of the machine description structure for the * current platform. machine_id contains the initial address where the diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 97e1dc9..edd193f 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -190,6 +190,9 @@ static ssize_t show_pw20_wait_time(struct device *dev, return sprintf(buf, "%llu\n", time > 0 ? time : 0); } +#ifdef CONFIG_CPU_IDLE_E500 +u32 cpuidle_entry_bit; +#endif static void set_pw20_wait_entry_bit(void *val) { u32 *value = val; @@ -204,7 +207,11 @@ static void set_pw20_wait_entry_bit(void *val) /* set count */ pw20_idle |= ((MAX_BIT - *value) << PWRMGTCR0_PW20_ENT_SHIFT); +#ifdef CONFIG_CPU_IDLE_E500 + cpuidle_entry_bit = *value; +#else mtspr(SPRN_PWRMGTCR0, pw20_idle); +#endif } static ssize_t store_pw20_wait_time(struct device *dev, diff --git a/drivers/cpuidle/Kconfig.powerpc b/drivers/cpuidle/Kconfig.powerpc index 66c3a09..0949dbf 100644 --- a/drivers/cpuidle/Kconfig.powerpc +++ b/drivers/cpuidle/Kconfig.powerpc @@ -18,3 +18,10 @@ config POWERNV_CPUIDLE help Select this option to enable processor idle state management through cpuidle subsystem. + +config CPU_IDLE_E500 + bool "CPU Idle Driver for E500 family processors" + depends on CPU_IDLE + depends on FSL_SOC_BOOKE + help + Select this to enable cpuidle on e500 family processors. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index f71ae1b..7e6adea 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o # POWERPC drivers obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o +obj-$(CONFIG_CPU_IDLE_E500) += cpuidle-e500.o diff --git a/drivers/cpuidle/cpuidle-e500.c b/drivers/cpuidle/cpuidle-e500.c new file mode 100644 index 0000000..ddc0def --- /dev/null +++ b/drivers/cpuidle/cpuidle-e500.c @@ -0,0 +1,194 @@ +/* + * CPU Idle driver for Freescale PowerPC e500 family processors. + * + * Copyright 2014 Freescale Semiconductor, Inc. + * + * Author: Dongsheng Wang + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static unsigned int max_idle_state; +static struct cpuidle_state *cpuidle_state_table; + +struct cpuidle_driver e500_idle_driver = { + .name = "e500_idle", + .owner = THIS_MODULE, +}; + +static void e500_cpuidle(void) +{ + if (cpuidle_idle_call()) + cpuidle_wait(); +} + +static int pw10_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + cpuidle_wait(); + return index; +} + +#define MAX_BIT 63 +#define MIN_BIT 1 +extern u32 cpuidle_entry_bit; +static int pw20_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + u32 pw20_idle; + u32 entry_bit; + pw20_idle = mfspr(SPRN_PWRMGTCR0); + if ((pw20_idle & PWRMGTCR0_PW20_ENT) != PWRMGTCR0_PW20_ENT) { + pw20_idle &= ~PWRMGTCR0_PW20_ENT; + entry_bit = MAX_BIT - cpuidle_entry_bit; + pw20_idle |= (entry_bit << PWRMGTCR0_PW20_ENT_SHIFT); + mtspr(SPRN_PWRMGTCR0, pw20_idle); + } + + cpuidle_wait(); + + pw20_idle &= ~PWRMGTCR0_PW20_ENT; + pw20_idle |= (MIN_BIT << PWRMGTCR0_PW20_ENT_SHIFT); + mtspr(SPRN_PWRMGTCR0, pw20_idle); + + return index; +} + +static struct cpuidle_state pw_idle_states[] = { + { + .name = "pw10", + .desc = "pw10", + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 0, + .target_residency = 0, + .enter = &pw10_enter + }, + + { + .name = "pw20", + .desc = "pw20-core-idle", + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 1, + .target_residency = 50, + .enter = &pw20_enter + }, +}; + +static int cpu_hotplug_notify(struct notifier_block *n, + unsigned long action, void *hcpu) +{ + unsigned long hotcpu = (unsigned long)hcpu; + struct cpuidle_device *dev = + per_cpu_ptr(cpuidle_devices, hotcpu); + + if (dev && cpuidle_get_driver()) { + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + cpuidle_pause_and_lock(); + cpuidle_enable_device(dev); + cpuidle_resume_and_unlock(); + break; + + case CPU_DEAD: + case CPU_DEAD_FROZEN: + cpuidle_pause_and_lock(); + cpuidle_disable_device(dev); + cpuidle_resume_and_unlock(); + break; + + default: + return NOTIFY_DONE; + } + } + + return NOTIFY_OK; +} + +static struct notifier_block cpu_hotplug_notifier = { + .notifier_call = cpu_hotplug_notify, +}; + +static void e500_cpuidle_driver_init(void) +{ + int idle_state; + struct cpuidle_driver *drv = &e500_idle_driver; + + drv->state_count = 0; + + for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { + if (!cpuidle_state_table[idle_state].enter) + break; + + drv->states[drv->state_count] = cpuidle_state_table[idle_state]; + drv->state_count++; + } +} + +static int e500_idle_state_probe(void) +{ + if (cpuidle_disable != IDLE_NO_OVERRIDE) + return -ENODEV; + + cpuidle_state_table = pw_idle_states; + max_idle_state = ARRAY_SIZE(pw_idle_states); + + /* Disable PW20 feature for e500mc, e5500 */ + if (PVR_VER(cur_cpu_spec->pvr_value) != PVR_VER_E6500) + cpuidle_state_table[1].enter = NULL; + + if (!cpuidle_state_table || !max_idle_state) + return -ENODEV; + + return 0; +} + +static void replace_orig_idle(void *dummy) +{ + return; +} + +static int __init e500_idle_init(void) +{ + struct cpuidle_driver *drv = &e500_idle_driver; + int err; + + if (e500_idle_state_probe()) + return -ENODEV; + + e500_cpuidle_driver_init(); + if (!drv->state_count) + return -ENODEV; + + err = cpuidle_register(drv, NULL); + if (err) { + pr_err("Register e500 family cpuidle driver failed.\n"); + + return err; + } + + err = register_cpu_notifier(&cpu_hotplug_notifier); + if (err) + pr_warn("Cpuidle driver: register cpu notifier failed.\n"); + + /* Replace the original way of idle after cpuidle registered. */ + ppc_md.power_save = e500_cpuidle; + on_each_cpu(replace_orig_idle, NULL, 1); + + pr_info("e500_idle_driver registered.\n"); + + return 0; +} +late_initcall(e500_idle_init);