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