raspberrypi-cpufreq.c (2373B)
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 17static struct platform_device *cpufreq_dt; 18 19static 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 62remove_opp: 63 dev_pm_opp_remove_all_dynamic(cpu_dev); 64 65 return ret; 66} 67 68static 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 */ 85static struct platform_driver raspberrypi_cpufreq_driver = { 86 .driver = { 87 .name = "raspberrypi-cpufreq", 88 }, 89 .probe = raspberrypi_cpufreq_probe, 90 .remove = raspberrypi_cpufreq_remove, 91}; 92module_platform_driver(raspberrypi_cpufreq_driver); 93 94MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de"); 95MODULE_DESCRIPTION("Raspberry Pi cpufreq driver"); 96MODULE_LICENSE("GPL"); 97MODULE_ALIAS("platform:raspberrypi-cpufreq");