File Index
Files and directories
Sign in
FILE

mtk-regulator-coupler.c

File preview with syntax highlighting.

linux-6.19.14/drivers/soc/mediatek/mtk-regulator-coupler.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Voltage regulators coupler for MediaTek SoCs
  4 *
  5 * Copyright (C) 2022 Collabora, Ltd.
  6 * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
  7 */
  8
  9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 10
 11#include <linux/init.h>
 12#include <linux/kernel.h>
 13#include <linux/of.h>
 14#include <linux/regulator/coupler.h>
 15#include <linux/regulator/driver.h>
 16#include <linux/regulator/machine.h>
 17#include <linux/suspend.h>
 18
 19#define to_mediatek_coupler(x)	container_of(x, struct mediatek_regulator_coupler, coupler)
 20
 21struct mediatek_regulator_coupler {
 22	struct regulator_coupler coupler;
 23	struct regulator_dev *vsram_rdev;
 24};
 25
 26/*
 27 * We currently support only couples of not more than two vregs and
 28 * modify the vsram voltage only when changing voltage of vgpu.
 29 *
 30 * This function is limited to the GPU<->SRAM voltages relationships.
 31 */
 32static int mediatek_regulator_balance_voltage(struct regulator_coupler *coupler,
 33					      struct regulator_dev *rdev,
 34					      suspend_state_t state)
 35{
 36	struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler);
 37	int max_spread = rdev->constraints->max_spread[0];
 38	int vsram_min_uV = mrc->vsram_rdev->constraints->min_uV;
 39	int vsram_max_uV = mrc->vsram_rdev->constraints->max_uV;
 40	int vsram_target_min_uV, vsram_target_max_uV;
 41	int min_uV = 0;
 42	int max_uV = INT_MAX;
 43	int ret;
 44
 45	/*
 46	 * If the target device is on, setting the SRAM voltage directly
 47	 * is not supported as it scales through its coupled supply voltage.
 48	 *
 49	 * An exception is made in case the use_count is zero: this means
 50	 * that this is the first time we power up the SRAM regulator, which
 51	 * implies that the target device has yet to perform initialization
 52	 * and setting a voltage at that time is harmless.
 53	 */
 54	if (rdev == mrc->vsram_rdev) {
 55		if (rdev->use_count == 0)
 56			return regulator_do_balance_voltage(rdev, state, true);
 57
 58		return -EPERM;
 59	}
 60
 61	ret = regulator_check_consumers(rdev, &min_uV, &max_uV, state);
 62	if (ret < 0)
 63		return ret;
 64
 65	if (min_uV == 0) {
 66		ret = regulator_get_voltage_rdev(rdev);
 67		if (ret < 0)
 68			return ret;
 69		min_uV = ret;
 70	}
 71
 72	ret = regulator_check_voltage(rdev, &min_uV, &max_uV);
 73	if (ret < 0)
 74		return ret;
 75
 76	/*
 77	 * If we're asked to set a voltage less than VSRAM min_uV, set
 78	 * the minimum allowed voltage on VSRAM, as in this case it is
 79	 * safe to ignore the max_spread parameter.
 80	 */
 81	vsram_target_min_uV = max(vsram_min_uV, min_uV + max_spread);
 82	vsram_target_max_uV = min(vsram_max_uV, vsram_target_min_uV + max_spread);
 83
 84	/* Make sure we're not out of range */
 85	vsram_target_min_uV = min(vsram_target_min_uV, vsram_max_uV);
 86
 87	pr_debug("Setting voltage %d-%duV on %s (minuV %d)\n",
 88		 vsram_target_min_uV, vsram_target_max_uV,
 89		 rdev_get_name(mrc->vsram_rdev), min_uV);
 90
 91	ret = regulator_set_voltage_rdev(mrc->vsram_rdev, vsram_target_min_uV,
 92					 vsram_target_max_uV, state);
 93	if (ret)
 94		return ret;
 95
 96	/* The sram voltage is now balanced: update the target vreg voltage */
 97	return regulator_do_balance_voltage(rdev, state, true);
 98}
 99
100static int mediatek_regulator_attach(struct regulator_coupler *coupler,
101				     struct regulator_dev *rdev)
102{
103	struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler);
104	const char *rdev_name = rdev_get_name(rdev);
105
106	/*
107	 * If we're getting a coupling of more than two regulators here and
108	 * this means that this is surely not a GPU<->SRAM couple: in that
109	 * case, we may want to use another coupler implementation, if any,
110	 * or the generic one: the regulator core will keep walking through
111	 * the list of couplers when any .attach_regulator() cb returns 1.
112	 */
113	if (rdev->coupling_desc.n_coupled > 2)
114		return 1;
115
116	if (strstr(rdev_name, "sram")) {
117		if (mrc->vsram_rdev)
118			return -EINVAL;
119		mrc->vsram_rdev = rdev;
120	} else if (!strstr(rdev_name, "vgpu") && !strstr(rdev_name, "Vgpu")) {
121		return 1;
122	}
123
124	return 0;
125}
126
127static int mediatek_regulator_detach(struct regulator_coupler *coupler,
128				     struct regulator_dev *rdev)
129{
130	struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler);
131
132	if (rdev == mrc->vsram_rdev)
133		mrc->vsram_rdev = NULL;
134
135	return 0;
136}
137
138static struct mediatek_regulator_coupler mediatek_coupler = {
139	.coupler = {
140		.attach_regulator = mediatek_regulator_attach,
141		.detach_regulator = mediatek_regulator_detach,
142		.balance_voltage = mediatek_regulator_balance_voltage,
143	},
144};
145
146static int mediatek_regulator_coupler_init(void)
147{
148	if (!of_machine_is_compatible("mediatek,mt8183") &&
149	    !of_machine_is_compatible("mediatek,mt8186") &&
150	    !of_machine_is_compatible("mediatek,mt8188") &&
151	    !of_machine_is_compatible("mediatek,mt8192"))
152		return 0;
153
154	return regulator_coupler_register(&mediatek_coupler.coupler);
155}
156arch_initcall(mediatek_regulator_coupler_init);
157
158MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
159MODULE_DESCRIPTION("MediaTek Regulator Coupler driver");
160MODULE_LICENSE("GPL");
~/ Read-only file preview.