1/* 2 * Generic big.LITTLE CPUFreq Interface driver 3 * 4 * It provides necessary ops to arm_big_little cpufreq driver and gets 5 * Frequency information from Device Tree. Freq table in DT must be in KHz. 6 * 7 * Copyright (C) 2013 Linaro. 8 * Viresh Kumar <viresh.kumar@linaro.org> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 15 * kind, whether express or implied; without even the implied warranty 16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 */ 19 20#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 21 22#include <linux/cpufreq.h> 23#include <linux/device.h> 24#include <linux/export.h> 25#include <linux/module.h> 26#include <linux/of_device.h> 27#include <linux/pm_opp.h> 28#include <linux/platform_device.h> 29#include <linux/slab.h> 30#include <linux/types.h> 31#include "arm_big_little.h" 32 33/* get cpu node with valid operating-points */ 34static struct device_node *get_cpu_node_with_valid_op(int cpu) 35{ 36 struct device_node *np = of_cpu_device_node_get(cpu); 37 38 if (!of_get_property(np, "operating-points", NULL)) { 39 of_node_put(np); 40 np = NULL; 41 } 42 43 return np; 44} 45 46static int dt_init_opp_table(struct device *cpu_dev) 47{ 48 struct device_node *np; 49 int ret; 50 51 np = of_node_get(cpu_dev->of_node); 52 if (!np) { 53 pr_err("failed to find cpu%d node\n", cpu_dev->id); 54 return -ENOENT; 55 } 56 57 ret = dev_pm_opp_of_add_table(cpu_dev); 58 of_node_put(np); 59 60 return ret; 61} 62 63static int dt_get_transition_latency(struct device *cpu_dev) 64{ 65 struct device_node *np; 66 u32 transition_latency = CPUFREQ_ETERNAL; 67 68 np = of_node_get(cpu_dev->of_node); 69 if (!np) { 70 pr_info("Failed to find cpu node. Use CPUFREQ_ETERNAL transition latency\n"); 71 return CPUFREQ_ETERNAL; 72 } 73 74 of_property_read_u32(np, "clock-latency", &transition_latency); 75 of_node_put(np); 76 77 pr_debug("%s: clock-latency: %d\n", __func__, transition_latency); 78 return transition_latency; 79} 80 81static struct cpufreq_arm_bL_ops dt_bL_ops = { 82 .name = "dt-bl", 83 .get_transition_latency = dt_get_transition_latency, 84 .init_opp_table = dt_init_opp_table, 85 .free_opp_table = dev_pm_opp_of_remove_table, 86}; 87 88static int generic_bL_probe(struct platform_device *pdev) 89{ 90 struct device_node *np; 91 92 np = get_cpu_node_with_valid_op(0); 93 if (!np) 94 return -ENODEV; 95 96 of_node_put(np); 97 return bL_cpufreq_register(&dt_bL_ops); 98} 99 100static int generic_bL_remove(struct platform_device *pdev) 101{ 102 bL_cpufreq_unregister(&dt_bL_ops); 103 return 0; 104} 105 106static struct platform_driver generic_bL_platdrv = { 107 .driver = { 108 .name = "arm-bL-cpufreq-dt", 109 }, 110 .probe = generic_bL_probe, 111 .remove = generic_bL_remove, 112}; 113module_platform_driver(generic_bL_platdrv); 114 115MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>"); 116MODULE_DESCRIPTION("Generic ARM big LITTLE cpufreq driver via DT"); 117MODULE_LICENSE("GPL v2"); 118