From patchwork Thu Jan 4 07:22:48 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chunyan Zhang X-Patchwork-Id: 123395 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp11109218qgn; Wed, 3 Jan 2018 23:24:15 -0800 (PST) X-Google-Smtp-Source: ACJfBotBLWBCTIo6PyJTwNE9guB9Ba2MeZNzk5EBGG+PQ1jMdS3jlpvivyZ5vNi+nDD3RMWfHhSP X-Received: by 10.84.242.69 with SMTP id c5mr3747040pll.73.1515050655135; Wed, 03 Jan 2018 23:24:15 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1515050655; cv=none; d=google.com; s=arc-20160816; b=jRs+H3ORqjXIsCrwAS51lbnNCnIudTXcTvamF65u3qH4eUw9yHPyD5VeeGM8t1iKPX Ftwg/IT9paqQeH08OvMMYnTqw40icKDOhAtDk2gHAVd/EQcIxOVBQ+U2wQgoPBPRyOiT 8tpcsU5VAPNe4cmMxC1sEUspMYyqLdB9VTuf4kCwUiacNZOtu/VZ9K4LarcQLhC46ojx QNHPQOtcD9qXwRcczmgW8CohMzJE7++MUfajP7md2Jkd7fDsC83R3Wxukf8gdAIVVLa7 sQlknL19rm9BKrjulu496tHnsPbwxVz7I3D3T6lmyEdUeVvXdHo4/AM+Z5JuR2UFtABt kMeQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=/DdbEiBqcN7QbxpagQVRdsaYb/A5W7VpoaQjEVHGYWA=; b=kDHpYuXvlT/WyQ6H/gGBxdnZTO404Njk+hNICrWgzHhDoqjnn2Q1lYBDw2GirHhadi s751rdJxzsLfpjrE+tAAh9j+bfy8pQRpJeASTX7SYmzV+ZAvM4ldcETUVQf5xEmkNMGi foc8rrfVykrkIYepgtK3ZkkV6PpgnMPa94mKeOs06KZ5E0MIw4+wy/VCWrcanFS0Ggv7 smIX1rTBus/DlUpOdae210wIV/mpPwjnqSDnIlN/msy/wk81p1UkSsvTSNa+xWsslPiG Z56keFiRaJdAKHpsXD/RmusiO+eaaLLhaTTCIgJ1IBMorjpWjdUYuuya+9PYi4PpafAV u9WQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ZmeLWXLP; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id v10si1897186ply.324.2018.01.03.23.24.14; Wed, 03 Jan 2018 23:24:15 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ZmeLWXLP; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752283AbeADHYL (ORCPT + 28 others); Thu, 4 Jan 2018 02:24:11 -0500 Received: from mail-pf0-f193.google.com ([209.85.192.193]:38153 "EHLO mail-pf0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751788AbeADHX6 (ORCPT ); Thu, 4 Jan 2018 02:23:58 -0500 Received: by mail-pf0-f193.google.com with SMTP id u25so436440pfg.5 for ; Wed, 03 Jan 2018 23:23:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=/DdbEiBqcN7QbxpagQVRdsaYb/A5W7VpoaQjEVHGYWA=; b=ZmeLWXLP0F4pUQB4hgOUHz2GH/PBrYBsNNNQQlJr3v8cc+/0Gqc+gDVJDikiRJTKcr 5CtiXjQvK+7Bh+l6CIVPhgS0maA97pXoE9wDT/J7BMCmWER52qUvYWbiFD9uvf6xR9wN 9+5IisftX+yhOVXALjAE66xkQlXIfwewdp9/M= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=/DdbEiBqcN7QbxpagQVRdsaYb/A5W7VpoaQjEVHGYWA=; b=er9M13vMJq58fjhrXbne9XO2ZFbZ6djx6QOb7mCUtsNC9a+cnmneekH1C/UsFlYCLT OenJBImKiXdbj8Tiuecr5HBvMZjZutqkSSov6ibGygfrm637E7mdNqR6HuwabyCO0OFN tXSheDW0UUtdFGtb7vPg33gvg3e/AFeT5eUwr+7KGBsPKVDwv+9OFfGH8PpslolzV1fr 9W9Y2nI6TaiLE8TuElr5y/jc7OXkqShjVMmMfVcSsJtFlADrbTb3Ls1DZTLQ6lEMHtCu UrMiZY/0/ry0jkHQbJknPcLrpFXabpbbRppYv9UkuYh9c4cjC48OltuTZyZlOfL3QpQ5 CVtA== X-Gm-Message-State: AKGB3mLMbFrHueftcRf4Qs4bhZzKLpA3titm8wVcIQC/DBkM3w35fjGe kthfwpMKnERpwMRKDWEU4lTnbg== X-Received: by 10.101.66.67 with SMTP id d3mr3277461pgq.244.1515050637587; Wed, 03 Jan 2018 23:23:57 -0800 (PST) Received: from ubt.spreadtrum.com ([117.18.48.82]) by smtp.gmail.com with ESMTPSA id s14sm6244456pfa.158.2018.01.03.23.23.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 03 Jan 2018 23:23:56 -0800 (PST) From: Chunyan Zhang To: Mark Brown , Rob Herring Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Ulf Hansson , Chunyan Zhang Subject: [PATCH V2 5/5] regulator: add PM suspend and resume hooks Date: Thu, 4 Jan 2018 15:22:48 +0800 Message-Id: <1515050568-23876-6-git-send-email-zhang.chunyan@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1515050568-23876-1-git-send-email-zhang.chunyan@linaro.org> References: <1515050568-23876-1-git-send-email-zhang.chunyan@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org In this patch, consumers are allowed to set suspend voltage, and this actually just set the "uV" in constraint::regulator_state, when the regulator_suspend_late() was called by PM core through callback when the system is entering into suspend, the regulator device would act suspend activity then. And it assumes that if any consumer set suspend voltage, the regulator device should be enabled in the suspend state. And if the suspend voltage of a regulator device for all consumers was set zero, the regulator device would be off in the suspend state. This patch also provides a new function hook to regulator devices for resuming from suspend states. Signed-off-by: Chunyan Zhang --- drivers/regulator/core.c | 251 +++++++++++++++++++++++++++++++++----- drivers/regulator/of_regulator.c | 14 +++ include/linux/regulator/driver.h | 2 + include/linux/regulator/machine.h | 16 ++- 4 files changed, 251 insertions(+), 32 deletions(-) -- 2.7.4 diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 080c233..3f4d3aa 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -236,6 +236,12 @@ static int regulator_check_voltage(struct regulator_dev *rdev, return 0; } +/* return 0 if the state is valid */ +static int regulator_check_states(suspend_state_t state) +{ + return (state > PM_SUSPEND_MAX || state == PM_SUSPEND_TO_IDLE); +} + /* Make sure we select a voltage that suits the needs of all * regulator consumers */ @@ -327,6 +333,24 @@ static int regulator_mode_constrain(struct regulator_dev *rdev, return -EINVAL; } +static inline struct regulator_state * +regulator_get_suspend_state(struct regulator_dev *rdev, suspend_state_t state) +{ + if (rdev->constraints == NULL) + return NULL; + + switch (state) { + case PM_SUSPEND_STANDBY: + return &rdev->constraints->state_standby; + case PM_SUSPEND_MEM: + return &rdev->constraints->state_mem; + case PM_SUSPEND_MAX: + return &rdev->constraints->state_disk; + default: + return NULL; + } +} + static ssize_t regulator_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -734,9 +758,14 @@ static int drms_uA_update(struct regulator_dev *rdev) } static int suspend_set_state(struct regulator_dev *rdev, - struct regulator_state *rstate) + suspend_state_t state) { int ret = 0; + struct regulator_state *rstate; + + rstate = regulator_get_suspend_state(rdev, state); + if (rstate == NULL) + return -EINVAL; /* If we have no suspend mode configration don't set anything; * only warn if the driver implements set_suspend_voltage or @@ -779,28 +808,8 @@ static int suspend_set_state(struct regulator_dev *rdev, return ret; } } - return ret; -} -/* locks held by caller */ -static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state) -{ - if (!rdev->constraints) - return -EINVAL; - - switch (state) { - case PM_SUSPEND_STANDBY: - return suspend_set_state(rdev, - &rdev->constraints->state_standby); - case PM_SUSPEND_MEM: - return suspend_set_state(rdev, - &rdev->constraints->state_mem); - case PM_SUSPEND_MAX: - return suspend_set_state(rdev, - &rdev->constraints->state_disk); - default: - return -EINVAL; - } + return ret; } static void print_constraints(struct regulator_dev *rdev) @@ -1069,7 +1078,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, /* do we need to setup our suspend state */ if (rdev->constraints->initial_state) { - ret = suspend_prepare(rdev, rdev->constraints->initial_state); + ret = suspend_set_state(rdev, rdev->constraints->initial_state); if (ret < 0) { rdev_err(rdev, "failed to set suspend state\n"); return ret; @@ -2898,6 +2907,35 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, return ret; } +static int _regulator_do_set_suspend_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, suspend_state_t state) +{ + struct regulator_state *rstate; + int uV, sel; + + rstate = regulator_get_suspend_state(rdev, state); + if (rstate == NULL) + return -EINVAL; + + if (!rstate->changeable) + return -EINVAL; + + if (min_uV < rstate->min_uV) + min_uV = rstate->min_uV; + if (max_uV > rstate->max_uV) + max_uV = rstate->max_uV; + + sel = regulator_map_voltage(rdev, min_uV, max_uV); + if (sel < 0) + return sel; + + uV = rdev->desc->ops->list_voltage(rdev, sel); + if (uV >= min_uV && uV <= max_uV) + rstate->uV = uV; + + return 0; +} + static int regulator_set_voltage_unlocked(struct regulator *regulator, int min_uV, int max_uV, suspend_state_t state) @@ -2993,7 +3031,11 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, } } - ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); + if (state == PM_SUSPEND_ON) + ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); + else + ret = _regulator_do_set_suspend_voltage(rdev, min_uV, + max_uV, state); if (ret < 0) goto out2; @@ -3049,6 +3091,92 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) } EXPORT_SYMBOL_GPL(regulator_set_voltage); +static inline int regulator_suspend_toggle(struct regulator_dev *rdev, + suspend_state_t state, bool en) +{ + struct regulator_state *rstate; + + rstate = regulator_get_suspend_state(rdev, state); + if (rstate == NULL) + return -EINVAL; + + if (!rstate->changeable) + return -EINVAL; + + rstate->enabled = en; + + return 0; +} + +static int regulator_suspend_enable(struct regulator_dev *rdev, + suspend_state_t state) +{ + return regulator_suspend_toggle(rdev, state, true); +} + +static int regulator_suspend_disable(struct regulator_dev *rdev, + suspend_state_t state) +{ + struct regulator *regulator; + struct regulator_voltage *voltage; + + /* + * if any consumer wants this regulator device keeping on in + * suspend states, don't set it as disabled. + */ + list_for_each_entry(regulator, &rdev->consumer_list, list) { + voltage = ®ulator->voltage[state]; + if (voltage->min_uV || voltage->max_uV) + return 0; + } + + return regulator_suspend_toggle(rdev, state, false); +} + +static int _regulator_set_suspend_voltage(struct regulator *regulator, + int min_uV, int max_uV, + suspend_state_t state) +{ + int ret; + struct regulator_dev *rdev = regulator->rdev; + + /* + * We assume users want to switch off the regulator device for + * suspend state when setting min_uV and max_uV all with zero. + */ + if (min_uV == 0 && max_uV == 0) { + regulator->voltage[state].min_uV = 0; + regulator->voltage[state].max_uV = 0; + return regulator_suspend_disable(rdev, state); + } + + ret = regulator_suspend_enable(rdev, state); + if (ret < 0) + return ret; + + return regulator_set_voltage_unlocked(regulator, min_uV, max_uV, state); +} + +int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV, + int max_uV, suspend_state_t state) +{ + int ret = 0; + + /* PM_SUSPEND_ON is handled by regulator_set_voltage() */ + if (regulator_check_states(state) || state == PM_SUSPEND_ON) + return -EINVAL; + + regulator_lock_supply(regulator->rdev); + + ret = _regulator_set_suspend_voltage(regulator, min_uV, + max_uV, state); + + regulator_unlock_supply(regulator->rdev); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_suspend_voltage); + /** * regulator_set_voltage_time - get raise/fall time * @regulator: regulator source @@ -3923,12 +4051,6 @@ static void regulator_dev_release(struct device *dev) kfree(rdev); } -static struct class regulator_class = { - .name = "regulator", - .dev_release = regulator_dev_release, - .dev_groups = regulator_dev_groups, -}; - static void rdev_init_debugfs(struct regulator_dev *rdev) { struct device *parent = rdev->dev.parent; @@ -4179,7 +4301,76 @@ void regulator_unregister(struct regulator_dev *rdev) } EXPORT_SYMBOL_GPL(regulator_unregister); +static int _regulator_suspend_late(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + suspend_state_t *state = data; + int ret; + mutex_lock(&rdev->mutex); + ret = suspend_set_state(rdev, *state); + mutex_unlock(&rdev->mutex); + + return ret; +} + +/** + * regulator_suspend_late - prepare regulators for system wide suspend + * @state: system suspend state + * + * Configure each regulator with it's suspend operating parameters for state. + */ +static int regulator_suspend_late(struct device *dev) +{ + suspend_state_t state = pm_suspend_target_state; + + return class_for_each_device(®ulator_class, NULL, &state, + _regulator_suspend_late); +} +static int _regulator_resume_early(struct device *dev, void *data) +{ + int ret = 0; + struct regulator_dev *rdev = dev_to_rdev(dev); + suspend_state_t *state = data; + struct regulator_state *rstate; + + rstate = regulator_get_suspend_state(rdev, *state); + if (rstate == NULL) + return -EINVAL; + + mutex_lock(&rdev->mutex); + + if (rdev->desc->ops->resume_early && + (rstate->enabled == ENABLE_IN_SUSPEND || + rstate->enabled == DISABLE_IN_SUSPEND)) + ret = rdev->desc->ops->resume_early(rdev); + + mutex_unlock(&rdev->mutex); + + return ret; +} + +static int regulator_resume_early(struct device *dev) +{ + suspend_state_t state = pm_suspend_target_state; + + return class_for_each_device(®ulator_class, NULL, &state, + _regulator_resume_early); +} + +static const struct dev_pm_ops __maybe_unused regulator_pm_ops = { + .suspend_late = regulator_suspend_late, + .resume_early = regulator_resume_early, +}; + +static struct class regulator_class = { + .name = "regulator", + .dev_release = regulator_dev_release, + .dev_groups = regulator_dev_groups, +#ifdef CONFIG_PM_SLEEP + .pm = ®ulator_pm_ops, +#endif +}; /** * regulator_has_full_constraints - the system has fully specified constraints * diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 41dad42..a09ef6c 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -184,9 +184,23 @@ static void of_get_regulation_constraints(struct device_node *np, else suspend_state->enabled = DO_NOTHING_IN_SUSPEND; + if (!of_property_read_u32(np, "regulator-suspend-min-microvolt", + &pval)) + suspend_state->min_uV = pval; + + if (!of_property_read_u32(np, "regulator-suspend-max-microvolt", + &pval)) + suspend_state->max_uV = pval; + if (!of_property_read_u32(suspend_np, "regulator-suspend-microvolt", &pval)) suspend_state->uV = pval; + else /* otherwise use min_uV as default suspend voltage */ + suspend_state->uV = suspend_state->min_uV; + + if (of_property_read_bool(suspend_np, + "regulator-changeable-in-suspend")) + suspend_state->changeable = true; if (i == PM_SUSPEND_MEM) constraints->initial_state = PM_SUSPEND_MEM; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 94417b4..4c00486 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -214,6 +214,8 @@ struct regulator_ops { /* set regulator suspend operating mode (defined in consumer.h) */ int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode); + int (*resume_early)(struct regulator_dev *rdev); + int (*set_pull_down) (struct regulator_dev *); }; diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index b4ddb56..f60ec1f 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -42,7 +42,12 @@ struct regulator; #define REGULATOR_CHANGE_DRMS 0x10 #define REGULATOR_CHANGE_BYPASS 0x20 -/* operations in suspend mode */ +/* + * operations in suspend mode + * DO_NOTHING_IN_SUSPEND - the default value + * DISABLE_IN_SUSPEND - turn off regulator in suspend states + * ENABLE_IN_SUSPEND - keep regulator on in suspend states + */ #define DO_NOTHING_IN_SUSPEND (-1) #define DISABLE_IN_SUSPEND 0 #define ENABLE_IN_SUSPEND 1 @@ -61,17 +66,24 @@ enum regulator_active_discharge { * state. One of enabled or disabled must be set for the * configuration to be applied. * - * @uV: Operating voltage during suspend. + * @uV: Default operating voltage during suspend, it can be adjusted + * among + * @min_uV: Minimum suspend voltage may be set. + * @max_uV: Maximum suspend voltage may be set. * @mode: Operating mode during suspend. * @enabled: operations during suspend. * - DO_NOTHING_IN_SUSPEND * - DISABLE_IN_SUSPEND * - ENABLE_IN_SUSPEND + * @changeable: Is this state can be changed. */ struct regulator_state { int uV; /* suspend voltage */ + int min_uV; + int max_uV; unsigned int mode; /* suspend regulator operating mode */ int enabled; /* is regulator enabled in this suspend state */ + bool changeable; }; /**