Branch data Line data Source code
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 : 404 : 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 : 404 : cpu_dev = get_cpu_device(0); 28 [ - + ]: 404 : if (!cpu_dev) { 29 : 0 : pr_err("Cannot get CPU for cpufreq driver\n"); 30 : 0 : return -ENODEV; 31 : : } 32 : : 33 : 404 : clk = clk_get(cpu_dev, NULL); 34 [ + - ]: 404 : if (IS_ERR(clk)) { 35 : 404 : dev_err(cpu_dev, "Cannot get clock for CPU0\n"); 36 : 404 : 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 : 0 : min = roundup(clk_round_rate(clk, 0), RASPBERRYPI_FREQ_INTERVAL); 44 : 0 : max = roundup(clk_round_rate(clk, ULONG_MAX), RASPBERRYPI_FREQ_INTERVAL); 45 : 0 : clk_put(clk); 46 : : 47 [ # # ]: 0 : for (rate = min; rate <= max; rate += RASPBERRYPI_FREQ_INTERVAL) { 48 : 0 : ret = dev_pm_opp_add(cpu_dev, rate, 0); 49 [ # # ]: 0 : if (ret) 50 : : goto remove_opp; 51 : : } 52 : : 53 : 0 : cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); 54 : : ret = PTR_ERR_OR_ZERO(cpufreq_dt); 55 [ # # ]: 0 : if (ret) { 56 : 0 : dev_err(cpu_dev, "Failed to create platform device, %d\n", ret); 57 : 0 : goto remove_opp; 58 : : } 59 : : 60 : : return 0; 61 : : 62 : : remove_opp: 63 : 0 : dev_pm_opp_remove_all_dynamic(cpu_dev); 64 : : 65 : 0 : return ret; 66 : : } 67 : : 68 : 0 : static int raspberrypi_cpufreq_remove(struct platform_device *pdev) 69 : : { 70 : : struct device *cpu_dev; 71 : : 72 : 0 : cpu_dev = get_cpu_device(0); 73 [ # # ]: 0 : if (cpu_dev) 74 : 0 : dev_pm_opp_remove_all_dynamic(cpu_dev); 75 : : 76 : 0 : platform_device_unregister(cpufreq_dt); 77 : : 78 : 0 : 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 : 404 : 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");