summaryrefslogtreecommitdiff
path: root/dvalin/kernel/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'dvalin/kernel/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c')
-rw-r--r--dvalin/kernel/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c207
1 files changed, 123 insertions, 84 deletions
diff --git a/dvalin/kernel/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c b/dvalin/kernel/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
index 2806f05..8c31499 100644
--- a/dvalin/kernel/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
+++ b/dvalin/kernel/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
@@ -1,11 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/*
*
- * (C) COPYRIGHT 2014-2020 ARM Limited. All rights reserved.
+ * (C) COPYRIGHT 2014-2021 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 licence.
+ * 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
@@ -16,8 +17,6 @@
* along with this program; if not, you can access it online at
* http://www.gnu.org/licenses/gpl-2.0.html.
*
- * SPDX-License-Identifier: GPL-2.0
- *
*/
#include <mali_kbase.h>
@@ -27,40 +26,53 @@
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/devfreq.h>
-#ifdef CONFIG_DEVFREQ_THERMAL
+#if IS_ENABLED(CONFIG_DEVFREQ_THERMAL)
#include <linux/devfreq_cooling.h>
#endif
#include <linux/version.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
#include <linux/pm_opp.h>
-#else /* Linux >= 3.13 */
-/* In 3.13 the OPP include header file, types, and functions were all
- * renamed. Use the old filename for the include, and define the new names to
- * the old, when an old kernel is detected.
- */
-#include <linux/opp.h>
-#define dev_pm_opp opp
-#define dev_pm_opp_get_voltage opp_get_voltage
-#define dev_pm_opp_get_opp_count opp_get_opp_count
-#define dev_pm_opp_find_freq_ceil opp_find_freq_ceil
-#define dev_pm_opp_find_freq_floor opp_find_freq_floor
-#endif /* Linux >= 3.13 */
/**
- * opp_translate - Translate nominal OPP frequency from devicetree into real
- * frequency and core mask
- * @kbdev: Device pointer
- * @freq: Nominal frequency
- * @core_mask: Pointer to u64 to store core mask to
- * @freqs: Pointer to array of frequencies
- * @volts: Pointer to array of voltages
+ * get_voltage() - Get the voltage value corresponding to the nominal frequency
+ * used by devfreq.
+ * @kbdev: Device pointer
+ * @freq: Nominal frequency in Hz passed by devfreq.
+ *
+ * This function will be called only when the opp table which is compatible with
+ * "operating-points-v2-mali", is not present in the devicetree for GPU device.
*
- * This function will only perform translation if an operating-points-v2-mali
- * table is present in devicetree. If one is not present then it will return an
- * untranslated frequency and all cores enabled.
+ * Return: Voltage value in milli volts, 0 in case of error.
*/
-static void opp_translate(struct kbase_device *kbdev, unsigned long freq,
+static unsigned long get_voltage(struct kbase_device *kbdev, unsigned long freq)
+{
+ struct dev_pm_opp *opp;
+ unsigned long voltage = 0;
+
+#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
+ rcu_read_lock();
+#endif
+
+ opp = dev_pm_opp_find_freq_exact(kbdev->dev, freq, true);
+
+ if (IS_ERR_OR_NULL(opp))
+ dev_err(kbdev->dev, "Failed to get opp (%ld)\n", PTR_ERR(opp));
+ else {
+ voltage = dev_pm_opp_get_voltage(opp);
+#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
+ dev_pm_opp_put(opp);
+#endif
+ }
+
+#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
+ rcu_read_unlock();
+#endif
+
+ /* Return the voltage in milli volts */
+ return voltage / 1000;
+}
+
+void kbase_devfreq_opp_translate(struct kbase_device *kbdev, unsigned long freq,
u64 *core_mask, unsigned long *freqs, unsigned long *volts)
{
unsigned int i;
@@ -82,12 +94,17 @@ static void opp_translate(struct kbase_device *kbdev, unsigned long freq,
}
/* If failed to find OPP, return all cores enabled
- * and nominal frequency
+ * and nominal frequency and the corresponding voltage.
*/
if (i == kbdev->num_opps) {
+ unsigned long voltage = get_voltage(kbdev, freq);
+
*core_mask = kbdev->gpu_props.props.raw_props.shader_present;
- for (i = 0; i < kbdev->nr_clocks; i++)
+
+ for (i = 0; i < kbdev->nr_clocks; i++) {
freqs[i] = freq;
+ volts[i] = voltage;
+ }
}
}
@@ -104,18 +121,18 @@ kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
nominal_freq = *target_freq;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
rcu_read_lock();
#endif
opp = devfreq_recommended_opp(dev, &nominal_freq, flags);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
rcu_read_unlock();
#endif
if (IS_ERR_OR_NULL(opp)) {
dev_err(dev, "Failed to get opp (%ld)\n", PTR_ERR(opp));
return PTR_ERR(opp);
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
dev_pm_opp_put(opp);
#endif
@@ -127,9 +144,10 @@ kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
return 0;
}
- opp_translate(kbdev, nominal_freq, &core_mask, freqs, volts);
+ kbase_devfreq_opp_translate(kbdev, nominal_freq, &core_mask,
+ freqs, volts);
-#ifdef CONFIG_REGULATOR
+#if IS_ENABLED(CONFIG_REGULATOR)
/* Regulators and clocks work in pairs: every clock has a regulator,
* and we never expect to have more regulators than clocks.
*
@@ -177,7 +195,7 @@ kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
}
}
-#ifdef CONFIG_REGULATOR
+#if IS_ENABLED(CONFIG_REGULATOR)
for (i = 0; i < kbdev->nr_clocks; i++) {
if (kbdev->regulators[i] &&
kbdev->current_voltages[i] != volts[i] &&
@@ -238,6 +256,10 @@ kbase_devfreq_status(struct device *dev, struct devfreq_dev_status *stat)
stat->current_frequency = kbdev->current_nominal_freq;
stat->private_data = NULL;
+#if MALI_USE_CSF && defined CONFIG_DEVFREQ_THERMAL
+ kbase_ipa_reset_data(kbdev);
+#endif
+
return 0;
}
@@ -249,11 +271,11 @@ static int kbase_devfreq_init_freq_table(struct kbase_device *kbdev,
unsigned long freq;
struct dev_pm_opp *opp;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
rcu_read_lock();
#endif
count = dev_pm_opp_get_opp_count(kbdev->dev);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
rcu_read_unlock();
#endif
if (count < 0)
@@ -264,20 +286,20 @@ static int kbase_devfreq_init_freq_table(struct kbase_device *kbdev,
if (!dp->freq_table)
return -ENOMEM;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
rcu_read_lock();
#endif
for (i = 0, freq = ULONG_MAX; i < count; i++, freq--) {
opp = dev_pm_opp_find_freq_floor(kbdev->dev, &freq);
if (IS_ERR(opp))
break;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
dev_pm_opp_put(opp);
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */
+#endif /* KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE */
dp->freq_table[i] = freq;
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE
rcu_read_unlock();
#endif
@@ -309,18 +331,21 @@ static void kbase_devfreq_term_freq_table(struct kbase_device *kbdev)
struct devfreq_dev_profile *dp = &kbdev->devfreq_profile;
kfree(dp->freq_table);
+ dp->freq_table = NULL;
}
static void kbase_devfreq_term_core_mask_table(struct kbase_device *kbdev)
{
kfree(kbdev->devfreq_table);
+ kbdev->devfreq_table = NULL;
}
static void kbase_devfreq_exit(struct device *dev)
{
struct kbase_device *kbdev = dev_get_drvdata(dev);
- kbase_devfreq_term_freq_table(kbdev);
+ if (kbdev)
+ kbase_devfreq_term_freq_table(kbdev);
}
static void kbasep_devfreq_read_suspend_clock(struct kbase_device *kbdev,
@@ -359,7 +384,7 @@ static void kbasep_devfreq_read_suspend_clock(struct kbase_device *kbdev,
static int kbase_devfreq_init_core_mask_table(struct kbase_device *kbdev)
{
-#if KERNEL_VERSION(3, 18, 0) > LINUX_VERSION_CODE || !defined(CONFIG_OF)
+#ifndef CONFIG_OF
/* OPP table initialization requires at least the capability to get
* regulators and clocks from the device tree, as well as parsing
* arrays of unsigned integer values.
@@ -392,7 +417,7 @@ static int kbase_devfreq_init_core_mask_table(struct kbase_device *kbdev)
u64 core_mask, opp_freq,
real_freqs[BASE_MAX_NR_CLOCKS_REGULATORS];
int err;
-#ifdef CONFIG_REGULATOR
+#if IS_ENABLED(CONFIG_REGULATOR)
u32 opp_volts[BASE_MAX_NR_CLOCKS_REGULATORS];
#endif
@@ -420,7 +445,7 @@ static int kbase_devfreq_init_core_mask_table(struct kbase_device *kbdev)
err);
continue;
}
-#ifdef CONFIG_REGULATOR
+#if IS_ENABLED(CONFIG_REGULATOR)
err = of_property_read_u32_array(node,
"opp-microvolt", opp_volts, kbdev->nr_regulators);
if (err < 0) {
@@ -474,7 +499,7 @@ static int kbase_devfreq_init_core_mask_table(struct kbase_device *kbdev)
kbdev->devfreq_table[i].real_freqs[j] =
real_freqs[j];
}
-#ifdef CONFIG_REGULATOR
+#if IS_ENABLED(CONFIG_REGULATOR)
if (kbdev->nr_regulators > 0) {
int j;
@@ -493,11 +518,9 @@ static int kbase_devfreq_init_core_mask_table(struct kbase_device *kbdev)
kbdev->num_opps = i;
return 0;
-#endif /* KERNEL_VERSION(3, 18, 0) > LINUX_VERSION_CODE */
+#endif /* CONFIG_OF */
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
-
static const char *kbase_devfreq_req_type_name(enum kbase_devfreq_work_type type)
{
const char *p;
@@ -554,27 +577,26 @@ static void kbase_devfreq_suspend_resume_worker(struct work_struct *work)
}
}
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) */
-
void kbase_devfreq_enqueue_work(struct kbase_device *kbdev,
enum kbase_devfreq_work_type work_type)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
unsigned long flags;
WARN_ON(work_type == DEVFREQ_WORK_NONE);
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
- kbdev->devfreq_queue.req_type = work_type;
- queue_work(kbdev->devfreq_queue.workq, &kbdev->devfreq_queue.work);
+ /* Skip enqueuing a work if workqueue has already been terminated. */
+ if (likely(kbdev->devfreq_queue.workq)) {
+ kbdev->devfreq_queue.req_type = work_type;
+ queue_work(kbdev->devfreq_queue.workq,
+ &kbdev->devfreq_queue.work);
+ }
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
dev_dbg(kbdev->dev, "Enqueuing devfreq req: %s\n",
kbase_devfreq_req_type_name(work_type));
-#endif
}
static int kbase_devfreq_work_init(struct kbase_device *kbdev)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
kbdev->devfreq_queue.req_type = DEVFREQ_WORK_NONE;
kbdev->devfreq_queue.acted_type = DEVFREQ_WORK_RESUME;
@@ -584,17 +606,23 @@ static int kbase_devfreq_work_init(struct kbase_device *kbdev)
INIT_WORK(&kbdev->devfreq_queue.work,
kbase_devfreq_suspend_resume_worker);
-#endif
return 0;
}
static void kbase_devfreq_work_term(struct kbase_device *kbdev)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
- destroy_workqueue(kbdev->devfreq_queue.workq);
-#endif
+ unsigned long flags;
+ struct workqueue_struct *workq;
+
+ spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
+ workq = kbdev->devfreq_queue.workq;
+ kbdev->devfreq_queue.workq = NULL;
+ spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
+
+ destroy_workqueue(workq);
}
+
int kbase_devfreq_init(struct kbase_device *kbdev)
{
struct devfreq_dev_profile *dp;
@@ -631,19 +659,11 @@ int kbase_devfreq_init(struct kbase_device *kbdev)
/* Record the maximum frequency possible */
kbdev->gpu_props.props.core_props.gpu_freq_khz_max =
dp->freq_table[0] / 1000;
- };
-
- err = kbase_devfreq_init_core_mask_table(kbdev);
- if (err) {
- kbase_devfreq_term_freq_table(kbdev);
- return err;
}
- /* Initialise devfreq suspend/resume workqueue */
- err = kbase_devfreq_work_init(kbdev);
+ err = kbase_devfreq_init_core_mask_table(kbdev);
if (err) {
kbase_devfreq_term_freq_table(kbdev);
- dev_err(kbdev->dev, "Devfreq initialization failed");
return err;
}
@@ -651,13 +671,27 @@ int kbase_devfreq_init(struct kbase_device *kbdev)
"simple_ondemand", NULL);
if (IS_ERR(kbdev->devfreq)) {
err = PTR_ERR(kbdev->devfreq);
- kbase_devfreq_work_term(kbdev);
+ kbdev->devfreq = NULL;
+ kbase_devfreq_term_core_mask_table(kbdev);
kbase_devfreq_term_freq_table(kbdev);
+ dev_err(kbdev->dev, "Fail to add devfreq device(%d)\n", err);
+ return err;
+ }
+
+ /* Initialize devfreq suspend/resume workqueue */
+ err = kbase_devfreq_work_init(kbdev);
+ if (err) {
+ if (devfreq_remove_device(kbdev->devfreq))
+ dev_err(kbdev->dev, "Fail to rm devfreq\n");
+ kbdev->devfreq = NULL;
+ kbase_devfreq_term_core_mask_table(kbdev);
+ dev_err(kbdev->dev, "Fail to init devfreq workqueue\n");
return err;
}
/* devfreq_add_device only copies a few of kbdev->dev's fields, so
- * set drvdata explicitly so IPA models can access kbdev. */
+ * set drvdata explicitly so IPA models can access kbdev.
+ */
dev_set_drvdata(&kbdev->devfreq->dev, kbdev);
err = devfreq_register_opp_notifier(kbdev->dev, kbdev->devfreq);
@@ -667,11 +701,11 @@ int kbase_devfreq_init(struct kbase_device *kbdev)
goto opp_notifier_failed;
}
-#ifdef CONFIG_DEVFREQ_THERMAL
+#if IS_ENABLED(CONFIG_DEVFREQ_THERMAL)
err = kbase_ipa_init(kbdev);
if (err) {
dev_err(kbdev->dev, "IPA initialization failed\n");
- goto cooling_failed;
+ goto ipa_init_failed;
}
kbdev->devfreq_cooling = of_devfreq_cooling_register_power(
@@ -683,23 +717,28 @@ int kbase_devfreq_init(struct kbase_device *kbdev)
dev_err(kbdev->dev,
"Failed to register cooling device (%d)\n",
err);
- goto cooling_failed;
+ goto cooling_reg_failed;
}
#endif
return 0;
-#ifdef CONFIG_DEVFREQ_THERMAL
-cooling_failed:
+#if IS_ENABLED(CONFIG_DEVFREQ_THERMAL)
+cooling_reg_failed:
+ kbase_ipa_term(kbdev);
+ipa_init_failed:
devfreq_unregister_opp_notifier(kbdev->dev, kbdev->devfreq);
#endif /* CONFIG_DEVFREQ_THERMAL */
+
opp_notifier_failed:
+ kbase_devfreq_work_term(kbdev);
+
if (devfreq_remove_device(kbdev->devfreq))
dev_err(kbdev->dev, "Failed to terminate devfreq (%d)\n", err);
- else
- kbdev->devfreq = NULL;
- kbase_devfreq_work_term(kbdev);
+ kbdev->devfreq = NULL;
+
+ kbase_devfreq_term_core_mask_table(kbdev);
return err;
}
@@ -710,7 +749,7 @@ void kbase_devfreq_term(struct kbase_device *kbdev)
dev_dbg(kbdev->dev, "Term Mali devfreq\n");
-#ifdef CONFIG_DEVFREQ_THERMAL
+#if IS_ENABLED(CONFIG_DEVFREQ_THERMAL)
if (kbdev->devfreq_cooling)
devfreq_cooling_unregister(kbdev->devfreq_cooling);
@@ -719,6 +758,8 @@ void kbase_devfreq_term(struct kbase_device *kbdev)
devfreq_unregister_opp_notifier(kbdev->dev, kbdev->devfreq);
+ kbase_devfreq_work_term(kbdev);
+
err = devfreq_remove_device(kbdev->devfreq);
if (err)
dev_err(kbdev->dev, "Failed to terminate devfreq (%d)\n", err);
@@ -726,6 +767,4 @@ void kbase_devfreq_term(struct kbase_device *kbdev)
kbdev->devfreq = NULL;
kbase_devfreq_term_core_mask_table(kbdev);
-
- kbase_devfreq_work_term(kbdev);
}