soc: mediatek: Introduce mediatek-regulator-coupler driver
authorAngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Thu, 6 Oct 2022 11:58:16 +0000 (13:58 +0200)
committerMatthias Brugger <matthias.bgg@gmail.com>
Mon, 30 Jan 2023 10:27:49 +0000 (11:27 +0100)
This driver currently deals with GPU-SRAM regulator coupling, ensuring
that the SRAM voltage is always between a specific range of distance to
the GPU voltage, depending on the SoC, necessary in order to achieve
system stability across the full range of supported GPU frequencies.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Reviewed-by: Alyssa Rosenzweig <alyssa@collabora.com>
Link: https://lore.kernel.org/r/20221006115816.66853-1-angelogioacchino.delregno@collabora.com
Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
drivers/soc/mediatek/Kconfig
drivers/soc/mediatek/Makefile
drivers/soc/mediatek/mtk-regulator-coupler.c [new file with mode: 0644]

index 20c7715..d6b83a5 100644 (file)
@@ -44,6 +44,11 @@ config MTK_PMIC_WRAP
          on different MediaTek SoCs. The PMIC wrapper is a proprietary
          hardware to connect the PMIC.
 
+config MTK_REGULATOR_COUPLER
+       bool "MediaTek SoC Regulator Coupler" if COMPILE_TEST
+       default ARCH_MEDIATEK
+       depends on REGULATOR
+
 config MTK_SCPSYS
        bool "MediaTek SCPSYS Support"
        default ARCH_MEDIATEK
index 0e9e703..8c0ddac 100644 (file)
@@ -3,6 +3,7 @@ obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
 obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
 obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
 obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
+obj-$(CONFIG_MTK_REGULATOR_COUPLER) += mtk-regulator-coupler.o
 obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
 obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o
 obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
diff --git a/drivers/soc/mediatek/mtk-regulator-coupler.c b/drivers/soc/mediatek/mtk-regulator-coupler.c
new file mode 100644 (file)
index 0000000..ad2ed42
--- /dev/null
@@ -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");