cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

exynos-regulator-coupler.c (5380B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
      4 *	      http://www.samsung.com/
      5 * Author: Marek Szyprowski <m.szyprowski@samsung.com>
      6 *
      7 * Simplified generic voltage coupler from regulator core.c
      8 * The main difference is that it keeps current regulator voltage
      9 * if consumers didn't apply their constraints yet.
     10 */
     11
     12#include <linux/init.h>
     13#include <linux/kernel.h>
     14#include <linux/of.h>
     15#include <linux/regulator/coupler.h>
     16#include <linux/regulator/driver.h>
     17#include <linux/regulator/machine.h>
     18
     19static int regulator_get_optimal_voltage(struct regulator_dev *rdev,
     20					 int *current_uV,
     21					 int *min_uV, int *max_uV,
     22					 suspend_state_t state)
     23{
     24	struct coupling_desc *c_desc = &rdev->coupling_desc;
     25	struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;
     26	struct regulation_constraints *constraints = rdev->constraints;
     27	int desired_min_uV = 0, desired_max_uV = INT_MAX;
     28	int max_current_uV = 0, min_current_uV = INT_MAX;
     29	int highest_min_uV = 0, target_uV, possible_uV;
     30	int i, ret, max_spread, n_coupled = c_desc->n_coupled;
     31	bool done;
     32
     33	*current_uV = -1;
     34
     35	/* Find highest min desired voltage */
     36	for (i = 0; i < n_coupled; i++) {
     37		int tmp_min = 0;
     38		int tmp_max = INT_MAX;
     39
     40		lockdep_assert_held_once(&c_rdevs[i]->mutex.base);
     41
     42		ret = regulator_check_consumers(c_rdevs[i],
     43						&tmp_min,
     44						&tmp_max, state);
     45		if (ret < 0)
     46			return ret;
     47
     48		if (tmp_min == 0) {
     49			ret = regulator_get_voltage_rdev(c_rdevs[i]);
     50			if (ret < 0)
     51				return ret;
     52			tmp_min = ret;
     53		}
     54
     55		/* apply constraints */
     56		ret = regulator_check_voltage(c_rdevs[i], &tmp_min, &tmp_max);
     57		if (ret < 0)
     58			return ret;
     59
     60		highest_min_uV = max(highest_min_uV, tmp_min);
     61
     62		if (i == 0) {
     63			desired_min_uV = tmp_min;
     64			desired_max_uV = tmp_max;
     65		}
     66	}
     67
     68	max_spread = constraints->max_spread[0];
     69
     70	/*
     71	 * Let target_uV be equal to the desired one if possible.
     72	 * If not, set it to minimum voltage, allowed by other coupled
     73	 * regulators.
     74	 */
     75	target_uV = max(desired_min_uV, highest_min_uV - max_spread);
     76
     77	/*
     78	 * Find min and max voltages, which currently aren't violating
     79	 * max_spread.
     80	 */
     81	for (i = 1; i < n_coupled; i++) {
     82		int tmp_act;
     83
     84		tmp_act = regulator_get_voltage_rdev(c_rdevs[i]);
     85		if (tmp_act < 0)
     86			return tmp_act;
     87
     88		min_current_uV = min(tmp_act, min_current_uV);
     89		max_current_uV = max(tmp_act, max_current_uV);
     90	}
     91
     92	/*
     93	 * Correct target voltage, so as it currently isn't
     94	 * violating max_spread
     95	 */
     96	possible_uV = max(target_uV, max_current_uV - max_spread);
     97	possible_uV = min(possible_uV, min_current_uV + max_spread);
     98
     99	if (possible_uV > desired_max_uV)
    100		return -EINVAL;
    101
    102	done = (possible_uV == target_uV);
    103	desired_min_uV = possible_uV;
    104
    105	/* Set current_uV if wasn't done earlier in the code and if necessary */
    106	if (*current_uV == -1) {
    107		ret = regulator_get_voltage_rdev(rdev);
    108		if (ret < 0)
    109			return ret;
    110		*current_uV = ret;
    111	}
    112
    113	*min_uV = desired_min_uV;
    114	*max_uV = desired_max_uV;
    115
    116	return done;
    117}
    118
    119static int exynos_coupler_balance_voltage(struct regulator_coupler *coupler,
    120					  struct regulator_dev *rdev,
    121					  suspend_state_t state)
    122{
    123	struct regulator_dev **c_rdevs;
    124	struct regulator_dev *best_rdev;
    125	struct coupling_desc *c_desc = &rdev->coupling_desc;
    126	int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev;
    127	unsigned int delta, best_delta;
    128	unsigned long c_rdev_done = 0;
    129	bool best_c_rdev_done;
    130
    131	c_rdevs = c_desc->coupled_rdevs;
    132	n_coupled = c_desc->n_coupled;
    133
    134	/*
    135	 * Find the best possible voltage change on each loop. Leave the loop
    136	 * if there isn't any possible change.
    137	 */
    138	do {
    139		best_c_rdev_done = false;
    140		best_delta = 0;
    141		best_min_uV = 0;
    142		best_max_uV = 0;
    143		best_c_rdev = 0;
    144		best_rdev = NULL;
    145
    146		/*
    147		 * Find highest difference between optimal voltage
    148		 * and current voltage.
    149		 */
    150		for (i = 0; i < n_coupled; i++) {
    151			/*
    152			 * optimal_uV is the best voltage that can be set for
    153			 * i-th regulator at the moment without violating
    154			 * max_spread constraint in order to balance
    155			 * the coupled voltages.
    156			 */
    157			int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0;
    158
    159			if (test_bit(i, &c_rdev_done))
    160				continue;
    161
    162			ret = regulator_get_optimal_voltage(c_rdevs[i],
    163							    &current_uV,
    164							    &optimal_uV,
    165							    &optimal_max_uV,
    166							    state);
    167			if (ret < 0)
    168				goto out;
    169
    170			delta = abs(optimal_uV - current_uV);
    171
    172			if (delta && best_delta <= delta) {
    173				best_c_rdev_done = ret;
    174				best_delta = delta;
    175				best_rdev = c_rdevs[i];
    176				best_min_uV = optimal_uV;
    177				best_max_uV = optimal_max_uV;
    178				best_c_rdev = i;
    179			}
    180		}
    181
    182		/* Nothing to change, return successfully */
    183		if (!best_rdev) {
    184			ret = 0;
    185			goto out;
    186		}
    187
    188		ret = regulator_set_voltage_rdev(best_rdev, best_min_uV,
    189						 best_max_uV, state);
    190
    191		if (ret < 0)
    192			goto out;
    193
    194		if (best_c_rdev_done)
    195			set_bit(best_c_rdev, &c_rdev_done);
    196
    197	} while (n_coupled > 1);
    198
    199out:
    200	return ret;
    201}
    202
    203static int exynos_coupler_attach(struct regulator_coupler *coupler,
    204				 struct regulator_dev *rdev)
    205{
    206	return 0;
    207}
    208
    209static struct regulator_coupler exynos_coupler = {
    210	.attach_regulator = exynos_coupler_attach,
    211	.balance_voltage  = exynos_coupler_balance_voltage,
    212};
    213
    214static int __init exynos_coupler_init(void)
    215{
    216	if (!of_machine_is_compatible("samsung,exynos5800"))
    217		return 0;
    218
    219	return regulator_coupler_register(&exynos_coupler);
    220}
    221arch_initcall(exynos_coupler_init);