From patchwork Fri Sep 5 17:34:54 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Pieralisi X-Patchwork-Id: 36886 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-qc0-f199.google.com (mail-qc0-f199.google.com [209.85.216.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id D0D09202E4 for ; Fri, 5 Sep 2014 17:35:08 +0000 (UTC) Received: by mail-qc0-f199.google.com with SMTP id x3sf35966679qcv.6 for ; Fri, 05 Sep 2014 10:35:08 -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=U5c/n+oTH92lkY1TorhZlVDpJ6gGKStfSY+3tdRNjEc=; b=KPtSy4o8OLYswRGSls2+XX9cvJZ39PBTEOTOmGOEbs7t62/wRuYBTjSUCFgBteKMAk TYwy2xP238ZbkVduAM5OAo+VBA32hxVKX2uM1gg6byeteFKGySmi8ZHrT6KxT0YpWu1R wl8//KP+yOxtFFs9gJgBa45nqnNO7txeB54UucWtRIiw/aMQU3qqmywLnvC2uUxFL9jH HPmr+AEmTFnpmDjN4WiEO6HwQR9dWjCsE9f6nFAz48CuMCN83/V+zjsJXCMhCgODW9Ly C9jZfbBAk2pbWc1Hhww184TIKnXWJvv6e58q3HI/lXEubwE4F98D5vK/NSTj28Bh2kW5 WH1A== X-Gm-Message-State: ALoCoQki0fPgZw5FUv4viGzTeZmn7WYcYUhOR3662R8eCExmLg2J7ywlluGjew+O9rUtNojYklDO X-Received: by 10.224.37.6 with SMTP id v6mr7644364qad.9.1409938508652; Fri, 05 Sep 2014 10:35:08 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.109.246 with SMTP id l109ls882447qgf.1.gmail; Fri, 05 Sep 2014 10:35:08 -0700 (PDT) X-Received: by 10.52.36.131 with SMTP id q3mr1778204vdj.90.1409938508490; Fri, 05 Sep 2014 10:35:08 -0700 (PDT) Received: from mail-vc0-f175.google.com (mail-vc0-f175.google.com [209.85.220.175]) by mx.google.com with ESMTPS id uv6si1231870vcb.3.2014.09.05.10.35.08 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 05 Sep 2014 10:35:08 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.175 as permitted sender) client-ip=209.85.220.175; Received: by mail-vc0-f175.google.com with SMTP id lf12so12530941vcb.6 for ; Fri, 05 Sep 2014 10:35:08 -0700 (PDT) X-Received: by 10.220.182.1 with SMTP id ca1mr11860433vcb.21.1409938508394; Fri, 05 Sep 2014 10:35:08 -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.221.45.67 with SMTP id uj3csp116250vcb; Fri, 5 Sep 2014 10:35:07 -0700 (PDT) X-Received: by 10.66.185.49 with SMTP id ez17mr23334997pac.58.1409938504519; Fri, 05 Sep 2014 10:35:04 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id yn9si5337604pac.118.2014.09.05.10.35.03 for ; Fri, 05 Sep 2014 10:35:04 -0700 (PDT) Received-SPF: none (google.com: devicetree-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 S1754118AbaIERfA (ORCPT + 5 others); Fri, 5 Sep 2014 13:35:00 -0400 Received: from service87.mimecast.com ([91.220.42.44]:54720 "EHLO service87.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754739AbaIERe6 (ORCPT ); Fri, 5 Sep 2014 13:34:58 -0400 Received: from cam-owa2.Emea.Arm.com (fw-tnat.cambridge.arm.com [217.140.96.21]) by service87.mimecast.com; Fri, 05 Sep 2014 18:34:56 +0100 Received: from red-moon.cambridge.arm.com ([10.1.255.212]) by cam-owa2.Emea.Arm.com with Microsoft SMTPSVC(6.0.3790.3959); Fri, 5 Sep 2014 18:34:53 +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 , Will Deacon , 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 , Mark Brown , Paul Walmsley , Chander Kashyap , Geoff Levand , Bartlomiej Zolnierkiewicz , Ashwin Chaugule , Lina Iyer Subject: [PATCH v9 5/8] drivers: cpuidle: implement DT based idle states infrastructure Date: Fri, 5 Sep 2014 18:34:54 +0100 Message-Id: <1409938498-17984-6-git-send-email-lorenzo.pieralisi@arm.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1409938498-17984-1-git-send-email-lorenzo.pieralisi@arm.com> References: <1409938498-17984-1-git-send-email-lorenzo.pieralisi@arm.com> X-OriginalArrivalTime: 05 Sep 2014 17:34:53.0746 (UTC) FILETIME=[B3FE3120:01CFC92F] X-MC-Unique: 114090518345601901 Sender: devicetree-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: devicetree@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=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.175 as permitted sender) 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: , On most common ARM systems, the low-power states a CPU can be put into are not discoverable in HW and require device tree bindings to describe power down suspend operations and idle states parameters. In order to enable DT based idle states and configure idle drivers, this patch implements the bulk infrastructure required to parse the device tree idle states bindings and initialize the corresponding CPUidle driver states data. The parsing API accepts a start index that defines the first idle state that should be initialized by the parsing code in order to give new and legacy driver flexibility over which states should be parsed using the new DT mechanism. The idle states node(s) is obtained from the phandle list of the first cpu in the driver cpumask; the kernel checks that the idle state node phandle is the same for all CPUs in the driver cpumask before declaring the idle state as valid and start parsing its content. The idle state enter function pointer is initialized through DT match structures passed in by the CPUidle driver, so that ARM legacy code can cope with platform specific idle entry method based on compatible string matching and the code used to initialize the enter function pointer can be moved to the DT generic layer. Acked-by: Catalin Marinas Acked-by: Daniel Lezcano Signed-off-by: Lorenzo Pieralisi --- drivers/cpuidle/Kconfig | 3 + drivers/cpuidle/Makefile | 1 + drivers/cpuidle/dt_idle_states.c | 213 +++++++++++++++++++++++++++++++++++++++ drivers/cpuidle/dt_idle_states.h | 7 ++ 4 files changed, 224 insertions(+) create mode 100644 drivers/cpuidle/dt_idle_states.c create mode 100644 drivers/cpuidle/dt_idle_states.h diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 32748c3..8deb934 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -25,6 +25,9 @@ config CPU_IDLE_GOV_MENU bool "Menu governor (for tickless system)" default y +config DT_IDLE_STATES + bool + menu "ARM CPU Idle Drivers" depends on ARM source "drivers/cpuidle/Kconfig.arm" diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 11edb31..002b653 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -4,6 +4,7 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o +obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o ################################################################################## # ARM SoC drivers diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c new file mode 100644 index 0000000..52f4d11 --- /dev/null +++ b/drivers/cpuidle/dt_idle_states.c @@ -0,0 +1,213 @@ +/* + * DT idle states parsing code. + * + * 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) "DT idle-states: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "dt_idle_states.h" + +static int init_state_node(struct cpuidle_state *idle_state, + const struct of_device_id *matches, + struct device_node *state_node) +{ + int err; + const struct of_device_id *match_id; + + match_id = of_match_node(matches, state_node); + if (!match_id) + return -ENODEV; + /* + * CPUidle drivers are expected to initialize the const void *data + * pointer of the passed in struct of_device_id array to the idle + * state enter function. + */ + idle_state->enter = match_id->data; + + err = of_property_read_u32(state_node, "wakeup-latency-us", + &idle_state->exit_latency); + if (err) { + u32 entry_latency, exit_latency; + + err = of_property_read_u32(state_node, "entry-latency-us", + &entry_latency); + if (err) { + pr_debug(" * %s missing entry-latency-us property\n", + state_node->full_name); + return -EINVAL; + } + + err = of_property_read_u32(state_node, "exit-latency-us", + &exit_latency); + if (err) { + pr_debug(" * %s missing exit-latency-us property\n", + state_node->full_name); + return -EINVAL; + } + /* + * If wakeup-latency-us is missing, default to entry+exit + * latencies as defined in idle states bindings + */ + idle_state->exit_latency = entry_latency + exit_latency; + } + + err = of_property_read_u32(state_node, "min-residency-us", + &idle_state->target_residency); + if (err) { + pr_debug(" * %s missing min-residency-us property\n", + state_node->full_name); + return -EINVAL; + } + + idle_state->flags = CPUIDLE_FLAG_TIME_VALID; + if (of_property_read_bool(state_node, "local-timer-stop")) + idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP; + /* + * TODO: + * replace with kstrdup and pointer assignment when name + * and desc become string pointers + */ + strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1); + strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1); + return 0; +} + +/* + * Check that the idle state is uniform across all CPUs in the CPUidle driver + * cpumask + */ +static bool idle_state_valid(struct device_node *state_node, unsigned int idx, + const cpumask_t *cpumask) +{ + int cpu; + struct device_node *cpu_node, *curr_state_node; + bool valid = true; + + /* + * Compare idle state phandles for index idx on all CPUs in the + * CPUidle driver cpumask. Start from next logical cpu following + * cpumask_first(cpumask) since that's the CPU state_node was + * retrieved from. If a mismatch is found bail out straight + * away since we certainly hit a firmware misconfiguration. + */ + for (cpu = cpumask_next(cpumask_first(cpumask), cpumask); + cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) { + cpu_node = of_cpu_device_node_get(cpu); + curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states", + idx); + if (state_node != curr_state_node) + valid = false; + + of_node_put(curr_state_node); + of_node_put(cpu_node); + if (!valid) + break; + } + + return valid; +} + +/** + * dt_init_idle_driver() - Parse the DT idle states and initialize the + * idle driver states array + * @drv: Pointer to CPU idle driver to be initialized + * @matches: Array of of_device_id match structures to search in for + * compatible idle state nodes. The data pointer for each valid + * struct of_device_id entry in the matches array must point to + * a function with the following signature, that corresponds to + * the CPUidle state enter function signature: + * + * int (*)(struct cpuidle_device *dev, + * struct cpuidle_driver *drv, + * int index); + * + * @start_idx: First idle state index to be initialized + * + * If DT idle states are detected and are valid the state count and states + * array entries in the cpuidle driver are initialized accordingly starting + * from index start_idx. + * + * Return: number of valid DT idle states parsed, <0 on failure + */ +int dt_init_idle_driver(struct cpuidle_driver *drv, + const struct of_device_id *matches, + unsigned int start_idx) +{ + struct cpuidle_state *idle_state; + struct device_node *state_node, *cpu_node; + int i, err = 0; + const cpumask_t *cpumask; + unsigned int state_idx = start_idx; + + if (state_idx >= CPUIDLE_STATE_MAX) + return -EINVAL; + /* + * We get the idle states for the first logical cpu in the + * driver mask (or cpu_possible_mask if the driver cpumask is not set) + * and we check through idle_state_valid() if they are uniform + * across CPUs, otherwise we hit a firmware misconfiguration. + */ + cpumask = drv->cpumask ? : cpu_possible_mask; + cpu_node = of_cpu_device_node_get(cpumask_first(cpumask)); + + for (i = 0; ; i++) { + state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + if (!state_node) + break; + + if (!idle_state_valid(state_node, i, cpumask)) { + pr_warn("%s idle state not valid, bailing out\n", + state_node->full_name); + err = -EINVAL; + break; + } + + if (state_idx == CPUIDLE_STATE_MAX) { + pr_warn("State index reached static CPU idle driver states array size\n"); + break; + } + + idle_state = &drv->states[state_idx++]; + err = init_state_node(idle_state, matches, state_node); + if (err) { + pr_err("Parsing idle state node %s failed with err %d\n", + state_node->full_name, err); + err = -EINVAL; + break; + } + of_node_put(state_node); + } + + of_node_put(state_node); + of_node_put(cpu_node); + if (err) + return err; + /* + * Update the driver state count only if some valid DT idle states + * were detected + */ + if (i) + drv->state_count = state_idx; + + /* + * Return the number of present and valid DT idle states, which can + * also be 0 on platforms with missing DT idle states or legacy DT + * configuration predating the DT idle states bindings. + */ + return i; +} +EXPORT_SYMBOL_GPL(dt_init_idle_driver); diff --git a/drivers/cpuidle/dt_idle_states.h b/drivers/cpuidle/dt_idle_states.h new file mode 100644 index 0000000..4818134 --- /dev/null +++ b/drivers/cpuidle/dt_idle_states.h @@ -0,0 +1,7 @@ +#ifndef __DT_IDLE_STATES +#define __DT_IDLE_STATES + +int dt_init_idle_driver(struct cpuidle_driver *drv, + const struct of_device_id *matches, + unsigned int start_idx); +#endif