Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpufreq: add driver for Raspberry Pi

Raspberry Pi's firmware offers and interface though which update it's
performance requirements. It allows us to request for specific runtime
frequencies, which the firmware might or might not respect, depending on
the firmware configuration and thermals.

As the maximum and minimum frequencies are configurable in the firmware
there is no way to know in advance their values. So the Raspberry Pi
cpufreq driver queries them, builds an opp frequency table to then
launch cpufreq-dt.

Also, as the firmware interface might be configured as a module, making
the cpu clock unavailable during init, this implements a full fledged
driver, as opposed to most drivers registering cpufreq-dt, which only
make use of an init routine.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Acked-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
Acked-by: Stefan Wahren <stefan.wahren@i2se.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>

authored by

Nicolas Saenz Julienne and committed by
Viresh Kumar
d3df18a9 e6abacab

+106
+8
drivers/cpufreq/Kconfig.arm
··· 141 141 The driver implements the cpufreq interface for this HW engine. 142 142 Say Y if you want to support CPUFreq HW. 143 143 144 + config ARM_RASPBERRYPI_CPUFREQ 145 + tristate "Raspberry Pi cpufreq support" 146 + depends on CLK_RASPBERRYPI || COMPILE_TEST 147 + help 148 + This adds the CPUFreq driver for Raspberry Pi 149 + 150 + If in doubt, say N. 151 + 144 152 config ARM_S3C_CPUFREQ 145 153 bool 146 154 help
+1
drivers/cpufreq/Makefile
··· 65 65 obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o 66 66 obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o 67 67 obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o 68 + obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o 68 69 obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o 69 70 obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o 70 71 obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
+97
drivers/cpufreq/raspberrypi-cpufreq.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Raspberry Pi cpufreq driver 4 + * 5 + * Copyright (C) 2019, Nicolas Saenz Julienne <nsaenzjulienne@suse.de> 6 + */ 7 + 8 + #include <linux/clk.h> 9 + #include <linux/cpu.h> 10 + #include <linux/cpufreq.h> 11 + #include <linux/module.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/pm_opp.h> 14 + 15 + #define RASPBERRYPI_FREQ_INTERVAL 100000000 16 + 17 + static struct platform_device *cpufreq_dt; 18 + 19 + static int raspberrypi_cpufreq_probe(struct platform_device *pdev) 20 + { 21 + struct device *cpu_dev; 22 + unsigned long min, max; 23 + unsigned long rate; 24 + struct clk *clk; 25 + int ret; 26 + 27 + cpu_dev = get_cpu_device(0); 28 + if (!cpu_dev) { 29 + pr_err("Cannot get CPU for cpufreq driver\n"); 30 + return -ENODEV; 31 + } 32 + 33 + clk = clk_get(cpu_dev, NULL); 34 + if (IS_ERR(clk)) { 35 + dev_err(cpu_dev, "Cannot get clock for CPU0\n"); 36 + return PTR_ERR(clk); 37 + } 38 + 39 + /* 40 + * The max and min frequencies are configurable in the Raspberry Pi 41 + * firmware, so we query them at runtime. 42 + */ 43 + min = roundup(clk_round_rate(clk, 0), RASPBERRYPI_FREQ_INTERVAL); 44 + max = roundup(clk_round_rate(clk, ULONG_MAX), RASPBERRYPI_FREQ_INTERVAL); 45 + clk_put(clk); 46 + 47 + for (rate = min; rate <= max; rate += RASPBERRYPI_FREQ_INTERVAL) { 48 + ret = dev_pm_opp_add(cpu_dev, rate, 0); 49 + if (ret) 50 + goto remove_opp; 51 + } 52 + 53 + cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); 54 + ret = PTR_ERR_OR_ZERO(cpufreq_dt); 55 + if (ret) { 56 + dev_err(cpu_dev, "Failed to create platform device, %d\n", ret); 57 + goto remove_opp; 58 + } 59 + 60 + return 0; 61 + 62 + remove_opp: 63 + dev_pm_opp_remove_all_dynamic(cpu_dev); 64 + 65 + return ret; 66 + } 67 + 68 + static int raspberrypi_cpufreq_remove(struct platform_device *pdev) 69 + { 70 + struct device *cpu_dev; 71 + 72 + cpu_dev = get_cpu_device(0); 73 + if (cpu_dev) 74 + dev_pm_opp_remove_all_dynamic(cpu_dev); 75 + 76 + platform_device_unregister(cpufreq_dt); 77 + 78 + return 0; 79 + } 80 + 81 + /* 82 + * Since the driver depends on clk-raspberrypi, which may return EPROBE_DEFER, 83 + * all the activity is performed in the probe, which may be defered as well. 84 + */ 85 + static struct platform_driver raspberrypi_cpufreq_driver = { 86 + .driver = { 87 + .name = "raspberrypi-cpufreq", 88 + }, 89 + .probe = raspberrypi_cpufreq_probe, 90 + .remove = raspberrypi_cpufreq_remove, 91 + }; 92 + module_platform_driver(raspberrypi_cpufreq_driver); 93 + 94 + MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de"); 95 + MODULE_DESCRIPTION("Raspberry Pi cpufreq driver"); 96 + MODULE_LICENSE("GPL"); 97 + MODULE_ALIAS("platform:raspberrypi-cpufreq");