162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Voltage regulators coupler for MediaTek SoCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2022 Collabora, Ltd. 662306a36Sopenharmony_ci * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/regulator/coupler.h> 1562306a36Sopenharmony_ci#include <linux/regulator/driver.h> 1662306a36Sopenharmony_ci#include <linux/regulator/machine.h> 1762306a36Sopenharmony_ci#include <linux/suspend.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define to_mediatek_coupler(x) container_of(x, struct mediatek_regulator_coupler, coupler) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct mediatek_regulator_coupler { 2262306a36Sopenharmony_ci struct regulator_coupler coupler; 2362306a36Sopenharmony_ci struct regulator_dev *vsram_rdev; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * We currently support only couples of not more than two vregs and 2862306a36Sopenharmony_ci * modify the vsram voltage only when changing voltage of vgpu. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * This function is limited to the GPU<->SRAM voltages relationships. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistatic int mediatek_regulator_balance_voltage(struct regulator_coupler *coupler, 3362306a36Sopenharmony_ci struct regulator_dev *rdev, 3462306a36Sopenharmony_ci suspend_state_t state) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler); 3762306a36Sopenharmony_ci int max_spread = rdev->constraints->max_spread[0]; 3862306a36Sopenharmony_ci int vsram_min_uV = mrc->vsram_rdev->constraints->min_uV; 3962306a36Sopenharmony_ci int vsram_max_uV = mrc->vsram_rdev->constraints->max_uV; 4062306a36Sopenharmony_ci int vsram_target_min_uV, vsram_target_max_uV; 4162306a36Sopenharmony_ci int min_uV = 0; 4262306a36Sopenharmony_ci int max_uV = INT_MAX; 4362306a36Sopenharmony_ci int ret; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * If the target device is on, setting the SRAM voltage directly 4762306a36Sopenharmony_ci * is not supported as it scales through its coupled supply voltage. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * An exception is made in case the use_count is zero: this means 5062306a36Sopenharmony_ci * that this is the first time we power up the SRAM regulator, which 5162306a36Sopenharmony_ci * implies that the target device has yet to perform initialization 5262306a36Sopenharmony_ci * and setting a voltage at that time is harmless. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci if (rdev == mrc->vsram_rdev) { 5562306a36Sopenharmony_ci if (rdev->use_count == 0) 5662306a36Sopenharmony_ci return regulator_do_balance_voltage(rdev, state, true); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return -EPERM; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ret = regulator_check_consumers(rdev, &min_uV, &max_uV, state); 6262306a36Sopenharmony_ci if (ret < 0) 6362306a36Sopenharmony_ci return ret; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (min_uV == 0) { 6662306a36Sopenharmony_ci ret = regulator_get_voltage_rdev(rdev); 6762306a36Sopenharmony_ci if (ret < 0) 6862306a36Sopenharmony_ci return ret; 6962306a36Sopenharmony_ci min_uV = ret; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ret = regulator_check_voltage(rdev, &min_uV, &max_uV); 7362306a36Sopenharmony_ci if (ret < 0) 7462306a36Sopenharmony_ci return ret; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * If we're asked to set a voltage less than VSRAM min_uV, set 7862306a36Sopenharmony_ci * the minimum allowed voltage on VSRAM, as in this case it is 7962306a36Sopenharmony_ci * safe to ignore the max_spread parameter. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci vsram_target_min_uV = max(vsram_min_uV, min_uV + max_spread); 8262306a36Sopenharmony_ci vsram_target_max_uV = min(vsram_max_uV, vsram_target_min_uV + max_spread); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Make sure we're not out of range */ 8562306a36Sopenharmony_ci vsram_target_min_uV = min(vsram_target_min_uV, vsram_max_uV); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci pr_debug("Setting voltage %d-%duV on %s (minuV %d)\n", 8862306a36Sopenharmony_ci vsram_target_min_uV, vsram_target_max_uV, 8962306a36Sopenharmony_ci rdev_get_name(mrc->vsram_rdev), min_uV); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ret = regulator_set_voltage_rdev(mrc->vsram_rdev, vsram_target_min_uV, 9262306a36Sopenharmony_ci vsram_target_max_uV, state); 9362306a36Sopenharmony_ci if (ret) 9462306a36Sopenharmony_ci return ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* The sram voltage is now balanced: update the target vreg voltage */ 9762306a36Sopenharmony_ci return regulator_do_balance_voltage(rdev, state, true); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int mediatek_regulator_attach(struct regulator_coupler *coupler, 10162306a36Sopenharmony_ci struct regulator_dev *rdev) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler); 10462306a36Sopenharmony_ci const char *rdev_name = rdev_get_name(rdev); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * If we're getting a coupling of more than two regulators here and 10862306a36Sopenharmony_ci * this means that this is surely not a GPU<->SRAM couple: in that 10962306a36Sopenharmony_ci * case, we may want to use another coupler implementation, if any, 11062306a36Sopenharmony_ci * or the generic one: the regulator core will keep walking through 11162306a36Sopenharmony_ci * the list of couplers when any .attach_regulator() cb returns 1. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci if (rdev->coupling_desc.n_coupled > 2) 11462306a36Sopenharmony_ci return 1; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (strstr(rdev_name, "sram")) { 11762306a36Sopenharmony_ci if (mrc->vsram_rdev) 11862306a36Sopenharmony_ci return -EINVAL; 11962306a36Sopenharmony_ci mrc->vsram_rdev = rdev; 12062306a36Sopenharmony_ci } else if (!strstr(rdev_name, "vgpu") && !strstr(rdev_name, "Vgpu")) { 12162306a36Sopenharmony_ci return 1; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int mediatek_regulator_detach(struct regulator_coupler *coupler, 12862306a36Sopenharmony_ci struct regulator_dev *rdev) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct mediatek_regulator_coupler *mrc = to_mediatek_coupler(coupler); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (rdev == mrc->vsram_rdev) 13362306a36Sopenharmony_ci mrc->vsram_rdev = NULL; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic struct mediatek_regulator_coupler mediatek_coupler = { 13962306a36Sopenharmony_ci .coupler = { 14062306a36Sopenharmony_ci .attach_regulator = mediatek_regulator_attach, 14162306a36Sopenharmony_ci .detach_regulator = mediatek_regulator_detach, 14262306a36Sopenharmony_ci .balance_voltage = mediatek_regulator_balance_voltage, 14362306a36Sopenharmony_ci }, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int mediatek_regulator_coupler_init(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci if (!of_machine_is_compatible("mediatek,mt8183") && 14962306a36Sopenharmony_ci !of_machine_is_compatible("mediatek,mt8186") && 15062306a36Sopenharmony_ci !of_machine_is_compatible("mediatek,mt8192")) 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return regulator_coupler_register(&mediatek_coupler.coupler); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ciarch_initcall(mediatek_regulator_coupler_init); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ciMODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>"); 15862306a36Sopenharmony_ciMODULE_DESCRIPTION("MediaTek Regulator Coupler driver"); 15962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 160