diff options
Diffstat (limited to 'mali_kbase/platform/meson/mali_kbase_runtime_pm.c')
-rw-r--r-- | mali_kbase/platform/meson/mali_kbase_runtime_pm.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/mali_kbase/platform/meson/mali_kbase_runtime_pm.c b/mali_kbase/platform/meson/mali_kbase_runtime_pm.c new file mode 100644 index 0000000..a9b380c --- /dev/null +++ b/mali_kbase/platform/meson/mali_kbase_runtime_pm.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +/* + * + * (C) COPYRIGHT 2015, 2017-2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#include <mali_kbase.h> +#include <mali_kbase_defs.h> +#include <device/mali_kbase_device.h> + +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> + +#include "mali_kbase_config_platform.h" + + +static struct reset_control **resets; +static int nr_resets; + +static int resets_init(struct kbase_device *kbdev) +{ + struct device_node *np; + int i; + int err = 0; + + np = kbdev->dev->of_node; + + nr_resets = of_count_phandle_with_args(np, "resets", "#reset-cells"); + if (nr_resets <= 0) { + dev_err(kbdev->dev, "Failed to get GPU resets from dtb\n"); + return nr_resets; + } + + resets = devm_kcalloc(kbdev->dev, nr_resets, sizeof(*resets), + GFP_KERNEL); + if (!resets) + return -ENOMEM; + + for (i = 0; i < nr_resets; ++i) { + resets[i] = devm_reset_control_get_exclusive_by_index( + kbdev->dev, i); + if (IS_ERR(resets[i])) { + err = PTR_ERR(resets[i]); + nr_resets = i; + break; + } + } + + return err; +} + +static int pm_callback_soft_reset(struct kbase_device *kbdev) +{ + int ret, i; + + if (!resets) { + ret = resets_init(kbdev); + if (ret) + return ret; + } + + for (i = 0; i < nr_resets; ++i) + reset_control_assert(resets[i]); + + udelay(10); + + for (i = 0; i < nr_resets; ++i) + reset_control_deassert(resets[i]); + + udelay(10); + + /* Override Power Management Settings, values from manufacturer's defaults */ + kbase_reg_write(kbdev, GPU_CONTROL_REG(PWR_KEY), 0x2968A819); + kbase_reg_write(kbdev, GPU_CONTROL_REG(PWR_OVERRIDE1), + 0xfff | (0x20 << 16)); + + /* + * RESET_COMPLETED interrupt will be raised, so continue with + * the normal soft reset procedure + */ + return 0; +} + +static void enable_gpu_power_control(struct kbase_device *kbdev) +{ + unsigned int i; + +#if defined(CONFIG_REGULATOR) + for (i = 0; i < kbdev->nr_regulators; i++) { + if (WARN_ON(kbdev->regulators[i] == NULL)) + ; + else if (!regulator_is_enabled(kbdev->regulators[i])) + WARN_ON(regulator_enable(kbdev->regulators[i])); + } +#endif + + for (i = 0; i < kbdev->nr_clocks; i++) { + if (WARN_ON(kbdev->clocks[i] == NULL)) + ; + else if (!__clk_is_enabled(kbdev->clocks[i])) + WARN_ON(clk_prepare_enable(kbdev->clocks[i])); + } +} + +static void disable_gpu_power_control(struct kbase_device *kbdev) +{ + unsigned int i; + + for (i = 0; i < kbdev->nr_clocks; i++) { + if (WARN_ON(kbdev->clocks[i] == NULL)) + ; + else if (__clk_is_enabled(kbdev->clocks[i])) { + clk_disable_unprepare(kbdev->clocks[i]); + WARN_ON(__clk_is_enabled(kbdev->clocks[i])); + } + } + +#if defined(CONFIG_REGULATOR) + for (i = 0; i < kbdev->nr_regulators; i++) { + if (WARN_ON(kbdev->regulators[i] == NULL)) + ; + else if (regulator_is_enabled(kbdev->regulators[i])) + WARN_ON(regulator_disable(kbdev->regulators[i])); + } +#endif +} + +static int pm_callback_power_on(struct kbase_device *kbdev) +{ + int ret = 1; /* Assume GPU has been powered off */ + int error; + + dev_dbg(kbdev->dev, "%s %pK\n", __func__, (void *)kbdev->dev->pm_domain); + +#ifdef KBASE_PM_RUNTIME + error = pm_runtime_get_sync(kbdev->dev); + if (error == 1) { + /* + * Let core know that the chip has not been + * powered off, so we can save on re-initialization. + */ + ret = 0; + } + dev_dbg(kbdev->dev, "pm_runtime_get_sync returned %d\n", error); +#else + enable_gpu_power_control(kbdev); +#endif + + return ret; +} + +static void pm_callback_power_off(struct kbase_device *kbdev) +{ + dev_dbg(kbdev->dev, "%s\n", __func__); + +#ifdef KBASE_PM_RUNTIME + pm_runtime_mark_last_busy(kbdev->dev); + pm_runtime_put_autosuspend(kbdev->dev); +#else + /* Power down the GPU immediately as runtime PM is disabled */ + disable_gpu_power_control(kbdev); +#endif +} + +#ifdef KBASE_PM_RUNTIME +static int kbase_device_runtime_init(struct kbase_device *kbdev) +{ + int ret = 0; + + dev_dbg(kbdev->dev, "%s\n", __func__); + + pm_runtime_set_autosuspend_delay(kbdev->dev, AUTO_SUSPEND_DELAY); + pm_runtime_use_autosuspend(kbdev->dev); + + pm_runtime_set_active(kbdev->dev); + pm_runtime_enable(kbdev->dev); + + if (!pm_runtime_enabled(kbdev->dev)) { + dev_warn(kbdev->dev, "pm_runtime not enabled"); + ret = -EINVAL; + } else if (atomic_read(&kbdev->dev->power.usage_count)) { + dev_warn(kbdev->dev, "%s: Device runtime usage count unexpectedly non zero %d", + __func__, atomic_read(&kbdev->dev->power.usage_count)); + ret = -EINVAL; + } + + return ret; +} + +static void kbase_device_runtime_disable(struct kbase_device *kbdev) +{ + dev_dbg(kbdev->dev, "%s\n", __func__); + + if (atomic_read(&kbdev->dev->power.usage_count)) + dev_warn(kbdev->dev, "%s: Device runtime usage count unexpectedly non zero %d", + __func__, atomic_read(&kbdev->dev->power.usage_count)); + + pm_runtime_disable(kbdev->dev); +} +#endif /* KBASE_PM_RUNTIME */ + +static int pm_callback_runtime_on(struct kbase_device *kbdev) +{ + dev_dbg(kbdev->dev, "%s\n", __func__); + + enable_gpu_power_control(kbdev); + return 0; +} + +static void pm_callback_runtime_off(struct kbase_device *kbdev) +{ + dev_dbg(kbdev->dev, "%s\n", __func__); + + disable_gpu_power_control(kbdev); +} + +static void pm_callback_resume(struct kbase_device *kbdev) +{ + int ret = pm_callback_runtime_on(kbdev); + + WARN_ON(ret); +} + +static void pm_callback_suspend(struct kbase_device *kbdev) +{ + pm_callback_runtime_off(kbdev); +} + +#ifdef CONFIG_MALI_HOST_CONTROLS_SC_RAILS +static void pm_callback_sc_rails_on(struct kbase_device *kbdev) +{ + dev_dbg(kbdev->dev, "SC rails are on"); +} + +static void pm_callback_sc_rails_off(struct kbase_device *kbdev) +{ + dev_dbg(kbdev->dev, "SC rails are off"); +} +#endif + +struct kbase_pm_callback_conf pm_callbacks = { + .power_on_callback = pm_callback_power_on, + .power_off_callback = pm_callback_power_off, + .power_suspend_callback = pm_callback_suspend, + .power_resume_callback = pm_callback_resume, + .soft_reset_callback = pm_callback_soft_reset, +#ifdef KBASE_PM_RUNTIME + .power_runtime_init_callback = kbase_device_runtime_init, + .power_runtime_term_callback = kbase_device_runtime_disable, + .power_runtime_on_callback = pm_callback_runtime_on, + .power_runtime_off_callback = pm_callback_runtime_off, +#else /* KBASE_PM_RUNTIME */ + .power_runtime_init_callback = NULL, + .power_runtime_term_callback = NULL, + .power_runtime_on_callback = NULL, + .power_runtime_off_callback = NULL, +#endif /* KBASE_PM_RUNTIME */ + +#if MALI_USE_CSF && defined(KBASE_PM_RUNTIME) + .power_runtime_gpu_idle_callback = pm_callback_runtime_gpu_idle, + .power_runtime_gpu_active_callback = pm_callback_runtime_gpu_active, +#else + .power_runtime_gpu_idle_callback = NULL, + .power_runtime_gpu_active_callback = NULL, +#endif + +#ifdef CONFIG_MALI_HOST_CONTROLS_SC_RAILS + .power_on_sc_rails_callback = pm_callback_sc_rails_on, + .power_off_sc_rails_callback = pm_callback_sc_rails_off, +#endif +}; |