summaryrefslogtreecommitdiff
path: root/mali_kbase/platform/meson/mali_kbase_runtime_pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'mali_kbase/platform/meson/mali_kbase_runtime_pm.c')
-rw-r--r--mali_kbase/platform/meson/mali_kbase_runtime_pm.c290
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
+};