CHROMIUM: MALI: Add Mali platform specific source
Create platform specific folder and source files
Add clock, power and regulator control
Support for GPU devfreq
BUG=b:109911488
Test=emerge-kukui sys-kernel/chromeos-kernel-4_19
Change-Id: I4e55458efb008553050e78703e09ac69d07ea21d
Signed-off-by: Nick Fan <Nick.Fan@mediatek.com>
Reviewed-on: https://chromium-review.googlesource.com/1317240
Commit-Ready: Nicolas Boichat <drinkcat@chromium.org>
Tested-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-by: Tomasz Figa <tfiga@chromium.org>
Reviewed-by: Nick Fan <nick.fan@mediatek.corp-partner.google.com>
diff --git a/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c b/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
index 683a24c..88358e0 100644
--- a/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
+++ b/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
@@ -79,6 +79,98 @@
return freq;
}
+static void voltage_range_check(unsigned long *voltage)
+{
+ voltage[1] = ((voltage[1] - voltage[0]) < MIN_VOLT_BIAS) ?
+ (voltage[0] + MIN_VOLT_BIAS) : voltage[1];
+ voltage[1] = ((voltage[1] - voltage[0]) > MAX_VOLT_BIAS) ?
+ (voltage[0] + MIN_VOLT_BIAS) : voltage[1];
+ voltage[1] = min_t(unsigned long, max_t(unsigned long, voltage[1],
+ VSRAM_GPU_MIN_VOLT), VSRAM_GPU_MAX_VOLT);
+}
+
+static bool get_step_volt(unsigned long *step_volt,
+ unsigned long *target_volt, int count, bool inc)
+{
+ unsigned long regulator_min_volt;
+ unsigned long regulator_max_volt;
+ unsigned long current_bias;
+ long adjust_step;
+ int i;
+
+ if (inc) {
+ current_bias = target_volt[1] - step_volt[0];
+ adjust_step = MIN_VOLT_BIAS;
+ } else {
+ current_bias = step_volt[1] - target_volt[0];
+ adjust_step = -MIN_VOLT_BIAS;
+ }
+
+ for (i = 0; i < count; ++i)
+ if (step_volt[i] != target_volt[i])
+ break;
+
+ if (i == count)
+ return 0;
+
+ for (i = 0; i < count; i++) {
+ if (i) {
+ regulator_min_volt = VSRAM_GPU_MIN_VOLT;
+ regulator_max_volt = VSRAM_GPU_MAX_VOLT;
+ } else {
+ regulator_min_volt = VGPU_MIN_VOLT;
+ regulator_max_volt = VGPU_MAX_VOLT;
+ }
+
+ if (current_bias > MAX_VOLT_BIAS) {
+ step_volt[i] = min(max(step_volt[0] + adjust_step,
+ regulator_min_volt), regulator_max_volt);
+ } else {
+ step_volt[i] = target_volt[i];
+ }
+ }
+ return 1;
+}
+
+static int
+set_voltages(struct kbase_device *kbdev, unsigned long *target_volt, int inc)
+{
+ unsigned long step_volt[REGULATOR_NUM];
+ ssize_t first, step;
+ ssize_t i;
+ int err;
+
+ for (i = 0; i < kbdev->regulator_num; ++i)
+ step_volt[i] = kbdev->current_voltage[i];
+
+ if (inc) {
+ first = kbdev->regulator_num - 1;
+ step = -1;
+ } else {
+ first = 0;
+ step = 1;
+ }
+
+ while (get_step_volt(step_volt, target_volt,
+ kbdev->regulator_num, inc)) {
+ for (i = first; i >= 0 && i < kbdev->regulator_num; i += step) {
+ if (kbdev->current_voltage[i] == step_volt[i])
+ continue;
+
+ err = regulator_set_voltage(kbdev->regulator[i],
+ step_volt[i], step_volt[i] + VOLT_TOL);
+
+ if (err) {
+ dev_err(kbdev->dev, "Failed to set reg %d voltage err:(%d)\n",
+ i, err);
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int
kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
{
@@ -86,9 +178,10 @@
struct dev_pm_opp *opp;
unsigned long nominal_freq;
unsigned long freq = 0;
- unsigned long voltage;
- int err;
+ unsigned long target_volt[REGULATOR_NUM];
+ int err, i;
u64 core_mask;
+ struct mfg_base *mfg = kbdev->platform_context;
freq = *target_freq;
@@ -96,7 +189,6 @@
rcu_read_lock();
#endif
opp = devfreq_recommended_opp(dev, &freq, flags);
- voltage = dev_pm_opp_get_voltage(opp);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
rcu_read_unlock();
#endif
@@ -104,6 +196,9 @@
dev_err(dev, "Failed to get opp (%ld)\n", PTR_ERR(opp));
return PTR_ERR(opp);
}
+
+ for (i = 0; i < kbdev->regulator_num; i++)
+ target_volt[i] = dev_pm_opp_get_voltage_supply(opp, i);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
dev_pm_opp_put(opp);
#endif
@@ -113,36 +208,51 @@
/*
* Only update if there is a change of frequency
*/
- if (kbdev->current_nominal_freq == nominal_freq) {
+ if (kbdev->current_nominal_freq == nominal_freq &&
+ kbdev->current_voltage[0] == target_volt[0]) {
*target_freq = nominal_freq;
return 0;
}
freq = opp_translate(kbdev, nominal_freq, &core_mask);
+ voltage_range_check(target_volt);
+
#ifdef CONFIG_REGULATOR
- if (kbdev->regulator && kbdev->current_voltage != voltage
- && kbdev->current_freq < freq) {
- err = regulator_set_voltage(kbdev->regulator, voltage, voltage);
+ if (kbdev->current_voltage[0] < target_volt[0]) {
+ err = set_voltages(kbdev, target_volt, 1);
if (err) {
- dev_err(dev, "Failed to increase voltage (%d)\n", err);
+ dev_err(kbdev->dev, "Failed to increase voltage\n");
return err;
}
}
#endif
- err = clk_set_rate(kbdev->clock, freq);
- if (err) {
- dev_err(dev, "Failed to set clock %lu (target %lu)\n",
- freq, *target_freq);
- return err;
+ if (kbdev->current_freq != freq) {
+ err = clk_set_parent(mfg->clk_mux, mfg->clk_sub_parent);
+ if (err) {
+ dev_err(kbdev->dev, "Failed to select sub clock src\n");
+ return err;
+ }
+
+ err = clk_set_rate(kbdev->clock, freq);
+ if (err) {
+ dev_err(dev, "Failed to set clock %lu (target %lu)\n",
+ freq, *target_freq);
+ return err;
+ }
+
+ err = clk_set_parent(mfg->clk_mux, mfg->clk_main_parent);
+ if (err) {
+ dev_err(kbdev->dev, "Failed to select main clock src\n");
+ return err;
+ }
}
#ifdef CONFIG_REGULATOR
- if (kbdev->regulator && kbdev->current_voltage != voltage
- && kbdev->current_freq > freq) {
- err = regulator_set_voltage(kbdev->regulator, voltage, voltage);
+ if (kbdev->current_voltage[0] > target_volt[0]) {
+ err = set_voltages(kbdev, target_volt, 0);
if (err) {
- dev_err(dev, "Failed to decrease voltage (%d)\n", err);
+ dev_err(kbdev->dev, "Failed to decrease voltage\n");
return err;
}
}
@@ -151,7 +261,8 @@
kbase_devfreq_set_core_mask(kbdev, core_mask);
*target_freq = nominal_freq;
- kbdev->current_voltage = voltage;
+ for (i = 0; i < kbdev->regulator_num; i++)
+ kbdev->current_voltage[i] = target_volt[i];
kbdev->current_nominal_freq = nominal_freq;
kbdev->current_freq = freq;
kbdev->current_core_mask = core_mask;
@@ -321,7 +432,7 @@
kbdev->opp_table[i].core_mask = core_mask;
dev_info(kbdev->dev, "OPP %d : opp_freq=%llu real_freq=%llu core_mask=%llx\n",
- i, opp_freq, real_freq, core_mask);
+ i, opp_freq, real_freq, core_mask);
i++;
}
diff --git a/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_debugfs.c b/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_debugfs.c
index 071a530..814bb4c 100644
--- a/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_debugfs.c
+++ b/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_debugfs.c
@@ -196,7 +196,7 @@
kbase_pm_context_active(kbdev);
kbase_get_real_power(df, &power,
- kbdev->current_nominal_freq, (kbdev->current_voltage / 1000));
+ kbdev->current_nominal_freq, (kbdev->current_voltage[0] / 1000));
kbase_pm_context_idle(kbdev);
*val = power;
diff --git a/drivers/gpu/arm/midgard/mali_kbase_core_linux.c b/drivers/gpu/arm/midgard/mali_kbase_core_linux.c
index d44ebd9..cc7b94f 100644
--- a/drivers/gpu/arm/midgard/mali_kbase_core_linux.c
+++ b/drivers/gpu/arm/midgard/mali_kbase_core_linux.c
@@ -3134,23 +3134,41 @@
{
struct kbase_device *kbdev = to_kbase_device(&pdev->dev);
int err = 0;
+ const char **reg_names;
+#if defined(CONFIG_REGULATOR)
+ int i;
+#endif
if (!kbdev)
return -ENODEV;
+ kbdev->regulator_num = of_property_count_strings(kbdev->dev->of_node,
+ "supply-names");
+
+ reg_names = kcalloc(kbdev->regulator_num, sizeof(char *), GFP_KERNEL);
+
+ if (of_property_read_string_array(kbdev->dev->of_node, "supply-names",
+ reg_names, kbdev->regulator_num) < 0) {
+ dev_err(&pdev->dev, "Failed to get supply names\n");
+ err = -EINVAL;
+ goto fail;
+ }
+
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) && defined(CONFIG_OF) \
&& defined(CONFIG_REGULATOR)
- kbdev->regulator = regulator_get_optional(kbdev->dev, "mali");
- if (IS_ERR_OR_NULL(kbdev->regulator)) {
- err = PTR_ERR(kbdev->regulator);
- kbdev->regulator = NULL;
- if (err == -EPROBE_DEFER) {
- dev_err(&pdev->dev, "Failed to get regulator\n");
- return err;
+ for (i = 0; i < kbdev->regulator_num; i++) {
+ kbdev->regulator[i] = regulator_get_optional(kbdev->dev, reg_names[i]);
+ if (IS_ERR_OR_NULL(kbdev->regulator[i])) {
+ err = PTR_ERR(kbdev->regulator[i]);
+ kbdev->regulator[i] = NULL;
+ if (err == -EPROBE_DEFER) {
+ dev_err(&pdev->dev, "Failed to get regulator\n");
+ goto fail;
+ }
+ dev_info(kbdev->dev,
+ "Continuing without %s regulator control\n", reg_names[i]);
+ /* Allow probe to continue without regulator */
}
- dev_info(kbdev->dev,
- "Continuing without Mali regulator control\n");
- /* Allow probe to continue without regulator */
}
#endif /* LINUX_VERSION_CODE >= 3, 12, 0 */
@@ -3178,6 +3196,15 @@
/* Register the OPPs if they are available in device tree */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) \
|| defined(LSK_OPPV2_BACKPORT)
+#if defined(CONFIG_REGULATOR)
+ kbdev->dev_opp_table = dev_pm_opp_set_regulators(kbdev->dev, reg_names, 2);
+ if (IS_ERR(kbdev->dev_opp_table)) {
+ err = PTR_ERR(kbdev->dev_opp_table);
+ kbdev->dev_opp_table = NULL;
+ dev_err(kbdev->dev, "Failed to set regulators for GPU err: %d\n",
+ err);
+ }
+#endif /* CONFIG_REGULATOR */
err = dev_pm_opp_of_add_table(kbdev->dev);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
err = of_init_opp_table(kbdev->dev);
@@ -3188,6 +3215,8 @@
dev_dbg(kbdev->dev, "OPP table not found\n");
#endif /* CONFIG_OF && CONFIG_PM_OPP */
+ kfree(reg_names);
+
return 0;
fail:
@@ -3198,17 +3227,22 @@
}
#ifdef CONFIG_REGULATOR
- if (NULL != kbdev->regulator) {
- regulator_put(kbdev->regulator);
- kbdev->regulator = NULL;
+ for (i = 0; i < kbdev->regulator_num; i++) {
+ if (NULL != kbdev->regulator[i]) {
+ regulator_put(kbdev->regulator[i]);
+ kbdev->regulator[i] = NULL;
+ }
}
#endif
+ kfree(reg_names);
+
return err;
}
static void power_control_term(struct kbase_device *kbdev)
{
+ int i;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) || \
defined(LSK_OPPV2_BACKPORT)
dev_pm_opp_of_remove_table(kbdev->dev);
@@ -3224,11 +3258,20 @@
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) && defined(CONFIG_OF) \
&& defined(CONFIG_REGULATOR)
- if (kbdev->regulator) {
- regulator_put(kbdev->regulator);
- kbdev->regulator = NULL;
+ for (i = 0; i < kbdev->regulator_num; i++) {
+ if (kbdev->regulator[i]) {
+ regulator_put(kbdev->regulator[i]);
+ kbdev->regulator[i] = NULL;
+ }
}
#endif /* LINUX_VERSION_CODE >= 3, 12, 0 */
+
+#ifdef CONFIG_REGULATOR
+ if (kbdev->dev_opp_table != NULL) {
+ dev_pm_opp_put_regulators(kbdev->dev_opp_table);
+ kbdev->dev_opp_table = NULL;
+ }
+#endif
}
#ifdef MALI_KBASE_BUILD
diff --git a/drivers/gpu/arm/midgard/mali_kbase_defs.h b/drivers/gpu/arm/midgard/mali_kbase_defs.h
index 4adfe35..6c978cc 100644
--- a/drivers/gpu/arm/midgard/mali_kbase_defs.h
+++ b/drivers/gpu/arm/midgard/mali_kbase_defs.h
@@ -1165,6 +1165,9 @@
#define DEVNAME_SIZE 16
+#if defined(CONFIG_REGULATOR)
+#define REGULATOR_NUM 2
+#endif
/**
@@ -1429,7 +1432,9 @@
struct clk *clock;
#ifdef CONFIG_REGULATOR
- struct regulator *regulator;
+ int regulator_num;
+ struct regulator *regulator[REGULATOR_NUM];
+ struct opp_table *dev_opp_table;
#endif
char devname[DEVNAME_SIZE];
@@ -1515,7 +1520,7 @@
struct devfreq *devfreq;
unsigned long current_freq;
unsigned long current_nominal_freq;
- unsigned long current_voltage;
+ unsigned long current_voltage[REGULATOR_NUM];
u64 current_core_mask;
struct kbase_devfreq_opp *opp_table;
int num_opps;
diff --git a/drivers/gpu/arm/midgard/platform/mediatek/Kbuild b/drivers/gpu/arm/midgard/platform/mediatek/Kbuild
new file mode 100644
index 0000000..c1161b0
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/mediatek/Kbuild
@@ -0,0 +1,14 @@
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# 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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+
+mali_kbase-y += \
+ $(MALI_PLATFORM_DIR)/mali_kbase_config_mediatek.o \
+ $(MALI_PLATFORM_DIR)/mali_kbase_runtime_pm.o
diff --git a/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_mediatek.c b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_mediatek.c
new file mode 100644
index 0000000..a273717
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_mediatek.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+
+#include <mali_kbase_config.h>
+
+int kbase_platform_early_init(void)
+{
+ /* Nothing needed at this stage */
+ return 0;
+}
+
+static struct kbase_platform_config dummy_platform_config;
+
+struct kbase_platform_config *kbase_get_platform_config(void)
+{
+ return &dummy_platform_config;
+}
+
+int kbase_platform_register(void)
+{
+ return 0;
+}
+
+void kbase_platform_unregister(void)
+{
+}
diff --git a/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_platform.h b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_platform.h
new file mode 100644
index 0000000..848b533
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_platform.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+struct mfg_base {
+ struct clk *clk_mux;
+ struct clk *clk_main_parent;
+ struct clk *clk_sub_parent;
+ struct clk *subsys_mfg_cg;
+ struct platform_device *gpu_core1_dev;
+ struct platform_device *gpu_core2_dev;
+ bool is_powered;
+};
+
+/* Definition for PMIC regulators */
+#define VSRAM_GPU_MAX_VOLT (925000) /* uV */
+#define VSRAM_GPU_MIN_VOLT (850000) /* uV */
+#define VGPU_MAX_VOLT (825000) /* uV */
+#define VGPU_MIN_VOLT (625000) /* uV */
+
+#define MIN_VOLT_BIAS (100000) /* uV */
+#define MAX_VOLT_BIAS (250000) /* uV */
+#define VOLT_TOL (125) /* uV */
+
+/**
+ * Maximum frequency GPU will be clocked at. Given in kHz.
+ * This must be specified as there is no default value.
+ *
+ * Attached value: number in kHz
+ * Default value: NA
+ */
+#define GPU_FREQ_KHZ_MAX (800000)
+/**
+ * Minimum frequency GPU will be clocked at. Given in kHz.
+ * This must be specified as there is no default value.
+ *
+ * Attached value: number in kHz
+ * Default value: NA
+ */
+#define GPU_FREQ_KHZ_MIN (300000)
+
+/**
+ * CPU_SPEED_FUNC - A pointer to a function that calculates the CPU clock
+ *
+ * CPU clock speed of the platform is in MHz - see kbase_cpu_clk_speed_func
+ * for the function prototype.
+ *
+ * Attached value: A kbase_cpu_clk_speed_func.
+ * Default Value: NA
+ */
+#define CPU_SPEED_FUNC (NULL)
+
+/**
+ * GPU_SPEED_FUNC - A pointer to a function that calculates the GPU clock
+ *
+ * GPU clock speed of the platform in MHz - see kbase_gpu_clk_speed_func
+ * for the function prototype.
+ *
+ * Attached value: A kbase_gpu_clk_speed_func.
+ * Default Value: NA
+ */
+#define GPU_SPEED_FUNC (NULL)
+
+/**
+ * Power management configuration
+ *
+ * Attached value: pointer to @ref kbase_pm_callback_conf
+ * Default value: See @ref kbase_pm_callback_conf
+ */
+#define POWER_MANAGEMENT_CALLBACKS (&pm_callbacks)
+
+/**
+ * Platform specific configuration functions
+ *
+ * Attached value: pointer to @ref kbase_platform_funcs_conf
+ * Default value: See @ref kbase_platform_funcs_conf
+ */
+#define PLATFORM_FUNCS (&platform_funcs)
+
+extern struct kbase_pm_callback_conf pm_callbacks;
+extern struct kbase_platform_funcs_conf platform_funcs;
+
+/**
+ * Autosuspend delay
+ *
+ * The delay time (in milliseconds) to be used for autosuspend
+ */
+#define AUTO_SUSPEND_DELAY (100)
diff --git a/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_runtime_pm.c b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_runtime_pm.c
new file mode 100644
index 0000000..a18ae2c
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_runtime_pm.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <mali_kbase.h>
+#include "mali_kbase_config_platform.h"
+#include <mali_kbase_defs.h>
+
+static struct platform_device *probe_gpu_core1_dev;
+static struct platform_device *probe_gpu_core2_dev;
+
+static const struct of_device_id mtk_gpu_corex_of_ids[] = {
+ { .compatible = "mediatek,gpu_core1", .data = "1" },
+ { .compatible = "mediatek,gpu_core2", .data = "2" },
+ {}
+};
+
+static int mtk_gpu_corex_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ const char *tmp;
+
+ match = of_match_device(mtk_gpu_corex_of_ids, dev);
+ if (!match)
+ return -ENODEV;
+ tmp = match->data;
+ if (*tmp == '1')
+ probe_gpu_core1_dev = pdev;
+ else
+ probe_gpu_core2_dev = pdev;
+
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+}
+
+static int mtk_gpu_corex_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver mtk_gpu_corex_driver = {
+ .probe = mtk_gpu_corex_probe,
+ .remove = mtk_gpu_corex_remove,
+ .driver = {
+ .name = "gpu_corex",
+ .of_match_table = mtk_gpu_corex_of_ids,
+ }
+};
+
+static int pm_callback_power_on(struct kbase_device *kbdev)
+{
+ int error, i;
+ struct mfg_base *mfg = kbdev->platform_context;
+
+ if (mfg->is_powered) {
+ dev_dbg(kbdev->dev, "mali_device is already powered\n");
+ return 0;
+ }
+
+ for (i = 0; i < kbdev->regulator_num; i++) {
+ error = regulator_enable(kbdev->regulator[i]);
+ if (error < 0) {
+ dev_err(kbdev->dev,
+ "Power on reg %d failed error = %d\n",
+ i, error);
+ return error;
+ }
+ }
+
+ error = pm_runtime_get_sync(kbdev->dev);
+ if (error < 0) {
+ dev_err(kbdev->dev,
+ "Power on core 0 failed (err: %d)\n", error);
+ return error;
+ }
+
+ error = pm_runtime_get_sync(&mfg->gpu_core1_dev->dev);
+ if (error < 0) {
+ dev_err(kbdev->dev,
+ "Power on core 1 failed (err: %d)\n", error);
+ return error;
+ }
+
+ error = pm_runtime_get_sync(&mfg->gpu_core2_dev->dev);
+ if (error < 0) {
+ dev_err(kbdev->dev,
+ "Power on core 2 failed (err: %d)\n", error);
+ return error;
+ }
+
+ error = clk_prepare_enable(mfg->clk_main_parent);
+ if (error < 0) {
+ dev_err(kbdev->dev,
+ "clk_main_parent clock enable failed (err: %d)\n",
+ error);
+ return error;
+ }
+
+ error = clk_prepare_enable(mfg->clk_mux);
+ if (error < 0) {
+ dev_err(kbdev->dev,
+ "clk_mux clock enable failed (err: %d)\n", error);
+ return error;
+ }
+
+ error = clk_prepare_enable(mfg->subsys_mfg_cg);
+ if (error < 0) {
+ dev_err(kbdev->dev,
+ "subsys_mfg_cg clock enable failed (err: %d)\n", error);
+ return error;
+ }
+
+ mfg->is_powered = true;
+
+ return 1;
+}
+
+static void pm_callback_power_off(struct kbase_device *kbdev)
+{
+ struct mfg_base *mfg = kbdev->platform_context;
+ int error, i;
+
+ if (!mfg->is_powered) {
+ dev_dbg(kbdev->dev, "mali_device is already powered off\n");
+ return;
+ }
+
+ mfg->is_powered = false;
+
+ clk_disable_unprepare(mfg->subsys_mfg_cg);
+
+ clk_disable_unprepare(mfg->clk_mux);
+
+ clk_disable_unprepare(mfg->clk_main_parent);
+
+ error = pm_runtime_put_sync(&mfg->gpu_core2_dev->dev);
+ if (error < 0)
+ dev_err(kbdev->dev,
+ "Power off core 2 failed (err: %d)\n", error);
+
+ error = pm_runtime_put_sync(&mfg->gpu_core1_dev->dev);
+ if (error < 0)
+ dev_err(kbdev->dev,
+ "Power off core 1 failed (err: %d)\n", error);
+
+ error = pm_runtime_put_sync(kbdev->dev);
+ if (error < 0)
+ dev_err(kbdev->dev,
+ "Power off core 0 failed (err: %d)\n", error);
+
+ for (i = 0; i < kbdev->regulator_num; i++) {
+ error = regulator_disable(kbdev->regulator[i]);
+ if (error < 0) {
+ dev_err(kbdev->dev,
+ "Power off reg %d failed error = %d\n",
+ i, error);
+ }
+ }
+}
+
+static int kbase_device_runtime_init(struct kbase_device *kbdev)
+{
+ dev_dbg(kbdev->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void kbase_device_runtime_disable(struct kbase_device *kbdev)
+{
+ dev_dbg(kbdev->dev, "%s\n", __func__);
+}
+
+static int pm_callback_runtime_on(struct kbase_device *kbdev)
+{
+ dev_dbg(kbdev->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static void pm_callback_runtime_off(struct kbase_device *kbdev)
+{
+ dev_dbg(kbdev->dev, "%s\n", __func__);
+}
+
+static void pm_callback_resume(struct kbase_device *kbdev)
+{
+ pm_callback_power_on(kbdev);
+}
+
+static void pm_callback_suspend(struct kbase_device *kbdev)
+{
+ pm_callback_power_off(kbdev);
+}
+
+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,
+#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 */
+};
+
+
+int mali_mfgsys_init(struct kbase_device *kbdev, struct mfg_base *mfg)
+{
+ int err = 0, i;
+ unsigned long volt;
+
+ if (!probe_gpu_core1_dev || !probe_gpu_core2_dev) {
+ dev_err(kbdev->dev,
+ "Wait for other power domain ret: %d\n", -EPROBE_DEFER);
+ return -EPROBE_DEFER;
+ }
+
+ for (i = 0; i < kbdev->regulator_num; i++)
+ if (kbdev->regulator[i] == NULL)
+ return -EINVAL;
+
+ mfg->gpu_core1_dev = probe_gpu_core1_dev;
+ mfg->gpu_core2_dev = probe_gpu_core2_dev;
+
+ mfg->clk_main_parent = devm_clk_get(kbdev->dev, "clk_main_parent");
+ if (IS_ERR(mfg->clk_main_parent)) {
+ err = PTR_ERR(mfg->clk_main_parent);
+ dev_err(kbdev->dev, "devm_clk_get clk_main_parent failed\n");
+ return err;
+ }
+
+ mfg->clk_sub_parent = devm_clk_get(kbdev->dev, "clk_sub_parent");
+ if (IS_ERR(mfg->clk_sub_parent)) {
+ err = PTR_ERR(mfg->clk_sub_parent);
+ dev_err(kbdev->dev, "devm_clk_get clk_sub_parent failed\n");
+ return err;
+ }
+
+ mfg->clk_mux = devm_clk_get(kbdev->dev, "clk_mux");
+ if (IS_ERR(mfg->clk_mux)) {
+ err = PTR_ERR(mfg->clk_mux);
+ dev_err(kbdev->dev, "devm_clk_get clk_mux failed\n");
+ return err;
+ }
+
+ mfg->subsys_mfg_cg = devm_clk_get(kbdev->dev, "subsys_mfg_cg");
+ if (IS_ERR(mfg->subsys_mfg_cg)) {
+ err = PTR_ERR(mfg->subsys_mfg_cg);
+ dev_err(kbdev->dev, "devm_clk_get subsys_mfg_cg failed\n");
+ return err;
+ }
+
+ for (i = 0; i < kbdev->regulator_num; i++) {
+ volt = (0 == i) ? VGPU_MAX_VOLT : VSRAM_GPU_MAX_VOLT;
+ err = regulator_set_voltage(kbdev->regulator[i],
+ volt, volt + VOLT_TOL);
+ if (err < 0) {
+ dev_err(kbdev->dev,
+ "Regulator %d set voltage failed: %d\n",
+ i, err);
+ return err;
+ }
+ kbdev->current_voltage[i] = volt;
+
+ err = regulator_enable(kbdev->regulator[i]);
+ if (err < 0) {
+ dev_err(kbdev->dev,
+ "Regulator %d enable failed: %d\n", i, err);
+ return err;
+ }
+ }
+
+ mfg->is_powered = false;
+
+ return 0;
+}
+
+void mali_mfgsys_deinit(struct kbase_device *kbdev)
+{
+ int err, i;
+
+ for (i = 0; i < kbdev->regulator_num; i++) {
+ err = regulator_disable(kbdev->regulator[i]);
+ if (err < 0) {
+ dev_err(kbdev->dev,
+ "Regulator %d disable failed: %d\n", i, err);
+ }
+ }
+}
+
+
+static int platform_init(struct kbase_device *kbdev)
+{
+ int err;
+ struct mfg_base *mfg;
+
+ mfg = kzalloc(sizeof(*mfg), GFP_KERNEL);
+ if (!mfg)
+ return -ENOMEM;
+
+ err = mali_mfgsys_init(kbdev, mfg);
+ if (err)
+ return err;
+
+ kbdev->platform_context = mfg;
+ pm_runtime_enable(kbdev->dev);
+
+ err = clk_set_parent(mfg->clk_mux, mfg->clk_sub_parent);
+ if (err) {
+ dev_err(kbdev->dev, "Failed to select sub clock src\n");
+ return err;
+ }
+
+ err = clk_set_rate(mfg->clk_main_parent, GPU_FREQ_KHZ_MAX * 1000);
+ if (err) {
+ dev_err(kbdev->dev, "Failed to set clock %d kHz\n",
+ GPU_FREQ_KHZ_MAX);
+ return err;
+ }
+
+ err = clk_set_parent(mfg->clk_mux, mfg->clk_main_parent);
+ if (err) {
+ dev_err(kbdev->dev, "Failed to select main clock src\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static void platform_term(struct kbase_device *kbdev)
+{
+ struct mfg_base *mfg = kbdev->platform_context;
+
+ kfree(mfg);
+ kbdev->platform_context = NULL;
+ mali_mfgsys_deinit(kbdev);
+ pm_runtime_disable(kbdev->dev);
+}
+
+struct kbase_platform_funcs_conf platform_funcs = {
+ .platform_init_func = platform_init,
+ .platform_term_func = platform_term
+};
+
+static int __init mtk_mfg_corex(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mtk_gpu_corex_driver);
+ if (ret != 0)
+ pr_debug("%s: Failed to register GPU core driver", __func__);
+
+ return ret;
+}
+
+subsys_initcall(mtk_mfg_corex);
+