162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Raspberry Pi cpufreq driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019, Nicolas Saenz Julienne <nsaenzjulienne@suse.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/cpu.h> 1062306a36Sopenharmony_ci#include <linux/cpufreq.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/pm_opp.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define RASPBERRYPI_FREQ_INTERVAL 100000000 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic struct platform_device *cpufreq_dt; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int raspberrypi_cpufreq_probe(struct platform_device *pdev) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct device *cpu_dev; 2262306a36Sopenharmony_ci unsigned long min, max; 2362306a36Sopenharmony_ci unsigned long rate; 2462306a36Sopenharmony_ci struct clk *clk; 2562306a36Sopenharmony_ci int ret; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci cpu_dev = get_cpu_device(0); 2862306a36Sopenharmony_ci if (!cpu_dev) { 2962306a36Sopenharmony_ci pr_err("Cannot get CPU for cpufreq driver\n"); 3062306a36Sopenharmony_ci return -ENODEV; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci clk = clk_get(cpu_dev, NULL); 3462306a36Sopenharmony_ci if (IS_ERR(clk)) { 3562306a36Sopenharmony_ci dev_err(cpu_dev, "Cannot get clock for CPU0\n"); 3662306a36Sopenharmony_ci return PTR_ERR(clk); 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * The max and min frequencies are configurable in the Raspberry Pi 4162306a36Sopenharmony_ci * firmware, so we query them at runtime. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci min = roundup(clk_round_rate(clk, 0), RASPBERRYPI_FREQ_INTERVAL); 4462306a36Sopenharmony_ci max = roundup(clk_round_rate(clk, ULONG_MAX), RASPBERRYPI_FREQ_INTERVAL); 4562306a36Sopenharmony_ci clk_put(clk); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci for (rate = min; rate <= max; rate += RASPBERRYPI_FREQ_INTERVAL) { 4862306a36Sopenharmony_ci ret = dev_pm_opp_add(cpu_dev, rate, 0); 4962306a36Sopenharmony_ci if (ret) 5062306a36Sopenharmony_ci goto remove_opp; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); 5462306a36Sopenharmony_ci ret = PTR_ERR_OR_ZERO(cpufreq_dt); 5562306a36Sopenharmony_ci if (ret) { 5662306a36Sopenharmony_ci dev_err(cpu_dev, "Failed to create platform device, %d\n", ret); 5762306a36Sopenharmony_ci goto remove_opp; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciremove_opp: 6362306a36Sopenharmony_ci dev_pm_opp_remove_all_dynamic(cpu_dev); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return ret; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void raspberrypi_cpufreq_remove(struct platform_device *pdev) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct device *cpu_dev; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci cpu_dev = get_cpu_device(0); 7362306a36Sopenharmony_ci if (cpu_dev) 7462306a36Sopenharmony_ci dev_pm_opp_remove_all_dynamic(cpu_dev); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci platform_device_unregister(cpufreq_dt); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * Since the driver depends on clk-raspberrypi, which may return EPROBE_DEFER, 8162306a36Sopenharmony_ci * all the activity is performed in the probe, which may be defered as well. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_cistatic struct platform_driver raspberrypi_cpufreq_driver = { 8462306a36Sopenharmony_ci .driver = { 8562306a36Sopenharmony_ci .name = "raspberrypi-cpufreq", 8662306a36Sopenharmony_ci }, 8762306a36Sopenharmony_ci .probe = raspberrypi_cpufreq_probe, 8862306a36Sopenharmony_ci .remove_new = raspberrypi_cpufreq_remove, 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_cimodule_platform_driver(raspberrypi_cpufreq_driver); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciMODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de"); 9362306a36Sopenharmony_ciMODULE_DESCRIPTION("Raspberry Pi cpufreq driver"); 9462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 9562306a36Sopenharmony_ciMODULE_ALIAS("platform:raspberrypi-cpufreq"); 96