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 void 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 79 /* 80 * Since the driver depends on clk-raspberrypi, which may return EPROBE_DEFER, 81 * all the activity is performed in the probe, which may be defered as well. 82 */ 83 static struct platform_driver raspberrypi_cpufreq_driver = { 84 .driver = { 85 .name = "raspberrypi-cpufreq", 86 }, 87 .probe = raspberrypi_cpufreq_probe, 88 .remove = raspberrypi_cpufreq_remove, 89 }; 90 module_platform_driver(raspberrypi_cpufreq_driver); 91 92 MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de"); 93 MODULE_DESCRIPTION("Raspberry Pi cpufreq driver"); 94 MODULE_LICENSE("GPL"); 95 MODULE_ALIAS("platform:raspberrypi-cpufreq"); 96