diff options
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.c | 207 |
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); } |