/* * * (C) COPYRIGHT 2014-2017 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. * * A copy of the licence is included with the program, and can also be obtained * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #define ENABLE_DEBUG_LOG #include "../../platform/rk/custom_log.h" #include #include #include #include #include #include #include #ifdef CONFIG_DEVFREQ_THERMAL #include #endif #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) #include #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 #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 */ #include #include static struct devfreq_simple_ondemand_data ondemand_data; static struct monitor_dev_profile mali_mdevp = { .type = MONITOR_TYPE_DEV, .low_temp_adjust = rockchip_monitor_dev_low_temp_adjust, .high_temp_adjust = rockchip_monitor_dev_high_temp_adjust, }; /** * 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 * * Return: Real target frequency * * 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. */ static unsigned long opp_translate(struct kbase_device *kbdev, unsigned long freq, u64 *core_mask) { int i; for (i = 0; i < kbdev->num_opps; i++) { if (kbdev->opp_table[i].opp_freq == freq) { *core_mask = kbdev->opp_table[i].core_mask; return kbdev->opp_table[i].real_freq; } } /* Failed to find OPP - return all cores enabled & nominal frequency */ *core_mask = kbdev->gpu_props.props.raw_props.shader_present; return freq; } static int kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags) { struct kbase_device *kbdev = dev_get_drvdata(dev); struct dev_pm_opp *opp; unsigned long nominal_freq; unsigned long freq = 0; unsigned long old_freq = kbdev->current_freq; unsigned long voltage; int err; u64 core_mask; freq = *target_freq; opp = devfreq_recommended_opp(dev, &freq, flags); if (IS_ERR(opp)) { dev_err(dev, "Failed to get opp (%ld)\n", PTR_ERR(opp)); return PTR_ERR(opp); } voltage = dev_pm_opp_get_voltage(opp); nominal_freq = freq; /* * Only update if there is a change of frequency */ if (kbdev->current_nominal_freq == nominal_freq) { *target_freq = nominal_freq; #ifdef CONFIG_REGULATOR if (kbdev->current_voltage == voltage) return 0; err = regulator_set_voltage(kbdev->regulator, voltage, INT_MAX); if (err) { dev_err(dev, "Failed to set voltage (%d)\n", err); return err; } kbdev->current_voltage = voltage; #endif return 0; } freq = opp_translate(kbdev, nominal_freq, &core_mask); #ifdef CONFIG_REGULATOR if (kbdev->regulator && kbdev->current_voltage != voltage && old_freq < freq) { err = regulator_set_voltage(kbdev->regulator, voltage, INT_MAX); if (err) { dev_err(dev, "Failed to increase voltage (%d)\n", err); 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; } *target_freq = freq; kbdev->current_freq = freq; if (kbdev->devfreq) kbdev->devfreq->last_status.current_frequency = freq; #ifdef CONFIG_REGULATOR if (kbdev->regulator && kbdev->current_voltage != voltage && old_freq > freq) { err = regulator_set_voltage(kbdev->regulator, voltage, INT_MAX); if (err) { dev_err(dev, "Failed to decrease voltage (%d)\n", err); return err; } } #endif if (kbdev->pm.backend.ca_current_policy->id == KBASE_PM_CA_POLICY_ID_DEVFREQ) kbase_devfreq_set_core_mask(kbdev, core_mask); *target_freq = nominal_freq; kbdev->current_voltage = voltage; kbdev->current_nominal_freq = nominal_freq; kbdev->current_freq = freq; kbdev->current_core_mask = core_mask; KBASE_TLSTREAM_AUX_DEVFREQ_TARGET((u64)nominal_freq); kbase_pm_reset_dvfs_utilisation(kbdev); return err; } static int kbase_devfreq_cur_freq(struct device *dev, unsigned long *freq) { struct kbase_device *kbdev = dev_get_drvdata(dev); *freq = kbdev->current_nominal_freq; return 0; } static int kbase_devfreq_status(struct device *dev, struct devfreq_dev_status *stat) { struct kbase_device *kbdev = dev_get_drvdata(dev); stat->current_frequency = kbdev->current_nominal_freq; kbase_pm_get_dvfs_utilisation(kbdev, &stat->total_time, &stat->busy_time); stat->private_data = NULL; return 0; } static int kbase_devfreq_init_freq_table(struct kbase_device *kbdev, struct devfreq_dev_profile *dp) { int count; int i = 0; unsigned long freq; struct dev_pm_opp *opp; count = dev_pm_opp_get_opp_count(kbdev->dev); if (count < 0) { return count; } dp->freq_table = kmalloc_array(count, sizeof(dp->freq_table[0]), GFP_KERNEL); if (!dp->freq_table) return -ENOMEM; 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; dev_pm_opp_put(opp); dp->freq_table[i] = freq; } if (count != i) dev_warn(kbdev->dev, "Unable to enumerate all OPPs (%d!=%d\n", count, i); dp->max_state = i; return 0; } 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->opp_table); kbdev->opp_table = NULL; } static void kbase_devfreq_exit(struct device *dev) { struct kbase_device *kbdev = dev_get_drvdata(dev); if (kbdev) kbase_devfreq_term_freq_table(kbdev); } static int kbase_devfreq_init_core_mask_table(struct kbase_device *kbdev) { struct device_node *opp_node = of_parse_phandle(kbdev->dev->of_node, "operating-points-v2", 0); struct device_node *node; int i = 0; int count; if (!opp_node) return 0; if (!of_device_is_compatible(opp_node, "operating-points-v2-mali")) return 0; count = dev_pm_opp_get_opp_count(kbdev->dev); kbdev->opp_table = kmalloc_array(count, sizeof(struct kbase_devfreq_opp), GFP_KERNEL); if (!kbdev->opp_table) return -ENOMEM; for_each_available_child_of_node(opp_node, node) { u64 core_mask; u64 opp_freq, real_freq; const void *core_count_p; if (of_property_read_u64(node, "opp-hz", &opp_freq)) { dev_warn(kbdev->dev, "OPP is missing required opp-hz property\n"); continue; } if (of_property_read_u64(node, "opp-hz-real", &real_freq)) real_freq = opp_freq; if (of_property_read_u64(node, "opp-core-mask", &core_mask)) core_mask = kbdev->gpu_props.props.raw_props.shader_present; core_count_p = of_get_property(node, "opp-core-count", NULL); if (core_count_p) { u64 remaining_core_mask = kbdev->gpu_props.props.raw_props.shader_present; int core_count = be32_to_cpup(core_count_p); core_mask = 0; for (; core_count > 0; core_count--) { int core = ffs(remaining_core_mask); if (!core) { dev_err(kbdev->dev, "OPP has more cores than GPU\n"); return -ENODEV; } core_mask |= (1ull << (core-1)); remaining_core_mask &= ~(1ull << (core-1)); } } if (!core_mask) { dev_err(kbdev->dev, "OPP has invalid core mask of 0\n"); return -ENODEV; } kbdev->opp_table[i].opp_freq = opp_freq; kbdev->opp_table[i].real_freq = real_freq; 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++; } kbdev->num_opps = i; return 0; } int kbase_devfreq_init(struct kbase_device *kbdev) { struct device_node *np = kbdev->dev->of_node; struct devfreq_dev_profile *dp; struct dev_pm_opp *opp; unsigned long opp_rate; int err; if (!kbdev->clock) { dev_err(kbdev->dev, "Clock not available for devfreq\n"); return -ENODEV; } kbdev->current_freq = clk_get_rate(kbdev->clock); kbdev->current_nominal_freq = kbdev->current_freq; dp = &kbdev->devfreq_profile; dp->initial_freq = kbdev->current_freq; /* .KP : set devfreq_dvfs_interval_in_ms */ dp->polling_ms = 20; dp->target = kbase_devfreq_target; dp->get_dev_status = kbase_devfreq_status; dp->get_cur_freq = kbase_devfreq_cur_freq; dp->exit = kbase_devfreq_exit; if (kbase_devfreq_init_freq_table(kbdev, dp)) return -EFAULT; err = kbase_devfreq_init_core_mask_table(kbdev); if (err) goto init_core_mask_table_failed; of_property_read_u32(np, "upthreshold", &ondemand_data.upthreshold); of_property_read_u32(np, "downdifferential", &ondemand_data.downdifferential); kbdev->devfreq = devfreq_add_device(kbdev->dev, dp, "simple_ondemand", &ondemand_data); if (IS_ERR(kbdev->devfreq)) { err = PTR_ERR(kbdev->devfreq); kbdev->devfreq = NULL; dev_err(kbdev->dev, "Fail to add devfreq device(%d)", err); goto devfreq_add_dev_failed; } /* devfreq_add_device only copies a few of kbdev->dev's fields, so * 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); if (err) { dev_err(kbdev->dev, "Failed to register OPP notifier (%d)\n", err); goto opp_notifier_failed; } opp_rate = kbdev->current_freq; opp = devfreq_recommended_opp(kbdev->dev, &opp_rate, 0); if (!IS_ERR(opp)) dev_pm_opp_put(opp); kbdev->devfreq->last_status.current_frequency = opp_rate; mali_mdevp.data = kbdev->devfreq; kbdev->mdev_info = rockchip_system_monitor_register(kbdev->dev, &mali_mdevp); if (IS_ERR(kbdev->mdev_info)) { dev_dbg(kbdev->dev, "without system monitor\n"); kbdev->mdev_info = NULL; } #ifdef CONFIG_DEVFREQ_THERMAL err = kbase_ipa_init(kbdev); if (err) { dev_err(kbdev->dev, "IPA initialization failed\n"); goto cooling_failed; } kbdev->devfreq_cooling = of_devfreq_cooling_register_power( kbdev->dev->of_node, kbdev->devfreq, &kbase_ipa_power_model_ops); if (IS_ERR_OR_NULL(kbdev->devfreq_cooling)) { err = PTR_ERR(kbdev->devfreq_cooling); dev_err(kbdev->dev, "Failed to register cooling device (%d)\n", err); goto cooling_failed; } I("success initing power_model_simple."); #endif return 0; #ifdef CONFIG_DEVFREQ_THERMAL cooling_failed: devfreq_unregister_opp_notifier(kbdev->dev, kbdev->devfreq); #endif /* CONFIG_DEVFREQ_THERMAL */ opp_notifier_failed: if (devfreq_remove_device(kbdev->devfreq)) dev_err(kbdev->dev, "Failed to terminate devfreq (%d)\n", err); else kbdev->devfreq = NULL; devfreq_add_dev_failed: kbase_devfreq_term_core_mask_table(kbdev); init_core_mask_table_failed: kbase_devfreq_term_freq_table(kbdev); return err; } void kbase_devfreq_term(struct kbase_device *kbdev) { int err; dev_dbg(kbdev->dev, "Term Mali devfreq\n"); rockchip_system_monitor_unregister(kbdev->mdev_info); #ifdef CONFIG_DEVFREQ_THERMAL if (kbdev->devfreq_cooling) devfreq_cooling_unregister(kbdev->devfreq_cooling); kbase_ipa_term(kbdev); #endif devfreq_unregister_opp_notifier(kbdev->dev, kbdev->devfreq); err = devfreq_remove_device(kbdev->devfreq); if (err) dev_err(kbdev->dev, "Failed to terminate devfreq (%d)\n", err); else kbdev->devfreq = NULL; kbase_devfreq_term_core_mask_table(kbdev); }