Received: by 10.223.176.46 with SMTP id f43csp2973530wra; Mon, 22 Jan 2018 06:31:35 -0800 (PST) X-Google-Smtp-Source: AH8x225/1t1JTX974Kg7CaWgeFyNCXqhl18sJggwEARGOOh1uAaw43TL707gxnbJ9+Hqm2EMGUOG X-Received: by 2002:a17:902:bc85:: with SMTP id bb5-v6mr3817435plb.425.1516631495510; Mon, 22 Jan 2018 06:31:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516631495; cv=none; d=google.com; s=arc-20160816; b=CgOnIl5EKEUOhRVGSrOqr7xpQe0yYwm/qLOj0E9vFLO86qRNLqYYFxoQ2OgMjxCqyt GnE+zV30hA9s4zEFkZdYjmTfKkBj5Su26rHwIyLPny6xZndLolZkr/Tkq+y0myQTlcPY sJZ+GkBiYXm9DSGL+nLl25Wv+48Vw/k1nGc1lY9jUJHnL+vtBAothUME9ayW69QPTqxr 82qf6v0TBL1bz0ee395RPu+gpnsUq5v5wi0KprB2Z+yurIThuxH9dVC0KfpPkM9UNHlZ zZ81aTtZeUl+gLrk+WXwnqqhJHP3e6ekQgM0lATV48BNFGkLB/wOcnbN3Lh5YTJyNche Zalg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:cms-type:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature:dkim-filter :arc-authentication-results; bh=MI/YyuMAeL3GlFAUb15YhVj9QSVz6wRA5iveXgm3+gU=; b=XFw7O7q/b81h4zgMIPxQGvCPhmhYWSIKIyZUC7rUdqxI+zgNPlrUbNgxGz2DvUH7Ol KF9O5bKKOMaRvjRi5oyDw7+6xfgwslZD/CFq43xdMI4Z6lyfNplssIJ/kdgGJ2DMnslk l0WqUWE6ucmtfRzVNWwP9vntR3CKaa/SPlpIYyg7pSHkVdUsd5ixiIg7OAGefavbcUXd e/B2ELU+WAMA2diT5n7Slo4jxzKRjeehkacTZS6vtD137d++GRbt6iIcGbFF+xS9jQsV gm2DX5N/cus0VqH9odGN+jJ4ovkF1C0JsagYS/P58AswDXgkecHE57sYmWutA6kDML4Y aL5A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@samsung.com header.s=mail20170921 header.b=pI3bT0H/; 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=samsung.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id y12-v6si3547059pln.497.2018.01.22.06.31.21; Mon, 22 Jan 2018 06:31:35 -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=@samsung.com header.s=mail20170921 header.b=pI3bT0H/; 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=samsung.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751307AbeAVOaq (ORCPT + 99 others); Mon, 22 Jan 2018 09:30:46 -0500 Received: from mailout1.w1.samsung.com ([210.118.77.11]:48208 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751187AbeAVOal (ORCPT ); Mon, 22 Jan 2018 09:30:41 -0500 Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20180122143039euoutp019b7bffbac733bbcc11cdae4a7ef4222b~MKEI0ehGf2788227882euoutp01n; Mon, 22 Jan 2018 14:30:39 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20180122143039euoutp019b7bffbac733bbcc11cdae4a7ef4222b~MKEI0ehGf2788227882euoutp01n DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1516631439; bh=MI/YyuMAeL3GlFAUb15YhVj9QSVz6wRA5iveXgm3+gU=; h=From:To:Cc:Subject:Date:In-reply-to:References:From; b=pI3bT0H/EnNd0wE83BW2sQR86r6agFKITMBUWDgWp/K+g3SxYH9fDNeeGlPn5yNwH /ipf2gZLdRE82Cnri4cn2wOSrOZCqpcKltsiboDn+O+BcmNy60bsYn4ZmaWcgUGgw9 eWdDf8YwztcO97Vyy/CPUe1t1/AV9Rcfi3J2aNS4= Received: from eusmges4.samsung.com (unknown [203.254.199.244]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20180122143038eucas1p2f073e3b432e1e5335e6eece1748c8624~MKEIHmXUp0437904379eucas1p2N; Mon, 22 Jan 2018 14:30:38 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges4.samsung.com (EUCPMTA) with SMTP id 2E.F3.30163.E85F56A5; Mon, 22 Jan 2018 14:30:38 +0000 (GMT) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20180122143037eucas1p2b91d1de50ca84f414aea0c3b83427797~MKEHcbaBj3029530295eucas1p2H; Mon, 22 Jan 2018 14:30:37 +0000 (GMT) X-AuditID: cbfec7f4-f790c6d0000075d3-3a-5a65f58e0b01 Received: from eusync4.samsung.com ( [203.254.199.214]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id 85.89.20118.D85F56A5; Mon, 22 Jan 2018 14:30:37 +0000 (GMT) Received: from AMDC2075.DIGITAL.local ([106.120.51.25]) by eusync4.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0P2Y0079BOAEJF00@eusync4.samsung.com>; Mon, 22 Jan 2018 14:30:37 +0000 (GMT) From: Maciej Purski To: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org Cc: Mark Brown , Liam Girdwood , Rob Herring , Mark Rutland , Marek Szyprowski , Doug Anderson , Bartlomiej Zolnierkiewicz , Maciej Purski Subject: [PATCH v4 6/7] regulator: core: Add voltage balancing mechanism Date: Mon, 22 Jan 2018 15:30:11 +0100 Message-id: <1516631412-17542-7-git-send-email-m.purski@samsung.com> X-Mailer: git-send-email 2.7.4 In-reply-to: <1516631412-17542-1-git-send-email-m.purski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrPIsWRmVeSWpSXmKPExsWy7djP87p9X1OjDF53ClhsnLGe1WLqwyds FvOPnGO1OLvsIJvFtysdTBaXd81hs1jw8haLxdojd9ktll6/yGTRuvcIuwOXx5p5axg9Zjdc ZPHYOesuu8emVZ1sHn1bVjF6fN4kF8AWxWWTkpqTWZZapG+XwJWx58IJloJvNhX/DrUwNzD+ 0O9i5OSQEDCR+H17JxOELSZx4d56ti5GLg4hgaWMEptOz2IESQgJfGaUOHTZFqbhyv7PLBBF yxglLjbCFP1nlLi6WbCLkYODTUBLYk17PEhYRMBG4u2NA4wg9cwCx5kkrk/dCLZNWMBD4sem g2C9LAKqEqfPzmcB6eUVcJHo+O0NsUtO4ua5TmYQm1PAVeJP30SwvRICG9gkzi/vZYYocpGY 0vKWFcIWlnh1fAs7hC0jcXlyNwuEXS1x8esuNgi7RqLx9gaoGmuJz5O2gM1hFuCTmLRtOjPI DRICvBIdbUIQJR4SPb3TGCFsR4l7ZzexQ/w+g1Fizp9VLBMYpRcwMqxiFEktLc5NTy020StO zC0uzUvXS87P3cQIjObT/45/2cG4+JjVIUYBDkYlHt4Og9QoIdbEsuLK3EOMEhzMSiK8aSuA QrwpiZVVqUX58UWlOanFhxilOViUxHlto9oihQTSE0tSs1NTC1KLYLJMHJxSDYwytxpUTu5b p7BtfZHXPDmOqkuTVLKuLkyYE23Ca5/ft2xZob65xaPAn5Pmfyr726s52T5Z+dyUx1L7Ttmz CoXaTny9/Y/o266YhgC7NTLHGEL5xKt/1vQ5H+cNrM++rBReJblQKvrGbYvD7n8a58SaibyR udjtucI4UGeBrnpwsrPs+Z/dB5RYijMSDbWYi4oTAVKL7BniAgAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrGLMWRmVeSWpSXmKPExsVy+t/xa7q9X1OjDC6cNbLYOGM9q8XUh0/Y LOYfOcdqcXbZQTaLb1c6mCwu75rDZrHg5S0Wi7VH7rJbLL1+kcmide8RdgcujzXz1jB6zG64 yOKxc9Zddo9NqzrZPPq2rGL0+LxJLoAtissmJTUnsyy1SN8ugStjz4UTLAXfbCr+HWphbmD8 od/FyMkhIWAicWX/ZxYIW0ziwr31bF2MXBxCAksYJR5vmM0M4TQySax6t5+xi5GDg01AS2JN ezxIg4iAjcTbGwcYQWqYBU4ySfy5uI4RJCEs4CHxY9NBMJtFQFXi9Nn5LCC9vAIuEh2/vSGW yUncPNfJDGJzCrhK/OmbCHaEEFDJ3gdv2Ccw8i5gZFjFKJJaWpybnltspFecmFtcmpeul5yf u4kRGHrbjv3csoOx613wIUYBDkYlHt4Og9QoIdbEsuLK3EOMEhzMSiK8aSuAQrwpiZVVqUX5 8UWlOanFhxilOViUxHl796yOFBJITyxJzU5NLUgtgskycXBKNTCafNoX76KwXS5tTuHcNGcp idxIQ5mVYZyz9eq1s7u+n96/ZNPrT0vfLXw2+VCFhWl54vbqsEsFXzYLeu1x3HnaNUB0qrf9 5e7lpypnSu36/2pplMbVhVnqwb6NLS6m6g63Juhfe3l4HpeF7nxL61cXf7kdVIhaMEe+fs5K ridnZnzguCLmUmWlxFKckWioxVxUnAgAxs9+8jkCAAA= X-CMS-MailID: 20180122143037eucas1p2b91d1de50ca84f414aea0c3b83427797 X-Msg-Generator: CA CMS-TYPE: 201P X-CMS-RootMailID: 20180122143037eucas1p2b91d1de50ca84f414aea0c3b83427797 X-RootMTR: 20180122143037eucas1p2b91d1de50ca84f414aea0c3b83427797 References: <1516631412-17542-1-git-send-email-m.purski@samsung.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Odroid XU3/4 and other Exynos5422 based boards there is a case, that different devices on the board are supplied by different regulators with non-fixed voltages. If one of these devices temporarily requires higher voltage, there might occur a situation that the spread between two devices' voltages is so high, that there is a risk of changing 'high' and 'low' states on the interconnection between devices powered by those regulators. Introduce new function regulator_balance_voltage(), which keeps max_spread constraint fulfilled between a group of coupled regulators. It should be called if a regulator changes its voltage or after disabling or enabling. Disabled regulators should follow changes of the enabled ones, but their consumers' demands shouldn't be taken into account while calculating voltage of other coupled regulators. Find voltages, which are closest to suiting all the consumers' demands, while fulfilling max_spread constraint, keeping the following rules: - if one regulator is about to rise its voltage, rise others voltages in order to keep the max_spread - if a regulator, which has caused rising other regulators, is lowered, lower other regulators if possible - if one regulator is about to lower its voltage, but it hasn't caused rising other regulators, don't change its voltage if it breaks the max_spread Change regulators' voltages step by step, keeping max_spread constraint fulfilled all the time. Function regulator_get_optimal_voltage() should find the best possible change for the regulator, which doesn't break max_spread constraint. In function regulator_balance_voltage() optimize number of steps by finding highest voltage difference on each iteration. If a regulator, which is about to change its voltage, is not coupled, method regulator_get_optimal_voltage() should simply return the lowest voltage fulfilling consumers' demands. Coupling should be checked only if the system is in PM_SUSPEND_ON state. Signed-off-by: Maciej Purski --- drivers/regulator/core.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 3628949..0492b0e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -105,6 +105,8 @@ static int _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV); +static int regulator_balance_voltage(struct regulator_dev *rdev, + suspend_state_t state); static struct regulator *create_regulator(struct regulator_dev *rdev, struct device *dev, const char *supply_name); @@ -2995,6 +2997,196 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, return ret; } +static int regulator_get_optimal_voltage(struct regulator_dev *rdev) +{ + struct coupling_desc *c_desc = &rdev->coupling_desc; + struct regulator_dev **c_rdevs = c_desc->coupled_rdevs; + int max_spread = rdev->constraints->max_spread; + int n_coupled = c_desc->n_coupled; + int desired_min_uV, desired_max_uV, min_current_uV = INT_MAX; + int max_current_uV = 0, highest_min_uV = 0, target_uV, possible_uV; + int i, ret; + + /* If consumers don't provide any demands, set voltage to min_uV */ + desired_min_uV = rdev->constraints->min_uV; + desired_max_uV = rdev->constraints->max_uV; + ret = regulator_check_consumers(rdev, + &desired_min_uV, + &desired_max_uV, PM_SUSPEND_ON); + if (ret < 0) + goto out; + + /* + * If there are no coupled regulators, simply set the voltage demanded + * by consumers. + */ + if (n_coupled == 1) { + ret = desired_min_uV; + goto out; + } + + /* Find highest min desired voltage */ + for (i = 0; i < n_coupled; i++) { + int tmp_min = 0; + int tmp_max = INT_MAX; + + if (!_regulator_is_enabled(c_rdevs[i])) + continue; + + ret = regulator_check_consumers(c_rdevs[i], + &tmp_min, + &tmp_max, PM_SUSPEND_ON); + if (ret < 0) + goto out; + + if (tmp_min > highest_min_uV) + highest_min_uV = tmp_min; + } + + /* + * Let target_uV be equal to the desired one if possible. + * If not, set it to minimum voltage, allowed by other coupled + * regulators. + */ + target_uV = max(desired_min_uV, highest_min_uV - max_spread); + + /* + * Find min and max voltages, which currently aren't + * violating max_spread + */ + for (i = 0; i < n_coupled; i++) { + int tmp_act; + + /* + * Don't check the regulator, which is about + * to change voltage + */ + if (c_rdevs[i] == rdev) + continue; + if (!_regulator_is_enabled(c_rdevs[i])) + continue; + + tmp_act = _regulator_get_voltage(c_rdevs[i]); + if (tmp_act < 0) { + ret = tmp_act; + goto out; + } + + if (tmp_act < min_current_uV) + min_current_uV = tmp_act; + + if (tmp_act > max_current_uV) + max_current_uV = tmp_act; + } + + /* There aren't any other regulators enabled */ + if (max_current_uV == 0) { + possible_uV = target_uV; + } else { + /* + * Correct target voltage, so as it currently isn't + * violating max_spread + */ + possible_uV = max(target_uV, max_current_uV - max_spread); + possible_uV = min(possible_uV, min_current_uV + max_spread); + } + + if (possible_uV > desired_max_uV) { + ret = -EINVAL; + goto out; + } + ret = possible_uV; + +out: + return ret; +} + +static int regulator_balance_voltage(struct regulator_dev *rdev, + suspend_state_t state) +{ + struct regulator_dev **c_rdevs; + struct regulator_dev *best_rdev; + struct coupling_desc *c_desc = &rdev->coupling_desc; + int n_coupled; + int i, best_delta, best_uV, ret = 1; + + c_rdevs = c_desc->coupled_rdevs; + n_coupled = c_desc->n_coupled; + + /* + * if system is in a state other than PM_SUSPEND_ON, don't check + * other coupled regulators + */ + if (state != PM_SUSPEND_ON) + n_coupled = 1; + + /* + * Find the best possible voltage change on each loop. Leave the loop + * if there isn't any possible change. + */ + while (1) { + best_delta = 0; + best_uV = 0; + best_rdev = NULL; + + /* + * Find highest difference between optimal voltage + * and current voltage. + */ + for (i = 0; i < n_coupled; i++) { + /* + * optimal_uV is the best voltage that can be set for + * i-th regulator at the moment without violating + * max_spread constraint in order to balance + * the coupled voltages. + */ + int optimal_uV, current_uV; + + optimal_uV = regulator_get_optimal_voltage(c_rdevs[i]); + if (optimal_uV < 0) { + ret = optimal_uV; + goto out; + } + + current_uV = _regulator_get_voltage(c_rdevs[i]); + if (current_uV < 0) { + ret = optimal_uV; + goto out; + } + + if (abs(best_delta) < abs(optimal_uV - current_uV)) { + best_delta = optimal_uV - current_uV; + best_rdev = c_rdevs[i]; + best_uV = optimal_uV; + } + } + + /* Nothing to change, return successfully */ + if (!best_rdev) { + ret = 0; + goto out; + } + + /* + * Lock just the supply regulators, as the regulator itself + * is already locked by regulator_lock_coupled(). + */ + if (best_rdev->supply) + regulator_lock_supply(best_rdev->supply->rdev); + + ret = regulator_set_voltage_rdev(best_rdev, best_uV, + best_uV, state); + if (best_rdev->supply) + regulator_unlock_supply(best_rdev->supply->rdev); + + if (ret < 0) + goto out; + } + +out: + return ret; +} + /** * regulator_set_voltage - set regulator output voltage * @regulator: regulator source -- 2.7.4