18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * CPUFreq support for Armada 370/XP platforms. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2012-2016 Marvell 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Yehuda Yitschak <yehuday@marvell.com> 78c2ecf20Sopenharmony_ci * Gregory Clement <gregory.clement@free-electrons.com> 88c2ecf20Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 118c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 128c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "mvebu-pmsu: " fmt 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci#include <linux/cpu.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/of_address.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/pm_opp.h> 248c2ecf20Sopenharmony_ci#include <linux/resource.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int __init armada_xp_pmsu_cpufreq_init(void) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct device_node *np; 298c2ecf20Sopenharmony_ci struct resource res; 308c2ecf20Sopenharmony_ci int ret, cpu; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (!of_machine_is_compatible("marvell,armadaxp")) 338c2ecf20Sopenharmony_ci return 0; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* 368c2ecf20Sopenharmony_ci * In order to have proper cpufreq handling, we need to ensure 378c2ecf20Sopenharmony_ci * that the Device Tree description of the CPU clock includes 388c2ecf20Sopenharmony_ci * the definition of the PMU DFS registers. If not, we do not 398c2ecf20Sopenharmony_ci * register the clock notifier and the cpufreq driver. This 408c2ecf20Sopenharmony_ci * piece of code is only for compatibility with old Device 418c2ecf20Sopenharmony_ci * Trees. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "marvell,armada-xp-cpu-clock"); 448c2ecf20Sopenharmony_ci if (!np) 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 1, &res); 488c2ecf20Sopenharmony_ci if (ret) { 498c2ecf20Sopenharmony_ci pr_warn(FW_WARN "not enabling cpufreq, deprecated armada-xp-cpu-clock binding\n"); 508c2ecf20Sopenharmony_ci of_node_put(np); 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci of_node_put(np); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * For each CPU, this loop registers the operating points 588c2ecf20Sopenharmony_ci * supported (which are the nominal CPU frequency and half of 598c2ecf20Sopenharmony_ci * it), and registers the clock notifier that will take care 608c2ecf20Sopenharmony_ci * of doing the PMSU part of a frequency transition. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 638c2ecf20Sopenharmony_ci struct device *cpu_dev; 648c2ecf20Sopenharmony_ci struct clk *clk; 658c2ecf20Sopenharmony_ci int ret; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci cpu_dev = get_cpu_device(cpu); 688c2ecf20Sopenharmony_ci if (!cpu_dev) { 698c2ecf20Sopenharmony_ci pr_err("Cannot get CPU %d\n", cpu); 708c2ecf20Sopenharmony_ci continue; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci clk = clk_get(cpu_dev, NULL); 748c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 758c2ecf20Sopenharmony_ci pr_err("Cannot get clock for CPU %d\n", cpu); 768c2ecf20Sopenharmony_ci return PTR_ERR(clk); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk), 0); 808c2ecf20Sopenharmony_ci if (ret) { 818c2ecf20Sopenharmony_ci clk_put(clk); 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk) / 2, 0); 868c2ecf20Sopenharmony_ci if (ret) { 878c2ecf20Sopenharmony_ci dev_pm_opp_remove(cpu_dev, clk_get_rate(clk)); 888c2ecf20Sopenharmony_ci clk_put(clk); 898c2ecf20Sopenharmony_ci dev_err(cpu_dev, "Failed to register OPPs\n"); 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = dev_pm_opp_set_sharing_cpus(cpu_dev, 948c2ecf20Sopenharmony_ci cpumask_of(cpu_dev->id)); 958c2ecf20Sopenharmony_ci if (ret) 968c2ecf20Sopenharmony_ci dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", 978c2ecf20Sopenharmony_ci __func__, ret); 988c2ecf20Sopenharmony_ci clk_put(clk); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci platform_device_register_simple("cpufreq-dt", -1, NULL, 0); 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_cidevice_initcall(armada_xp_pmsu_cpufreq_init); 105