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(cpu: 0); |
28 | if (!cpu_dev) { |
29 | pr_err("Cannot get CPU for cpufreq driver\n" ); |
30 | return -ENODEV; |
31 | } |
32 | |
33 | clk = clk_get(dev: cpu_dev, NULL); |
34 | if (IS_ERR(ptr: clk)) { |
35 | dev_err(cpu_dev, "Cannot get clock for CPU0\n" ); |
36 | return PTR_ERR(ptr: 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(dev: cpu_dev, freq: rate, u_volt: 0); |
49 | if (ret) |
50 | goto remove_opp; |
51 | } |
52 | |
53 | cpufreq_dt = platform_device_register_simple(name: "cpufreq-dt" , id: -1, NULL, num: 0); |
54 | ret = PTR_ERR_OR_ZERO(ptr: 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(dev: 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(cpu: 0); |
73 | if (cpu_dev) |
74 | dev_pm_opp_remove_all_dynamic(dev: 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_new = 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 | |