From patchwork Thu Jun 2 06:16:14 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingbo Wen X-Patchwork-Id: 69128 Delivered-To: patch@linaro.org Received: by 10.140.23.41 with SMTP id 38csp166049qgo; Wed, 1 Jun 2016 23:17:20 -0700 (PDT) X-Received: by 10.98.86.151 with SMTP id h23mr1429009pfj.16.1464848240465; Wed, 01 Jun 2016 23:17:20 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id li15si16727412pab.191.2016.06.01.23.17.20; Wed, 01 Jun 2016 23:17:20 -0700 (PDT) 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; 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 dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752881AbcFBGRP (ORCPT + 30 others); Thu, 2 Jun 2016 02:17:15 -0400 Received: from mail-pf0-f181.google.com ([209.85.192.181]:32990 "EHLO mail-pf0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751888AbcFBGRN (ORCPT ); Thu, 2 Jun 2016 02:17:13 -0400 Received: by mail-pf0-f181.google.com with SMTP id b124so27426373pfb.0 for ; Wed, 01 Jun 2016 23:17:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=jqaIdACBkL7rH2j/08zjjlo4xTqR7nnqmTXeTz8frd4=; b=fE1LNwyJH55aA+x/iToU5jd6IC0zHTdnOLcnxcjuXYmnu2oLJv3EE0ysK+z5eahMNe JhhCcbgXOpTh+efkcZSG4Y1oWVATucI4Q3/Fjyk5Y2HW8VyBxqkbnZJGWoD4dokCxojg jwnhUDAdKyBGo7o5ITZxYn461eJq6AEk1gi6Q= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=jqaIdACBkL7rH2j/08zjjlo4xTqR7nnqmTXeTz8frd4=; b=cM5KXgRkJXyajyZ1n5PEnajR8KRo+bzTZChDc6WD3c+GT8eBJFcyQTiOiWArQydGle 5Hk7h+6XDjcrfJR509j2sFNyCiWh0sW9a+PaEsNOEwr6/ru8xmJXfTMaGYtcfJFZFyfl qNBYitTAlyyn1MWKDk2qpAsHCSSlEE0bGZjtDvi/7Ph2k4zr19OHr9SkWUIfTTJDCvX5 VQFTE29pDv7ldLgkrKDpDhS00qPqpIBABFpQdh547R2SU84Ls+YY3W4elOGF6AuXZ3DI PdzWkAYU2CMMcJDQihvKJfrVX4T7u+h0IbOE/6PI+qpIB2fF1udKcT8AEVeZ/00l1I3J uT1Q== X-Gm-Message-State: ALyK8tLdIm1aFc7I0qMPCe49a8RLMlZOyBKFbEvBuC6ba2ZlMs8NIqRYyojtJrwf6HETZNtU X-Received: by 10.98.58.84 with SMTP id h81mr1355505pfa.93.1464848232064; Wed, 01 Jun 2016 23:17:12 -0700 (PDT) Received: from localhost.localdomain ([202.130.115.78]) by smtp.gmail.com with ESMTPSA id l67sm52416788pfi.10.2016.06.01.23.17.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 01 Jun 2016 23:17:11 -0700 (PDT) From: WEN Pingbo To: linux-kernel@vger.kernel.org Cc: broonie@kernel.org, lgirdwood@gmail.com, vincent.guittot@linaro.org, WEN Pingbo , Stephen Boyd , David Collins Subject: [RESEND][RFC PATCH] regulator: introduce boot protection flag Date: Thu, 2 Jun 2016 14:16:14 +0800 Message-Id: <1464848174-17721-1-git-send-email-pingbo.wen@linaro.org> X-Mailer: git-send-email 1.9.1 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org In some platforms, critical shared regulator is initialized in bootloader. But during kernel booting, the driver probing order and conflicting operations from other regulator consumers, may set the regulator in a undefined state, which will cause serious problem. This patch try to add a boot_protection flag in regulator constraints. And regulator core will postpone all operations until all consumers have taked their place. The boot_protection flag only work before late_initicall. And as other constraints liked, you can specify this flag in a board file, or in dts file. Signed-off-by: WEN Pingbo Cc: Stephen Boyd Cc: David Collins --- drivers/regulator/core.c | 106 +++++++++++++++++++++++++++++++++++--- drivers/regulator/internal.h | 2 + drivers/regulator/of_regulator.c | 3 ++ include/linux/regulator/driver.h | 3 ++ include/linux/regulator/machine.h | 1 + 5 files changed, 109 insertions(+), 6 deletions(-) -- 1.9.1 diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index fe47d38..f994a0f 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -55,6 +55,7 @@ static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); static LIST_HEAD(regulator_supply_alias_list); static bool has_full_constraints; +static bool regulator_has_booted; static struct dentry *debugfs_root; @@ -1030,6 +1031,13 @@ static int set_machine_constraints(struct regulator_dev *rdev, if (!rdev->constraints) return -ENOMEM; + /* + * If a regulator driver is registered after late_initcall, the + * boot_protection should be ingnored. + */ + if (regulator_has_booted) + rdev->constraints->boot_protection = 0; + ret = machine_constraints_voltage(rdev, rdev->constraints); if (ret != 0) return ret; @@ -2195,8 +2203,14 @@ static int _regulator_disable(struct regulator_dev *rdev) if (rdev->use_count == 1 && (rdev->constraints && !rdev->constraints->always_on)) { - /* we are last user */ - if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) { + /* + * We are last user. + * + * If boot_protection is set, we only clear use_count, + * and regulator_init_complete() will disable it. + */ + if (!rdev->constraints->boot_protection && + regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) { ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_DISABLE, NULL); @@ -2297,6 +2311,10 @@ int regulator_force_disable(struct regulator *regulator) struct regulator_dev *rdev = regulator->rdev; int ret; + WARN(rdev->constraints->boot_protection, + "disable regulator %s with boot protection flag\n", + rdev->desc->name); + mutex_lock(&rdev->mutex); regulator->uA_load = 0; ret = _regulator_force_disable(regulator->rdev); @@ -2852,6 +2870,10 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, if (ret < 0) goto out2; + /* We need to change voltage, but boot_protection is set. */ + if (rdev->constraints->boot_protection) + goto out; + if (rdev->supply && (rdev->desc->min_dropout_uV || !rdev->desc->ops->get_voltage)) { int current_supply_uV; @@ -3069,6 +3091,9 @@ int regulator_sync_voltage(struct regulator *regulator) if (ret < 0) goto out; + if (rdev->constraints->boot_protection) + goto out; + ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); out: @@ -3161,6 +3186,15 @@ int regulator_set_current_limit(struct regulator *regulator, if (ret < 0) goto out; + /* + * Stage new current value, and applied it later. + */ + if (rdev->constraints->boot_protection) { + regulator->min_uA = min_uA; + regulator->max_uA = max_uA; + goto out; + } + ret = rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA); out: mutex_unlock(&rdev->mutex); @@ -3240,6 +3274,11 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode) if (ret < 0) goto out; + if (rdev->constraints->boot_protection) { + rdev->boot_mode = mode; + goto out; + } + ret = rdev->desc->ops->set_mode(rdev, mode); out: mutex_unlock(&rdev->mutex); @@ -3306,11 +3345,14 @@ EXPORT_SYMBOL_GPL(regulator_get_mode); int regulator_set_load(struct regulator *regulator, int uA_load) { struct regulator_dev *rdev = regulator->rdev; - int ret; + int ret = 0; mutex_lock(&rdev->mutex); regulator->uA_load = uA_load; - ret = drms_uA_update(rdev); + + if (!rdev->constraints->boot_protection) + ret = drms_uA_update(rdev); + mutex_unlock(&rdev->mutex); return ret; @@ -3344,7 +3386,8 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable) if (enable && !regulator->bypass) { rdev->bypass_count++; - if (rdev->bypass_count == rdev->open_count) { + if (rdev->bypass_count == rdev->open_count && + !rdev->constraints->boot_protection) { ret = rdev->desc->ops->set_bypass(rdev, enable); if (ret != 0) rdev->bypass_count--; @@ -3353,7 +3396,8 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable) } else if (!enable && regulator->bypass) { rdev->bypass_count--; - if (rdev->bypass_count != rdev->open_count) { + if (rdev->bypass_count != rdev->open_count && + !rdev->constraints->boot_protection) { ret = rdev->desc->ops->set_bypass(rdev, enable); if (ret != 0) rdev->bypass_count++; @@ -4346,6 +4390,51 @@ static int __init regulator_init(void) /* init early to allow our consumers to complete system booting */ core_initcall(regulator_init); +static void __init regulator_clear_boot_protection(struct regulator_dev *rdev) +{ + struct regulator *regulator; + int min_uA = INT_MAX, max_uA = 0; + + mutex_lock(&rdev->mutex); + + rdev->constraints->boot_protection = 0; + + /* update current setting */ + list_for_each_entry(regulator, &rdev->consumer_list, list) { + if (regulator->min_uA < min_uA) + min_uA = regulator->min_uA; + if (regulator->max_uA > max_uA) + max_uA = regulator->max_uA; + } + + if (max_uA && !regulator_check_current_limit(rdev, &min_uA, &max_uA)) + rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA); + + /* constraints check has already done */ + if (rdev->boot_mode) + rdev->desc->ops->set_mode(rdev, rdev->boot_mode); + + /* update regulator load */ + drms_uA_update(rdev); + + /* check if we need to set bypass mode */ + if (rdev->desc->ops->set_bypass && rdev->bypass_count && + regulator_ops_is_valid(rdev, REGULATOR_CHANGE_BYPASS)) { + if (rdev->bypass_count == rdev->open_count) + rdev->desc->ops->set_bypass(rdev, true); + else + rdev->desc->ops->set_bypass(rdev, false); + } + + regulator = list_first_entry_or_null(&rdev->consumer_list, + struct regulator, list); + mutex_unlock(&rdev->mutex); + + if (regulator) + regulator_set_voltage(regulator, regulator->min_uV, + regulator->max_uV); +} + static int __init regulator_late_cleanup(struct device *dev, void *data) { struct regulator_dev *rdev = dev_to_rdev(dev); @@ -4353,6 +4442,9 @@ static int __init regulator_late_cleanup(struct device *dev, void *data) struct regulation_constraints *c = rdev->constraints; int enabled, ret; + if (c->boot_protection) + regulator_clear_boot_protection(rdev); + if (c && c->always_on) return 0; @@ -4406,6 +4498,8 @@ static int __init regulator_init_complete(void) if (of_have_populated_dt()) has_full_constraints = true; + regulator_has_booted = true; + /* If we have a full configuration then disable any regulators * we have permission to change the status for and which are * not in use or always_on. This is effectively the default diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index c74ac87..ab81a71 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -29,6 +29,8 @@ struct regulator { int uA_load; int min_uV; int max_uV; + int min_uA; + int max_uA; char *supply_name; struct device_attribute dev_attr; struct regulator_dev *rdev; diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 6b0aa80..95fd789 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -78,6 +78,9 @@ static void of_get_regulation_constraints(struct device_node *np, if (of_property_read_bool(np, "regulator-allow-set-load")) constraints->valid_ops_mask |= REGULATOR_CHANGE_DRMS; + constraints->boot_protection = of_property_read_bool(np, + "regulator-boot-protection"); + ret = of_property_read_u32(np, "regulator-ramp-delay", &pval); if (!ret) { if (pval) diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index cd271e8..ddb80a3 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -389,6 +389,9 @@ struct regulator_dev { u32 open_count; u32 bypass_count; + /* save mode during boot protection */ + unsigned int boot_mode; + /* lists we belong to */ struct list_head list; /* list of all regulators */ diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 5d627c8..fd88a12 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -155,6 +155,7 @@ struct regulation_constraints { /* constraint flags */ unsigned always_on:1; /* regulator never off when system is on */ unsigned boot_on:1; /* bootloader/firmware enabled regulator */ + unsigned boot_protection:1; /* protect regulator initialized by bootloader */ unsigned apply_uV:1; /* apply uV constraint if min == max */ unsigned ramp_disable:1; /* disable ramp delay */ unsigned soft_start:1; /* ramp voltage slowly */