Skip to content

Instantly share code, notes, and snippets.

@Ansuel
Created August 3, 2020 19:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ansuel/87064910fafa2e702b3109a8c8c8b866 to your computer and use it in GitHub Desktop.
Save Ansuel/87064910fafa2e702b3109a8c8c8b866 to your computer and use it in GitHub Desktop.
--- 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