// SPDX-License-Identifier: GPL-2.0 /* * Raspberry Pi cpufreq driver * * Copyright (C) 2019, Nicolas Saenz Julienne */ #include #include #include #include #include #include #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