Created
August 3, 2020 19:20
-
-
Save Ansuel/87064910fafa2e702b3109a8c8c8b866 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- a/drivers/cpufreq/Kconfig.arm | |
+++ b/drivers/cpufreq/Kconfig.arm | |
@@ -126,6 +126,14 @@ config ARM_OMAP2PLUS_CPUFREQ | |
depends on ARCH_OMAP2PLUS | |
default ARCH_OMAP2PLUS | |
+config ARM_QCOM_KRAIT_CACHE | |
+ tristate "CPU Cache Frequency scaling support for Krait SoCs" | |
+ depends on ARCH_QCOM || COMPILE_TEST | |
+ help | |
+ This adds the CPU Cache scaling driver for Qualcomm Krait SoC based boards. | |
+ | |
+ If in doubt, say N. | |
+ | |
config ARM_QCOM_CPUFREQ_NVMEM | |
tristate "Qualcomm nvmem based CPUFreq" | |
depends on ARM64 | |
--- a/drivers/cpufreq/Makefile | |
+++ b/drivers/cpufreq/Makefile | |
@@ -63,6 +63,7 @@ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o | |
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o | |
obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o | |
obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o | |
+obj-$(CONFIG_ARM_QCOM_KRAIT_CACHE) += krait-cache.o | |
obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o | |
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o | |
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o | |
--- /dev/null | |
+++ b/drivers/cpufreq/krait-cache.c | |
@@ -0,0 +1,212 @@ | |
+ | |
+#include <linux/kernel.h> | |
+#include <linux/init.h> | |
+#include <linux/module.h> | |
+#include <linux/cpufreq.h> | |
+#include <linux/of.h> | |
+#include <linux/platform_device.h> | |
+#include <linux/clk.h> | |
+#include <linux/slab.h> | |
+#include <linux/regulator/consumer.h> | |
+ | |
+struct krait_data { | |
+ struct clk *l2_clk; /* L2 clock */ | |
+ unsigned int l2_volt_tol; /* L2 voltage tolerance */ | |
+ | |
+ struct regulator *l2_regulator; /* L2 supply */ | |
+ unsigned int l2_rate[3]; /* L2 bus clock rate */ | |
+ bool l2_rate_set; | |
+ | |
+ unsigned int l2_cpufreq[3]; /* L2 target CPU frequency */ | |
+ unsigned int l2_volt[3]; /* L2 voltage array */ | |
+ | |
+ unsigned long curr_l2_freq; | |
+ unsigned long curr_l2_volt; | |
+ | |
+ struct notifier_block nb; | |
+}; | |
+ | |
+static int krait_cache_notifier(struct notifier_block *nb, unsigned long cmd, | |
+ void *v) | |
+{ | |
+ struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)v; | |
+ unsigned long l2_freq, target_l2_freq; | |
+ unsigned long l2_vol, target_l2_volt; | |
+ struct regulator *l2_regulator; | |
+ struct krait_data *data; | |
+ struct clk *l2_clk; | |
+ int ret = 0; | |
+ | |
+ data = container_of(nb, struct krait_data, nb); | |
+ | |
+ if (cmd == CPUFREQ_PRECHANGE) { | |
+ unsigned int target_freq = freqs->new; | |
+ int cpu, cur_cpu = freqs->policy->cpu, l2_index, tol = 0; | |
+ l2_clk = data->l2_clk; | |
+ | |
+ /* find the max freq across all core */ | |
+ for_each_present_cpu (cpu) | |
+ if (cpu != cur_cpu) | |
+ target_freq = max(target_freq, | |
+ cpufreq_quick_get(cpu)); | |
+ | |
+ /* find l2_freq and l2_volt */ | |
+ for (l2_index = 0; | |
+ l2_index < 2 && target_freq <= data->l2_cpufreq[l2_index]; | |
+ l2_index++) | |
+ break; | |
+ | |
+ l2_freq = data->curr_l2_freq; | |
+ target_l2_freq = data->l2_rate[l2_index]; | |
+ | |
+ /* scale only if needed */ | |
+ if (l2_freq != target_l2_freq) { | |
+ /* | |
+ * Set to idle bin if switching from normal to high bin | |
+ * or vice versa. It has been notice that a bug is triggered | |
+ * in cache scaling when more than one bin is scaled, to fix | |
+ * this we first need to transition to the base rate and then | |
+ * to target rate | |
+ */ | |
+ if ((l2_index == 2 && l2_freq == data->l2_rate[1]) || | |
+ (l2_index == 1 && l2_freq == data->l2_rate[2])) { | |
+ ret = clk_set_rate(l2_clk, data->l2_rate[0]); | |
+ if (ret) | |
+ goto exit; | |
+ } | |
+ | |
+ ret = clk_set_rate(l2_clk, target_l2_freq); | |
+ if (ret) | |
+ goto exit; | |
+ | |
+ data->curr_l2_freq = target_l2_freq; | |
+ | |
+ l2_regulator = data->l2_regulator; | |
+ if (l2_regulator) { | |
+ l2_vol = data->curr_l2_volt; | |
+ target_l2_volt = data->l2_volt[l2_index]; | |
+ | |
+ if (l2_vol != target_l2_volt) { | |
+ tol = target_l2_volt * | |
+ data->l2_volt_tol / 100; | |
+ ret = regulator_set_voltage_tol( | |
+ l2_regulator, target_l2_volt, | |
+ tol); | |
+ if (ret) | |
+ goto exit; | |
+ | |
+ data->curr_l2_volt = target_l2_volt; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+exit: | |
+ return notifier_from_errno(ret); | |
+} | |
+ | |
+static int krait_cache_probe(struct platform_device *pdev) | |
+{ | |
+ int ret; | |
+ struct clk *l2_clk; | |
+ struct device_node *vdd; | |
+ struct krait_data *data; | |
+ struct regulator *l2_regulator; | |
+ struct device *dev = &pdev->dev; | |
+ struct device_node *node = dev->of_node; | |
+ | |
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | |
+ if (!data) | |
+ return -ENOMEM; | |
+ | |
+ data->l2_clk = clk_get(dev, "l2"); | |
+ if (IS_ERR(l2_clk)) { | |
+ ret = PTR_ERR(l2_clk); | |
+ goto err; | |
+ } | |
+ | |
+ of_property_read_u32(node, "voltage-tolerance", &data->l2_volt_tol); | |
+ | |
+ of_property_read_u32_array(node, "l2-rates", data->l2_rate, 3); | |
+ if (data->l2_rate[0] && data->l2_rate[1] && data->l2_rate[2]) { | |
+ data->l2_rate_set = true; | |
+ of_property_read_u32_array(node, "l2-cpufreq", data->l2_cpufreq, | |
+ 3); | |
+ of_property_read_u32_array(node, "l2-volt", data->l2_volt, 3); | |
+ } else | |
+ dev_warn(dev, "failed to parse L2 rates\n"); | |
+ | |
+ if (!data->l2_cpufreq[0] && !data->l2_cpufreq[1] && | |
+ !data->l2_cpufreq[2] && data->l2_rate_set) { | |
+ int i; | |
+ | |
+ dev_warn(dev, | |
+ "failed to parse target cpu freq, using defaults\n"); | |
+ for (i = 0; i < 3; i++) | |
+ data->l2_cpufreq[i] = data->l2_rate[i]; | |
+ } | |
+ | |
+ if (data->l2_volt[0] && data->l2_volt[1] && data->l2_volt[2] && | |
+ data->l2_rate_set) { | |
+ vdd = of_parse_phandle(node, "l2-supply", 0); | |
+ | |
+ if (vdd) { | |
+ l2_regulator = devm_regulator_get(dev, vdd->name); | |
+ if (!IS_ERR(l2_regulator)) { | |
+ data->l2_regulator = l2_regulator; | |
+ } else { | |
+ dev_err(dev, | |
+ "failed to get l2 supply, error=%pe\n", | |
+ l2_regulator); | |
+ } | |
+ | |
+ of_node_put(vdd); | |
+ } | |
+ } | |
+ | |
+ platform_set_drvdata(pdev, data); | |
+ | |
+ data->nb.notifier_call = krait_cache_notifier; | |
+ cpufreq_register_notifier(&data->nb, CPUFREQ_TRANSITION_NOTIFIER); | |
+ | |
+ return 0; | |
+ | |
+err: | |
+ kfree(data); | |
+ return ret; | |
+} | |
+ | |
+static int krait_cache_remove(struct platform_device *pdev) | |
+{ | |
+ struct krait_data *data = platform_get_drvdata(pdev); | |
+ | |
+ cpufreq_unregister_notifier(&data->nb, CPUFREQ_TRANSITION_NOTIFIER); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static const struct of_device_id krait_cache_match_table[] = { | |
+ { .compatible = "qcom,krait-cache" }, | |
+ {} | |
+}; | |
+ | |
+static struct platform_driver krait_cache_driver = { | |
+ .probe = krait_cache_probe, | |
+ .remove = krait_cache_remove, | |
+ .driver = { | |
+ .name = "krait-cache-scaling", | |
+ .of_match_table = krait_cache_match_table, | |
+ }, | |
+}; | |
+ | |
+static int __init krait_cache_init(void) | |
+{ | |
+ return platform_driver_register(&krait_cache_driver); | |
+} | |
+late_initcall(krait_cache_init); | |
+ | |
+static void __exit krait_cache_exit(void) | |
+{ | |
+ platform_driver_unregister(&krait_cache_driver); | |
+} | |
+module_exit(krait_cache_exit); | |
-- | |
2.27.0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment