diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
commit | ace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch) | |
tree | b2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/soc/mediatek/mtk-regulator-coupler.c | |
parent | Initial commit. (diff) | |
download | linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip |
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/soc/mediatek/mtk-regulator-coupler.c')
-rw-r--r-- | drivers/soc/mediatek/mtk-regulator-coupler.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/soc/mediatek/mtk-regulator-coupler.c b/drivers/soc/mediatek/mtk-regulator-coupler.c new file mode 100644 index 0000000000..ad2ed42aa6 --- /dev/null +++ b/drivers/soc/mediatek/mtk-regulator-coupler.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Voltage regulators coupler for MediaTek SoCs + * + * Copyright (C) 2022 Collabora, Ltd. + * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/regulator/coupler.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/suspend.h> + +#define to_mediatek_coupler(x) container_of(x, struct mediatek_regulator_coupler, coupler) + +struct mediatek_regulator_coupler { + struct regulator_coupler coupler; + struct regulator_dev *vsram_rdev; +}; + +/* + * We currently support only couples of not more than two vregs and + * modify the vsram voltage only when changing voltage of vgpu. + * + * This function is limited to the GPU<->SRAM voltages relationships. + */ +static int mediatek_regulator_balance_voltage(struct regulator_coupler *coupler, + struct regulator_dev *rdev, + suspend_state_t state) +{ + struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler); + int max_spread = rdev->constraints->max_spread[0]; + int vsram_min_uV = mrc->vsram_rdev->constraints->min_uV; + int vsram_max_uV = mrc->vsram_rdev->constraints->max_uV; + int vsram_target_min_uV, vsram_target_max_uV; + int min_uV = 0; + int max_uV = INT_MAX; + int ret; + + /* + * If the target device is on, setting the SRAM voltage directly + * is not supported as it scales through its coupled supply voltage. + * + * An exception is made in case the use_count is zero: this means + * that this is the first time we power up the SRAM regulator, which + * implies that the target device has yet to perform initialization + * and setting a voltage at that time is harmless. + */ + if (rdev == mrc->vsram_rdev) { + if (rdev->use_count == 0) + return regulator_do_balance_voltage(rdev, state, true); + + return -EPERM; + } + + ret = regulator_check_consumers(rdev, &min_uV, &max_uV, state); + if (ret < 0) + return ret; + + if (min_uV == 0) { + ret = regulator_get_voltage_rdev(rdev); + if (ret < 0) + return ret; + min_uV = ret; + } + + ret = regulator_check_voltage(rdev, &min_uV, &max_uV); + if (ret < 0) + return ret; + + /* + * If we're asked to set a voltage less than VSRAM min_uV, set + * the minimum allowed voltage on VSRAM, as in this case it is + * safe to ignore the max_spread parameter. + */ + vsram_target_min_uV = max(vsram_min_uV, min_uV + max_spread); + vsram_target_max_uV = min(vsram_max_uV, vsram_target_min_uV + max_spread); + + /* Make sure we're not out of range */ + vsram_target_min_uV = min(vsram_target_min_uV, vsram_max_uV); + + pr_debug("Setting voltage %d-%duV on %s (minuV %d)\n", + vsram_target_min_uV, vsram_target_max_uV, + rdev_get_name(mrc->vsram_rdev), min_uV); + + ret = regulator_set_voltage_rdev(mrc->vsram_rdev, vsram_target_min_uV, + vsram_target_max_uV, state); + if (ret) + return ret; + + /* The sram voltage is now balanced: update the target vreg voltage */ + return regulator_do_balance_voltage(rdev, state, true); +} + +static int mediatek_regulator_attach(struct regulator_coupler *coupler, + struct regulator_dev *rdev) +{ + struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler); + const char *rdev_name = rdev_get_name(rdev); + + /* + * If we're getting a coupling of more than two regulators here and + * this means that this is surely not a GPU<->SRAM couple: in that + * case, we may want to use another coupler implementation, if any, + * or the generic one: the regulator core will keep walking through + * the list of couplers when any .attach_regulator() cb returns 1. + */ + if (rdev->coupling_desc.n_coupled > 2) + return 1; + + if (strstr(rdev_name, "sram")) { + if (mrc->vsram_rdev) + return -EINVAL; + mrc->vsram_rdev = rdev; + } else if (!strstr(rdev_name, "vgpu") && !strstr(rdev_name, "Vgpu")) { + return 1; + } + + return 0; +} + +static int mediatek_regulator_detach(struct regulator_coupler *coupler, + struct regulator_dev *rdev) +{ + struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler); + + if (rdev == mrc->vsram_rdev) + mrc->vsram_rdev = NULL; + + return 0; +} + +static struct mediatek_regulator_coupler mediatek_coupler = { + .coupler = { + .attach_regulator = mediatek_regulator_attach, + .detach_regulator = mediatek_regulator_detach, + .balance_voltage = mediatek_regulator_balance_voltage, + }, +}; + +static int mediatek_regulator_coupler_init(void) +{ + if (!of_machine_is_compatible("mediatek,mt8183") && + !of_machine_is_compatible("mediatek,mt8186") && + !of_machine_is_compatible("mediatek,mt8192")) + return 0; + + return regulator_coupler_register(&mediatek_coupler.coupler); +} +arch_initcall(mediatek_regulator_coupler_init); + +MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>"); +MODULE_DESCRIPTION("MediaTek Regulator Coupler driver"); +MODULE_LICENSE("GPL"); |