From patchwork Tue May 6 18:04:42 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Pieralisi X-Patchwork-Id: 29730 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ie0-f197.google.com (mail-ie0-f197.google.com [209.85.223.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 37F54202E6 for ; Tue, 6 May 2014 18:03:27 +0000 (UTC) Received: by mail-ie0-f197.google.com with SMTP id rd18sf50952182iec.8 for ; Tue, 06 May 2014 11:03:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe:content-type :content-transfer-encoding; bh=LoS7AOwjA5N1Z1Pj699gKWmzlrY0OdkTXautWN0O7PM=; b=XQ3JzXctoP9WqPVQzkem0VpOObJXoocO+HDJbHLxK1z64tJAMXxMAGLCO5CBuK9S45 ru3I3ws27cr6jyUXNhfVkahHk7Ds2wNhISTO9aj6ckaf9fWbysfPp80w00Z1Hpqw22G7 6fRPtfQ92H99YRDM6TKJgZIShgdn/CNlf0Gj48BZamOVhPJ5/nw0t+sX0jDSIgpbxmSg vZ2MatFuk8n1Brf0FQ069RxAs/h+ELXjEOtI7YEj/LqKlwExlCoryAt4eguUXkLS2gw+ RcB/sjbxhn2efyEhYCWG9tEpegSAAKymTBgRJrVfneSxNSBmZ+pWeUzjaIDVQTu2yRPf 0TvA== X-Gm-Message-State: ALoCoQkCUytKDQXKPSqffYIHllUNihZsB4tYyS06InVC5Kx6koMkOBBgMdh4O5zxNeDotYhLZbKl X-Received: by 10.43.125.196 with SMTP id gt4mr1677112icc.32.1399399406698; Tue, 06 May 2014 11:03:26 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.43.35 with SMTP id d32ls3025526qga.87.gmail; Tue, 06 May 2014 11:03:26 -0700 (PDT) X-Received: by 10.53.1.69 with SMTP id be5mr7609480vdd.27.1399399403143; Tue, 06 May 2014 11:03:23 -0700 (PDT) Received: from mail-ve0-f174.google.com (mail-ve0-f174.google.com [209.85.128.174]) by mx.google.com with ESMTPS id m10si2458007vcf.47.2014.05.06.11.03.23 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 06 May 2014 11:03:23 -0700 (PDT) Received-SPF: none (google.com: patch+caf_=patchwork-forward=linaro.org@linaro.org does not designate permitted sender hosts) client-ip=209.85.128.174; Received: by mail-ve0-f174.google.com with SMTP id jw12so4256298veb.33 for ; Tue, 06 May 2014 11:03:23 -0700 (PDT) X-Received: by 10.58.112.98 with SMTP id ip2mr2043644veb.35.1399399403043; Tue, 06 May 2014 11:03:23 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.220.221.72 with SMTP id ib8csp244346vcb; Tue, 6 May 2014 11:03:22 -0700 (PDT) X-Received: by 10.66.153.80 with SMTP id ve16mr9023873pab.143.1399399402300; Tue, 06 May 2014 11:03:22 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id qb5si471296pbb.28.2014.05.06.11.03.21; Tue, 06 May 2014 11:03:21 -0700 (PDT) Received-SPF: none (google.com: linux-pm-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752788AbaEFSDR (ORCPT + 12 others); Tue, 6 May 2014 14:03:17 -0400 Received: from service87.mimecast.com ([91.220.42.44]:50301 "EHLO service87.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752406AbaEFSDQ (ORCPT ); Tue, 6 May 2014 14:03:16 -0400 Received: from cam-owa1.Emea.Arm.com (fw-tnat.cambridge.arm.com [217.140.96.21]) by service87.mimecast.com; Tue, 06 May 2014 19:03:14 +0100 Received: from red-moon.cambridge.arm.com ([10.1.255.212]) by cam-owa1.Emea.Arm.com with Microsoft SMTPSVC(6.0.3790.3959); Tue, 6 May 2014 19:03:22 +0100 From: Lorenzo Pieralisi To: linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org Cc: devicetree@vger.kernel.org, Lorenzo Pieralisi , Mark Rutland , Sudeep Holla , Catalin Marinas , Charles Garcia Tobin , Nicolas Pitre , Rob Herring , Grant Likely , Peter De Schrijver , Santosh Shilimkar , Daniel Lezcano , Amit Kucheria , Vincent Guittot , Antti Miettinen , Stephen Boyd , Kevin Hilman , Sebastian Capella , Tomasz Figa Subject: [PATCH RFC v3 5/6] drivers: cpuidle: CPU idle ARM64 driver Date: Tue, 6 May 2014 19:04:42 +0100 Message-Id: <1399399483-17112-6-git-send-email-lorenzo.pieralisi@arm.com> X-Mailer: git-send-email 1.8.4 In-Reply-To: <1399399483-17112-1-git-send-email-lorenzo.pieralisi@arm.com> References: <1399399483-17112-1-git-send-email-lorenzo.pieralisi@arm.com> X-OriginalArrivalTime: 06 May 2014 18:03:22.0877 (UTC) FILETIME=[785162D0:01CF6955] X-MC-Unique: 114050619031407201 Sender: linux-pm-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: lorenzo.pieralisi@arm.com X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: patch+caf_=patchwork-forward=linaro.org@linaro.org does not designate permitted sender hosts) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , This patch implements a generic CPU idle driver for ARM64 machines. It relies on the DT idle states infrastructure to initialize idle states count and respective parameters. Current code assumes the driver is managing idle states on all possible CPUs but can be easily generalized to support heterogenous systems and build cpumasks at runtime using MIDRs or DT cpu nodes compatible properties. Suspend back-ends (eg PSCI) must register a suspend initializer with the CPU idle driver so that the suspend backend call can be detected, and the driver code can call the back-end infrastructure to complete the suspend backend initialization. Idle state index 0 is always initialized as a simple wfi state, ie always considered present and functional on all ARM64 platforms. Signed-off-by: Lorenzo Pieralisi --- drivers/cpuidle/Kconfig | 5 ++ drivers/cpuidle/Kconfig.arm64 | 13 ++++ drivers/cpuidle/Makefile | 4 + drivers/cpuidle/cpuidle-arm64.c | 161 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 drivers/cpuidle/Kconfig.arm64 create mode 100644 drivers/cpuidle/cpuidle-arm64.c diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 995654e..0654eb5 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -44,6 +44,11 @@ depends on ARM source "drivers/cpuidle/Kconfig.arm" endmenu +menu "ARM64 CPU Idle Drivers" +depends on ARM64 +source "drivers/cpuidle/Kconfig.arm64" +endmenu + menu "POWERPC CPU Idle Drivers" depends on PPC source "drivers/cpuidle/Kconfig.powerpc" diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64 new file mode 100644 index 0000000..b83612c --- /dev/null +++ b/drivers/cpuidle/Kconfig.arm64 @@ -0,0 +1,13 @@ +# +# ARM64 CPU Idle drivers +# + +config ARM64_CPUIDLE + bool "Generic ARM64 CPU idle Driver" + select OF_IDLE_STATES + help + Select this to enable generic cpuidle driver for ARM v8. + It provides a generic idle driver whose idle states are configured + at run-time through DT nodes. The CPUidle suspend backend is + initialized by the device tree parsing code on matching the entry + method to the respective CPU operations. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 2fc5139..161a5f2 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -16,6 +16,10 @@ obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o ############################################################################### +# ARM64 drivers +obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o + +############################################################################### # POWERPC drivers obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c new file mode 100644 index 0000000..fef1fad --- /dev/null +++ b/drivers/cpuidle/cpuidle-arm64.c @@ -0,0 +1,161 @@ +/* + * ARM64 generic CPU idle driver. + * + * Copyright (C) 2014 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * 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. + */ + +#define pr_fmt(fmt) "CPUidle arm64: " fmt + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "of_idle_states.h" + +typedef int (*suspend_init_fn)(struct cpuidle_driver *, + struct device_node *[]); + +struct cpu_suspend_ops { + const char *id; + suspend_init_fn init_fn; +}; + +static const struct cpu_suspend_ops suspend_operations[] __initconst = { + {"arm,psci", psci_dt_register_idle_states}, + {} +}; + +static __init const struct cpu_suspend_ops *get_suspend_ops(const char *str) +{ + int i; + + if (!str) + return NULL; + + for (i = 0; suspend_operations[i].id; i++) + if (!strcmp(suspend_operations[i].id, str)) + return &suspend_operations[i]; + + return NULL; +} + +/* + * arm_enter_idle_state - Programs CPU to enter the specified state + * + * @dev: cpuidle device + * @drv: cpuidle driver + * @idx: state index + * + * Called from the CPUidle framework to program the device to the + * specified target state selected by the governor. + */ +static int arm_enter_idle_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx) +{ + int ret; + + if (!idx) { + cpu_do_idle(); + return idx; + } + + cpu_pm_enter(); + /* + * Pass idle state index to cpu_suspend which in turn will call + * the CPU ops suspend protocol with idle index as a parameter. + * + * Some states would not require context to be saved and flushed + * to DRAM, so calling cpu_suspend would not be stricly necessary. + * When power domains specifications for ARM CPUs are finalized then + * this code can be optimized to prevent saving registers if not + * needed. + */ + ret = cpu_suspend(idx); + + cpu_pm_exit(); + + return ret ? -1 : idx; +} + +struct cpuidle_driver arm64_idle_driver = { + .name = "arm64_idle", + .owner = THIS_MODULE, +}; + +static struct device_node *state_nodes[CPUIDLE_STATE_MAX] __initdata; + +/* + * arm64_idle_init + * + * Registers the arm64 specific cpuidle driver with the cpuidle + * framework. It relies on core code to parse the idle states + * and initialize them using driver data structures accordingly. + */ +static int __init arm64_idle_init(void) +{ + int i, ret; + const char *entry_method; + struct device_node *idle_states_node; + const struct cpu_suspend_ops *suspend_init; + struct cpuidle_driver *drv = &arm64_idle_driver; + + idle_states_node = of_find_node_by_path("/cpus/idle-states"); + if (!idle_states_node) + return -ENOENT; + + if (of_property_read_string(idle_states_node, "entry-method", + &entry_method)) { + pr_warn(" * %s missing entry-method property\n", + idle_states_node->full_name); + of_node_put(idle_states_node); + return -EOPNOTSUPP; + } + + suspend_init = get_suspend_ops(entry_method); + if (!suspend_init) { + pr_warn("Missing suspend initializer\n"); + of_node_put(idle_states_node); + return -EOPNOTSUPP; + } + + /* + * State at index 0 is standby wfi and considered standard + * on all ARM platforms. If in some platforms simple wfi + * can't be used as "state 0", DT bindings must be implemented + * to work around this issue and allow installing a special + * handler for idle state index 0. + */ + drv->states[0].exit_latency = 1; + drv->states[0].target_residency = 1; + drv->states[0].flags = CPUIDLE_FLAG_TIME_VALID; + strncpy(drv->states[0].name, "ARM WFI", CPUIDLE_NAME_LEN); + strncpy(drv->states[0].desc, "ARM WFI", CPUIDLE_DESC_LEN); + + drv->cpumask = (struct cpumask *) cpu_possible_mask; + /* + * Start at index 1, request idle state nodes to be filled + */ + ret = of_init_idle_driver(drv, state_nodes, 1, true); + if (ret) + return ret; + + if (suspend_init->init_fn(drv, state_nodes)) + return -EOPNOTSUPP; + + for (i = 0; i < drv->state_count; i++) + drv->states[i].enter = arm_enter_idle_state; + + return cpuidle_register(drv, NULL); +} +device_initcall(arm64_idle_init);