Received: by 10.223.185.111 with SMTP id b44csp246353wrg; Fri, 9 Mar 2018 04:25:34 -0800 (PST) X-Google-Smtp-Source: AG47ELveV0AUQbdFOxxbqwhg1iv+0CpnErOjZDkMnGsCd2RsUIjMJTRr1d2UDpk5T7g5qPKlguSQ X-Received: by 10.98.234.22 with SMTP id t22mr29907692pfh.56.1520598334101; Fri, 09 Mar 2018 04:25:34 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1520598334; cv=none; d=google.com; s=arc-20160816; b=iaFvqIvCjQrSM69Z8BMsUT9QbiVAVTiCi2LHfhdmbBrlH+1DHoBplV3oZGnW9Z532x MYzjLjOkDGBEyYnrAvZk1trDz6b/D1JHZg2CuOaYXLfbKTonfIjalwJyvouY0qdlcm/r 6/+tH4ZGgNAhppKl1U/bBkqX5lWKhBNyU0hLeN6KmSK39WxshGpTvZh95jHMd3dIGtyC HiPpyVnuMm5SMfrumKA8KrGgl0iy5KlYshXk8Stox6TviwSTIILUy47fMvcYoVm55r0I 7s+ky98awQf4mlRb2wfNiqMv1RCvUemVGT/aGJLqQ7IDII0sodFQbCCBO7HPgWrsG+Ch 4sOA== 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=KGXiXoio9TAsMhCxVEuzWdjaLZxrKSq/orEt7NuFMZU=; b=WptnOiGnPBZwQtoFJgaia8smZeYouKE4TfzVb2GhG6urb8gImwhbUB9TwfjMUf3XSw UVmnV4JLMDOQrVZttKMKWaAjfvcXKIY/lP3Dvs1NWtrJrJ1509ZCdDq5i6YI8rdYAnDb DrDuvLqQs/IKRpPHpdSpvASmK9hPKh83+1xAKiNdYQD21N6hy9kTHZNxs1Z+eAY/QU4Y zLLlkSXoVmpJF/Fz60TaPZleNWIVClK+vPVHP+yuC7e5tvyjtaPsFTP2/xqfPg4GL8C1 jZSIq4m79g0gWpZtD66vIALGF2Zy7nBqg/PDBT0XVNn7H9F8VNRAqzHjLSHhAq0A9utS yOjQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@samsung.com header.s=mail20170921 header.b=eUJ+l9Xt; 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 g17-v6si762393plo.655.2018.03.09.04.25.19; Fri, 09 Mar 2018 04:25:34 -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=eUJ+l9Xt; 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 S1751303AbeCIMX2 (ORCPT + 99 others); Fri, 9 Mar 2018 07:23:28 -0500 Received: from mailout2.w1.samsung.com ([210.118.77.12]:56006 "EHLO mailout2.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751122AbeCIMWi (ORCPT ); Fri, 9 Mar 2018 07:22:38 -0500 Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20180309122236euoutp02bb8a91cdff46b5122b9bb81308be9c52~aP-d3AoGl3047930479euoutp02W; Fri, 9 Mar 2018 12:22:36 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20180309122236euoutp02bb8a91cdff46b5122b9bb81308be9c52~aP-d3AoGl3047930479euoutp02W DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1520598156; bh=KGXiXoio9TAsMhCxVEuzWdjaLZxrKSq/orEt7NuFMZU=; h=From:To:Cc:Subject:Date:In-reply-to:References:From; b=eUJ+l9Xt0zww7FjL2DcPw+hTdzWfrJm5YlL6vzcsqilZIL+rYFFA8go4oe2g7IuTW IkiVGvcgSWdzKqCSTTMJJb2390/pa7VZ7zLY8IORLepfbXuMkNtL3ucoRsUrRTDrGa i0O8oGo8B38tc9SUP4e9j2bNJ+vj0Qev4cll4NoI= Received: from eusmges3new.samsung.com (unknown [203.254.199.245]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20180309122235eucas1p180f4d7a67bd44d86772ec72132bbc68d~aP-dGlXPm2797727977eucas1p1n; Fri, 9 Mar 2018 12:22:35 +0000 (GMT) Received: from eucas1p1.samsung.com ( [182.198.249.206]) by eusmges3new.samsung.com (EUCPMTA) with SMTP id 99.47.10409.B8C72AA5; Fri, 9 Mar 2018 12:22:35 +0000 (GMT) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20180309122234eucas1p1cc52e61f0930c4e0d4ec64784b601626~aP-cVT5Op2625326253eucas1p1q; Fri, 9 Mar 2018 12:22:34 +0000 (GMT) X-AuditID: cbfec7f5-b5fff700000028a9-ea-5aa27c8bfe2b Received: from eusync1.samsung.com ( [203.254.199.211]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id 78.F8.04183.A8C72AA5; Fri, 9 Mar 2018 12:22:34 +0000 (GMT) Received: from AMDC2075.DIGITAL.local ([106.120.51.25]) by eusync1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0P5B00JEWP1FM310@eusync1.samsung.com>; Fri, 09 Mar 2018 12:22:34 +0000 (GMT) From: Maciej Purski To: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org Cc: Mark Brown , Fabio Estevam , Tony Lindgren , Liam Girdwood , Rob Herring , Mark Rutland , Marek Szyprowski , Doug Anderson , Bartlomiej Zolnierkiewicz , Maciej Purski Subject: [PATCH v6 5/6] regulator: core: Add voltage balancing mechanism Date: Fri, 09 Mar 2018 13:22:07 +0100 Message-id: <1520598128-11768-6-git-send-email-m.purski@samsung.com> X-Mailer: git-send-email 2.7.4 In-reply-to: <1520598128-11768-1-git-send-email-m.purski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrKIsWRmVeSWpSXmKPExsWy7djPc7rdNYuiDCZtZbfYOGM9q8XUh0/Y LOYfOcdqcXbZQTaLh1f9Lb5d6WCyuLxrDpvFgpe3WCzWHrnLbrH0+kUmi9a9R9gt9l/xcuDx WDNvDaPHt6+TWDxmN1xk8dg56y67x6ZVnWwefVtWMXp83iQXwB7FZZOSmpNZllqkb5fAlbH/ xVX2gu82FQ2rXzA2MP7U72Lk5JAQMJFYNHkyWxcjF4eQwApGidsTepghnM+MEhM27GSGqdqy bgYjRGIZo8Sa9vuMIAkhgf+MEtu/FXcxcnCwCWgBxeNBwiICNhJvbxwAq2cW6GeWmNzUygKS EBbwkHjW/pcJxGYRUJU4/mIRG4jNK+AiseXPK0aIZXISN891gi3mFHCVOHf9DdggCYEdbBI3 Wn6xQhS5SCy8tYcNwhaWeHV8CzuELSNxeXI3C4RdLXHx6y6omhqJxtsboGqsJT5P2gK2gFmA T2LStunMIA9ICPBKdLQJQZR4SHzr2wV1jyPQbT+hQTSDUaKvaxHjBEapBYwMqxjFU0uLc9NT i43zUsv1ihNzi0vz0vWS83M3MQIj+/S/4193MO77k3SIUYCDUYmH94Hjwigh1sSy4srcQ4wS HMxKIrxVFYuihHhTEiurUovy44tKc1KLDzFKc7AoifPGadRFCQmkJ5akZqemFqQWwWSZODil Ghgb2dunMXrtemvubxbsod+m6yrRmBKx8PfcC0JLdxb1Sim0SNgfCFDIDLereGqxY9qSvk0l gTZv77Z+dlHKVC1d0Z54oK7dTenslfRA4dc7BHaJSXLHpNw/YZTyVz5yQ2RcvatJ9y25B8nC 2lNbzzfEcSnnv6hbfvnhQi3Wl7m/lhj5vYwMV2Ipzkg01GIuKk4EAG2+wSXoAgAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrBLMWRmVeSWpSXmKPExsVy+t/xy7pdNYuiDC5/UbDYOGM9q8XUh0/Y LOYfOcdqcXbZQTaLh1f9Lb5d6WCyuLxrDpvFgpe3WCzWHrnLbrH0+kUmi9a9R9gt9l/xcuDx WDNvDaPHt6+TWDxmN1xk8dg56y67x6ZVnWwefVtWMXp83iQXwB7FZZOSmpNZllqkb5fAlbH/ xVX2gu82FQ2rXzA2MP7U72Lk5JAQMJHYsm4GYxcjF4eQwBJGiZlL2tkhnEYmiYO9V9m6GDk4 2AS0JNa0x4M0iAjYSLy9cQCsgVlgIrPEwsVXWEESwgIeEs/a/zKB2CwCqhLHXyxiA7F5BVwk tvx5xQixTU7i5rlOZhCbU8BV4tz1N2BxIaCajhnP2Scw8ixgZFjFKJJaWpybnltspFecmFtc mpeul5yfu4kRGH7bjv3csoOx613wIUYBDkYlHt4HjgujhFgTy4orcw8xSnAwK4nwVlUsihLi TUmsrEotyo8vKs1JLT7EKM3BoiTOe96gMkpIID2xJDU7NbUgtQgmy8TBKdXAKP3z5nRX/19H JbZ5+W2e61cpxNjU8OqQhXETa5emy+4aTcYbM1NtIgs6/8xs+LDB1nnv3HkC8pJJiy8dd45c yv+h+tFdn+vzJrBkZcvN0I86wn9k9abLfHNvnjMUeVjFyuiyaPHfY9VKBon2ZRcnqqUrBfJt dV3uLnbh9/QFUgsnM6cLZ7DVK7EUZyQaajEXFScCAL0B38k7AgAA X-CMS-MailID: 20180309122234eucas1p1cc52e61f0930c4e0d4ec64784b601626 X-Msg-Generator: CA CMS-TYPE: 201P X-CMS-RootMailID: 20180309122234eucas1p1cc52e61f0930c4e0d4ec64784b601626 X-RootMTR: 20180309122234eucas1p1cc52e61f0930c4e0d4ec64784b601626 References: <1520598128-11768-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 60fb05d..1b1e0375 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); @@ -3090,6 +3092,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