mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
97 lines
2.3 KiB
97 lines
2.3 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Raspberry Pi cpufreq driver |
|
* |
|
* Copyright (C) 2019, Nicolas Saenz Julienne <[email protected]> |
|
*/ |
|
|
|
#include <linux/clk.h> |
|
#include <linux/cpu.h> |
|
#include <linux/cpufreq.h> |
|
#include <linux/module.h> |
|
#include <linux/platform_device.h> |
|
#include <linux/pm_opp.h> |
|
|
|
#define RASPBERRYPI_FREQ_INTERVAL 100000000 |
|
|
|
static struct platform_device *cpufreq_dt; |
|
|
|
static int raspberrypi_cpufreq_probe(struct platform_device *pdev) |
|
{ |
|
struct device *cpu_dev; |
|
unsigned long min, max; |
|
unsigned long rate; |
|
struct clk *clk; |
|
int ret; |
|
|
|
cpu_dev = get_cpu_device(0); |
|
if (!cpu_dev) { |
|
pr_err("Cannot get CPU for cpufreq driver\n"); |
|
return -ENODEV; |
|
} |
|
|
|
clk = clk_get(cpu_dev, NULL); |
|
if (IS_ERR(clk)) { |
|
dev_err(cpu_dev, "Cannot get clock for CPU0\n"); |
|
return PTR_ERR(clk); |
|
} |
|
|
|
/* |
|
* The max and min frequencies are configurable in the Raspberry Pi |
|
* firmware, so we query them at runtime. |
|
*/ |
|
min = roundup(clk_round_rate(clk, 0), RASPBERRYPI_FREQ_INTERVAL); |
|
max = roundup(clk_round_rate(clk, ULONG_MAX), RASPBERRYPI_FREQ_INTERVAL); |
|
clk_put(clk); |
|
|
|
for (rate = min; rate <= max; rate += RASPBERRYPI_FREQ_INTERVAL) { |
|
ret = dev_pm_opp_add(cpu_dev, rate, 0); |
|
if (ret) |
|
goto remove_opp; |
|
} |
|
|
|
cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); |
|
ret = PTR_ERR_OR_ZERO(cpufreq_dt); |
|
if (ret) { |
|
dev_err(cpu_dev, "Failed to create platform device, %d\n", ret); |
|
goto remove_opp; |
|
} |
|
|
|
return 0; |
|
|
|
remove_opp: |
|
dev_pm_opp_remove_all_dynamic(cpu_dev); |
|
|
|
return ret; |
|
} |
|
|
|
static int raspberrypi_cpufreq_remove(struct platform_device *pdev) |
|
{ |
|
struct device *cpu_dev; |
|
|
|
cpu_dev = get_cpu_device(0); |
|
if (cpu_dev) |
|
dev_pm_opp_remove_all_dynamic(cpu_dev); |
|
|
|
platform_device_unregister(cpufreq_dt); |
|
|
|
return 0; |
|
} |
|
|
|
/* |
|
* Since the driver depends on clk-raspberrypi, which may return EPROBE_DEFER, |
|
* all the activity is performed in the probe, which may be defered as well. |
|
*/ |
|
static struct platform_driver raspberrypi_cpufreq_driver = { |
|
.driver = { |
|
.name = "raspberrypi-cpufreq", |
|
}, |
|
.probe = raspberrypi_cpufreq_probe, |
|
.remove = raspberrypi_cpufreq_remove, |
|
}; |
|
module_platform_driver(raspberrypi_cpufreq_driver); |
|
|
|
MODULE_AUTHOR("Nicolas Saenz Julienne <[email protected]"); |
|
MODULE_DESCRIPTION("Raspberry Pi cpufreq driver"); |
|
MODULE_LICENSE("GPL"); |
|
MODULE_ALIAS("platform:raspberrypi-cpufreq");
|
|
|