Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.11 116 lines 3.1 kB view raw
1/* 2 * System Control and Power Interface (SCPI) based CPUFreq Interface driver 3 * 4 * It provides necessary ops to arm_big_little cpufreq driver. 5 * 6 * Copyright (C) 2015 ARM Ltd. 7 * Sudeep Holla <sudeep.holla@arm.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 14 * kind, whether express or implied; without even the implied warranty 15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 20 21#include <linux/cpu.h> 22#include <linux/cpufreq.h> 23#include <linux/module.h> 24#include <linux/platform_device.h> 25#include <linux/pm_opp.h> 26#include <linux/scpi_protocol.h> 27#include <linux/types.h> 28 29#include "arm_big_little.h" 30 31static struct scpi_ops *scpi_ops; 32 33static struct scpi_dvfs_info *scpi_get_dvfs_info(struct device *cpu_dev) 34{ 35 int domain = topology_physical_package_id(cpu_dev->id); 36 37 if (domain < 0) 38 return ERR_PTR(-EINVAL); 39 return scpi_ops->dvfs_get_info(domain); 40} 41 42static int scpi_get_transition_latency(struct device *cpu_dev) 43{ 44 struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev); 45 46 if (IS_ERR(info)) 47 return PTR_ERR(info); 48 return info->latency; 49} 50 51static int scpi_init_opp_table(const struct cpumask *cpumask) 52{ 53 int idx, ret; 54 struct scpi_opp *opp; 55 struct device *cpu_dev = get_cpu_device(cpumask_first(cpumask)); 56 struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev); 57 58 if (IS_ERR(info)) 59 return PTR_ERR(info); 60 61 if (!info->opps) 62 return -EIO; 63 64 for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) { 65 ret = dev_pm_opp_add(cpu_dev, opp->freq, opp->m_volt * 1000); 66 if (ret) { 67 dev_warn(cpu_dev, "failed to add opp %uHz %umV\n", 68 opp->freq, opp->m_volt); 69 while (idx-- > 0) 70 dev_pm_opp_remove(cpu_dev, (--opp)->freq); 71 return ret; 72 } 73 } 74 75 ret = dev_pm_opp_set_sharing_cpus(cpu_dev, cpumask); 76 if (ret) 77 dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", 78 __func__, ret); 79 return ret; 80} 81 82static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = { 83 .name = "scpi", 84 .get_transition_latency = scpi_get_transition_latency, 85 .init_opp_table = scpi_init_opp_table, 86 .free_opp_table = dev_pm_opp_cpumask_remove_table, 87}; 88 89static int scpi_cpufreq_probe(struct platform_device *pdev) 90{ 91 scpi_ops = get_scpi_ops(); 92 if (!scpi_ops) 93 return -EIO; 94 95 return bL_cpufreq_register(&scpi_cpufreq_ops); 96} 97 98static int scpi_cpufreq_remove(struct platform_device *pdev) 99{ 100 bL_cpufreq_unregister(&scpi_cpufreq_ops); 101 scpi_ops = NULL; 102 return 0; 103} 104 105static struct platform_driver scpi_cpufreq_platdrv = { 106 .driver = { 107 .name = "scpi-cpufreq", 108 }, 109 .probe = scpi_cpufreq_probe, 110 .remove = scpi_cpufreq_remove, 111}; 112module_platform_driver(scpi_cpufreq_platdrv); 113 114MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); 115MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver"); 116MODULE_LICENSE("GPL v2");