--- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -15,6 +15,7 @@ clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-k clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o +clk-qcom-y += fab_scaling.o # Keep alphabetically sorted by config obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o --- /dev/null +++ b/drivers/clk/qcom/fab_scaling.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct qcom_fab_scaling_data { + u32 fab_freq_high; + u32 fab_freq_nominal; + u32 cpu_freq_threshold; + struct clk *apps_fab_clk; + struct clk *ddr_fab_clk; +}; + +static struct qcom_fab_scaling_data *drv_data; + +int scale_fabrics(unsigned long max_cpu_freq) +{ + struct clk *apps_fab_clk = drv_data->apps_fab_clk, + *ddr_fab_clk = drv_data->ddr_fab_clk; + unsigned long target_freq, cur_freq; + int ret; + + /* Skip fab scaling if the driver is not ready */ + if (!apps_fab_clk || !ddr_fab_clk) + return 0; + + if (max_cpu_freq > drv_data->cpu_freq_threshold) + target_freq = drv_data->fab_freq_high; + else + target_freq = drv_data->fab_freq_nominal; + + cur_freq = clk_get_rate(ddr_fab_clk); + + if (target_freq != cur_freq) { + ret = clk_set_rate(apps_fab_clk, target_freq); + if (ret) + return ret; + ret = clk_set_rate(ddr_fab_clk, target_freq); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(scale_fabrics); + +static int ipq806x_fab_scaling_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct clk *apps_fab_clk, *ddr_fab_clk; + int ret; + + if (!np) + return -ENODEV; + + drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + if (of_property_read_u32(np, "fab_freq_high", &drv_data->fab_freq_high)) { + pr_err("FABRICS turbo freq not found. Using defaults...\n"); + drv_data->fab_freq_high = 533000000; + } + + if (of_property_read_u32(np, "fab_freq_nominal", &drv_data->fab_freq_nominal)) { + pr_err("FABRICS nominal freq not found. Using defaults...\n"); + drv_data->fab_freq_nominal = 400000000; + } + + if (of_property_read_u32(np, "cpu_freq_threshold", &drv_data->cpu_freq_threshold)) { + pr_err("FABRICS cpu freq threshold not found. Using defaults...\n"); + drv_data->cpu_freq_threshold = 1000000000; + } + + apps_fab_clk = devm_clk_get(&pdev->dev, "apps-fab-clk"); + ret = PTR_ERR_OR_ZERO(apps_fab_clk); + if (ret) { + /* + * If apps fab clk node is present, but clock is not yet + * registered, we should try defering probe. + */ + if (ret != -EPROBE_DEFER) { + pr_err("Failed to get APPS FABRIC clock: %d\n", ret); + ret = -ENODEV; + } + goto err; + } + + clk_prepare_enable(apps_fab_clk); + clk_set_rate(apps_fab_clk, drv_data->fab_freq_high); + drv_data->apps_fab_clk = apps_fab_clk; + + ddr_fab_clk = devm_clk_get(&pdev->dev, "ddr-fab-clk"); + ret = PTR_ERR_OR_ZERO(ddr_fab_clk); + if (ret) { + /* + * If ddr fab clk node is present, but clock is not yet + * registered, we should try defering probe. + */ + if (ret != -EPROBE_DEFER) { + pr_err("Failed to get DDR FABRIC clock: %d\n", ret); + ddr_fab_clk = NULL; + ret = -ENODEV; + } + goto err; + } + + clk_prepare_enable(ddr_fab_clk); + clk_set_rate(ddr_fab_clk, drv_data->fab_freq_high); + drv_data->ddr_fab_clk = ddr_fab_clk; + + return 0; +err: + kfree(drv_data); + return ret; +} + +static int ipq806x_fab_scaling_remove(struct platform_device *pdev) +{ + kfree(drv_data); + return 0; +} + +static const struct of_device_id fab_scaling_ipq806x_match_table[] = { + { .compatible = "qcom,fab-scaling" }, + { } +}; + +static struct platform_driver fab_scaling_ipq806x_driver = { + .probe = ipq806x_fab_scaling_probe, + .remove = ipq806x_fab_scaling_remove, + .driver = { + .name = "fab-scaling", + .of_match_table = fab_scaling_ipq806x_match_table, + }, +}; + +static int __init fab_scaling_ipq806x_init(void) +{ + return platform_driver_register(&fab_scaling_ipq806x_driver); +} +late_initcall(fab_scaling_ipq806x_init); + +static void __exit fab_scaling_ipq806x_exit(void) +{ + platform_driver_unregister(&fab_scaling_ipq806x_driver); +} +module_exit(fab_scaling_ipq806x_exit); --- /dev/null +++ b/include/linux/fab_scaling.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifndef __FAB_SCALING_H +#define __FAB_SCALING_H + +/** + * scale_fabrics - Scale DDR and APPS FABRICS + * + * This function monitors all the registered clocks and does APPS + * and DDR FABRIC scaling based on the idle frequencies with which + * it was registered. + * + */ +int scale_fabrics(unsigned long max_cpu_freq); + +#endif --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "cpufreq-dt.h" @@ -111,6 +112,13 @@ static int set_target(struct cpufreq_pol } } } + + /* + * Scale fabrics with max freq across all cores + */ + ret = scale_fabrics(target_freq); + if (ret) + goto exit; } priv->opp_freq = freq * 1000; arch_set_freq_scale(policy->related_cpus, freq,